#pragma once

#include <SFML/Graphics.hpp>
#include <nlohmann/json.hpp>

#include <vector>
#include <fstream>
#include <array>
#include <iostream>
#include <sstream>
#include <cmath>
#include "Log.hpp"
#include "Asset.hpp"
#include "Collider.hpp"
#include "Sand.hpp"
#include "Spike.hpp"

#define CW 20
#define CH 12 

using json = nlohmann::json;
using Block = std::array<sf::Vertex, 4u>;

class BlockLayer final: public sf::Drawable {
public:
	BlockLayer() = delete;
	BlockLayer(const std::string& filename, const std::string& tileset):
	m_tex(&Asset::texture(tileset)),
	m_tileSize(32.f) {
		std::ifstream ifs(filename);
		json j;
		ifs >> j;
		
		auto layer = j["layers"][2];
		m_size.x = layer["width"];
		m_size.y = layer["height"];
		
		m_blocks.reserve(m_size.x * m_size.y);
		
		for(std::size_t i = 0; i < layer["data"].size(); ++i) {
			int tile = layer["data"][i];
			size_t x = i % m_size.x;
			size_t y = i / m_size.x;
			
			int collide_type = Collider::AIR;
			switch(tile) {
			case 1:
				collide_type = Collider::SOLID;
				break;
			case 2:
				collide_type = Collider::SAND;
				break;
			case 3:
				collide_type = Collider::LEVER;
				break;
			case 4:
				collide_type = Collider::SPIKE;
				break;
			default:
				break;
			}
			m_map.push_back(collide_type);
			
			if(0 && tile == 2) {
				sf::Vector2f tileOffset(x * m_tileSize, y * m_tileSize);
				std::size_t tileIndex = tile - 1;

				m_blocks.emplace_back(
					tileOffset, 
					sf::Color::White, 
					sf::Vector2f(tileIndex * m_tileSize, 0.f)
				);
				m_blocks.emplace_back(
					tileOffset + sf::Vector2f(m_tileSize, 0.f), 
					sf::Color::White, 
					sf::Vector2f((tileIndex + 1) * m_tileSize, 0.f)
				);
				m_blocks.emplace_back(
					tileOffset + sf::Vector2f(m_tileSize, m_tileSize), 
					sf::Color::White, 
					sf::Vector2f((tileIndex + 1) * m_tileSize, 
					m_tileSize)
				);
				m_blocks.emplace_back(
					tileOffset + sf::Vector2f(0.f, m_tileSize), 
					sf::Color::White, 
					sf::Vector2f(tileIndex * m_tileSize, 
					m_tileSize)
				);
			} else {
				for(std::size_t i = 0; i < 4; ++i) {
					m_blocks.emplace_back(sf::Vector2f(0.f, 0.f), sf::Color::White, sf::Vector2f(0.f, 0.f));
				}
			}
		}
	}
	~BlockLayer() {
		/*for(std::size_t i = 0; i < m_blocks.size(); ++i) {
			if(m_blocks[i]) {
				delete m_blocks[i];
			}
		}*/
	}
	
	std::vector<int> rebuildCollider(int focus) {
		std::vector<int> res;
		res.reserve(CW * CH);
		for(int j = 0; j < CH; ++j) {
			for(int i = focus * CW; i < focus * CW + CW; ++i) {
                int curr = m_map[i + j * m_size.x];
				if(curr != Collider::SPIKE && curr != Collider::SAND) {
					res.push_back(m_map[i + j * m_size.x]);
				} else {
					res.push_back(Collider::AIR);
				}
			}
		}
		return res;
	}
	
	std::vector<Spike> reloadSpikes(int focus, Collider* collider) {
		std::vector<Spike> res;
		for(int j = 1; j < CH; ++j) {
			for(int i = focus * CW; i < focus * CW + CW; ++i) {
				if(m_map[i + j * m_size.x] == Collider::SPIKE) {
					res.emplace_back(collider, m_map[i + (j - 1) * m_size.x] == Collider::LEVER);
					res.back().setPosition(i * 32.f, j * 32.f);
					res.back().setTexture(Asset::texture("data/tileset.png"));
					res.back().setTextureRect(sf::IntRect(
						96,
						0,
						32,
						32
					));
				}
			}
		}
		return res;
	}
	
    std::vector<Sand> reloadSand(int focus, Collider* collider) {
		std::vector<Sand> res;
		for(int j = 0; j < CH; ++j) {
			for(int i = focus * CW; i < focus * CW + CW; ++i) {
				if(m_map[i + j * m_size.x] == Collider::SAND) {
					res.emplace_back(collider);
					res.back().setPosition(i * 32.f, j * 32.f);
					res.back().setTexture(Asset::texture("data/tileset.png"));
					res.back().setTextureRect(sf::IntRect(
						32,
						0,
						32,
						32
					));
				}
			}
		}
		return res;
	}
	
	std::vector<sf::Vertex> m_blocks;
	std::vector<int> m_map;
	
    BlockLayer(const BlockLayer&) = delete;
    BlockLayer& operator=(const BlockLayer&) = delete;

    //const sf::FloatRect& getGlobalBounds() const { 
	//	return m_globalBounds; 
	//}

private:
    const sf::Texture* m_tex;
	sf::Vector2u m_size;
	float m_tileSize;

    void draw(sf::RenderTarget& rt, sf::RenderStates states) const override {
		states.texture = m_tex;
        rt.draw(m_blocks.data(), m_blocks.size(), sf::Quads, states);
    }
};