#pragma once
#include <list>
#include <efficient_block.hpp>
#include <toolbox.hpp>
#include <enums.hpp>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <shader.hpp>
#include <string>
class SlimBlockchainHandler {
public:
	/*Assumes all toolbox textures are initialized*/
	SlimBlockchainHandler(Toolbox& tb)
		: previous_mstate_(tb.m_state),
			blockchain_(0),
			dragpairs_(0),
			shader_drawblocks_((tb.shader_path + "draw_blocks.vert").c_str(), (tb.shader_path + "draw_blocks.frag").c_str())
	{
		shader_drawblocks_.use();
		shader_drawblocks_.setInt("source_texture", 0);

		/*Set up Framebuffers*/
		glGenFramebuffers(1, &fbo_wave_);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo_wave_);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tb.tex_wave_1, 0);
		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
			std::cout << "ERROR::FRAMEBUFFER: Blockchain Framebuffer not complete!" << std::endl;
		}
		glGenFramebuffers(1, &fbo_dynamic_);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo_dynamic_);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tb.tex_damp_dynamic, 0);
		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
			std::cout << "ERROR::FRAMEBUFFER: Blockchain Framebuffer not complete!" << std::endl;
		}
		glGenFramebuffers(1, &fbo_static_);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo_static_);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tb.tex_damp_static, 0);
		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
			std::cout << "ERROR::FRAMEBUFFER: Blockchain Framebuffer not complete!" << std::endl;
		}
		glBindFramebuffer(GL_FRAMEBUFFER, 0);

		/*Set up rendering infrastructure*/
		glGenVertexArrays(1, &vao_);
		glGenBuffers(1, &vbo_);
		glBindVertexArray(vao_);
		glBindBuffer(GL_ARRAY_BUFFER, vbo_);
		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
		glEnableVertexAttribArray(1);
		glBindVertexArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	void update(Toolbox& tb);
	size_t num_blocks();

private:
	void clear_blocks_();
	void update_blocks_(const Toolbox& tb, bool reload_all);
	bool in_wave_window_(const Toolbox&, const Pevent&) const;
	//Vertex Calculation
	void xywhs_to_vertices_(const Toolbox& tb, const std::vector<bool>& duplicates, const std::vector<glm::ivec4>& xywhs, std::vector<float>& vertices) const;
	//Duplicate identification
	void find_dynamic_duplicates_();

	//Data
	int previous_mstate_ = static_cast<int>(MSTATE::PLACE); /*Keep track when mstate changes*/
	std::list<EfficientBlock> blockchain_; /*Blocks present in system*/
	std::list<std::pair<EfficientBlock*, SDL_FingerID> > dragpairs_; /*Dragged Blocks and their dragging finger*/
	/*Rendering infrastructure*/
	Shader shader_drawblocks_;
	GLuint vao_;
	GLuint vbo_;
	/*Framebuffers*/
	GLuint fbo_wave_ = 0;
	GLuint fbo_dynamic_ = 0;
	GLuint fbo_static_ = 0;
	/*Drawlists*/
	std::vector<glm::ivec4> drawlist_static_;
	std::vector<glm::ivec4> drawlist_dynamic_;
	std::vector<glm::ivec4> eraselist_dynamic_;
	std::vector<bool> drawlist_dynamic_duplicates_;
	std::vector<bool> eraselist_dynamic_duplicates_;
};