#include "pch.h"
#include "Drawer2D.h"

#include "gl/Buffer.h"
#include "gl/Shader.h"
#include "gl/ShaderProgram.h"


	Drawer2D::Drawer2D() {

		// char8_t* data_byte_ptr = (char8_t*)(new uint8_t[byte_sz]);
		// for(int i = 0; i < byte_sz; i++) {
		// 	data_byte_ptr[i] = 0;
		// }
		// this->buff->byte_len

		this->buff = new Buffer(BuffDesc{
			.byte_len = megabyte_in_bytes * 1,
			.subdata_disabled = false,
			.name = "Buff Drawer2D"
		});

		// this->buff_ptr = (float*)this->buff->mapped_buff;
		this->buff_ptr = (float*)this->buff->cpu_data;
		this->prev_buff_ptr = (float*)this->buff->cpu_data;
		this->shader_prog= new ShaderProgram({
			new Shader("engine/drawer_2d.vert"),
			new Shader("engine/drawer_2d.frag")
		});
	}

	void Drawer2D::push_vert(Drawer2DVert v) {
		*buff_ptr++ = v.pos.x;
		*buff_ptr++ = v.pos.y;
		*buff_ptr++ = v.uv.x;
		*buff_ptr++ = v.uv.y;
		*buff_ptr++ = v.color.r;
		*buff_ptr++ = v.color.g;
		*buff_ptr++ = v.color.b;
		*buff_ptr++ = v.color.w;
		*buff_ptr++ = std::bit_cast<float>(v.tex_idx);
		*buff_ptr++ = std::bit_cast<int>((int)v.is_tex_coloured);
		*buff_ptr++;
		*buff_ptr++;
		this->vert_cnt++;
	}

	void Drawer2D::draw_rect(RectDescr desc){
		int tex_idx = -1;
		bool is_tex_coloured = false;
		glm::vec4 col = glm::vec4(1,1,1,1);
		if(desc.tex.has_value()) {
			tex_idx = desc.tex.value()->bindless_idx;
			if(desc.col.has_value()) {
				is_tex_coloured = true;
			}
		} else if(desc.col.has_value()) {
			col = desc.col.value();
		}
		Drawer2DVert v_bot_l = {
			.pos = desc.pos - desc.sz,
			.uv = glm::vec2(0,0),
			.color = col,
			.tex_idx = tex_idx,
			.is_tex_coloured = is_tex_coloured
		};
		Drawer2DVert v_bot_r = {
			.pos = desc.pos - desc.sz * glm::vec2(-1,1),
			.uv = glm::vec2(1,0),
			.color = col,
			.tex_idx = tex_idx,
			.is_tex_coloured = is_tex_coloured
		};
		Drawer2DVert v_top_r = {
			.pos = desc.pos + desc.sz * glm::vec2(1,1),
			.uv = glm::vec2(1,1),
			.color = col,
			.tex_idx = tex_idx,
			.is_tex_coloured = is_tex_coloured
		};
		Drawer2DVert v_top_l = {
			.pos = desc.pos + desc.sz * glm::vec2(-1,1),
			.uv = glm::vec2(0,1),
			.color = col,
			.tex_idx = tex_idx,
			.is_tex_coloured = is_tex_coloured
		};
		push_vert(v_bot_l);
		push_vert(v_bot_r);
		push_vert(v_top_r);

		push_vert(v_bot_l);
		push_vert(v_top_r);
		push_vert(v_top_l);
	}

	void Drawer2D::flush_quads() {
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDisable(GL_DEPTH_TEST);
		this->shader_prog->use();
		this->shader_prog->setUniform("drawer_2d_buff", this->buff);
		// int cnt = (buff_ptr - (float*)this->buff->mapped_buff) / (sizeof(Drawer2DVert)/sizeof(float));
		// int cnt = (buff_ptr - (float*)this->buff->mapped_buff) / (sizeof(Drawer2DVert)/sizeof(float));
		// int cnt = (buff_ptr - (float*)this->buff->mapped_buff) / (sizeof(Drawer2DVert)/sizeof(float));
		// glDrawArrays(GL_TRIANGLES, 0, cnt);

		// int verts_per_char = 6;

		constexpr int floats_per_vert = 12;
		constexpr int bytes_per_vert = floats_per_vert * 4;

		int byte_cnt = this->vert_cnt * bytes_per_vert; // 8 floats * 4 bytes

		int float_offs_from_start = this->prev_buff_ptr - (float*)this->buff->cpu_data;
		int byte_offs_from_start = float_offs_from_start * 4;

		this->buff->upload_sub_data(
			this->prev_buff_ptr, byte_cnt, byte_offs_from_start
		);


		glDrawArrays(GL_TRIANGLES, this->vert_cnt_accum, this->vert_cnt);
		glDisable(GL_BLEND);

		this->vert_cnt_accum += this->vert_cnt;
		this->vert_cnt = 0;
		this->prev_buff_ptr = this->buff_ptr;
		// reset();
	}

void Drawer2D::flush_points() {
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glDisable(GL_DEPTH_TEST);
		this->shader_prog->use();
		this->shader_prog->setUniform("drawer_2d_buff", this->buff);
		// int cnt = (buff_ptr - (float*)this->buff->mapped_buff) / (sizeof(Drawer2DVert)/sizeof(float));
		// int cnt = (buff_ptr - (float*)this->buff->mapped_buff) / (sizeof(Drawer2DVert)/sizeof(float));
		// int cnt = (buff_ptr - (float*)this->buff->mapped_buff) / (sizeof(Drawer2DVert)/sizeof(float));
		// glDrawArrays(GL_TRIANGLES, 0, cnt);

		// int verts_per_char = 6;

		constexpr int floats_per_vert = 8;
		constexpr int bytes_per_vert = 8 * 4;

		int byte_cnt = this->vert_cnt * bytes_per_vert; // 8 floats * 4 bytes

		int float_offs_from_start = this->prev_buff_ptr - (float*)this->buff->cpu_data;
		int byte_offs_from_start = float_offs_from_start * 4;

		this->buff->upload_sub_data(
			this->prev_buff_ptr, byte_cnt, byte_offs_from_start
		);


		glDrawArrays(GL_POINTS, this->vert_cnt_accum, this->vert_cnt);
		glDisable(GL_BLEND);

		this->vert_cnt_accum += this->vert_cnt;
		this->vert_cnt = 0;
		this->prev_buff_ptr = this->buff_ptr;

}

void Drawer2D::reset(){
		this->vert_cnt = 0;
		this->vert_cnt_accum = 0;
		this->buff_ptr = (float*)this->buff->cpu_data;
		this->prev_buff_ptr = (float*)this->buff->cpu_data;
	}
