#include "pch.h"

#include "DrawerNew.h"
#include "engine/Camera.h"
#include "gl/Buffer.h"
#include "gl/Shader.h"
#include "gl/ShaderManager.h"
#include "gl/ShaderProgram.h"
#include <earcut.hpp>



using namespace glm;


BuffDesc get_buff_desc(){
	BuffDesc desc = {
		.byte_len = megabyte_in_bytes*40,
		.subdata_disabled = false,
		.mappable_write = false,
		.mappable_read = false,
		.persistennt = false,
		.write_from_shader = false,
		.name = "Drawer Transforms"
	};
	return desc;
}


uint64_t Trans::global_id = 0;

Trans::Trans() {
	this->local_id = Trans::global_id++;
	mat = glm::mat4(
		1,0,0,0,
		0,1,0,0,
		0,0,1,0,
		0,0,0,1
	);
}

Trans Trans::rot(glm::vec3 axis, float angle) {
	Trans new_trans = *this;
	new_trans.local_id = Trans::global_id++;
	new_trans.mat = glm::rotate(new_trans.mat, angle, axis);
	new_trans.is_identity = false;
	return new_trans;
}

Trans Trans::scale(glm::vec3 scale) {
	Trans new_trans = *this;
	new_trans.local_id = Trans::global_id++;
	new_trans.mat = glm::scale(new_trans.mat, scale);
	new_trans.is_identity = false;
	return new_trans;
}

Trans Trans::trans(glm::vec3 trans) {
	Trans new_trans = *this;
	new_trans.local_id = Trans::global_id++;
	new_trans.mat = glm::translate(new_trans.mat, trans);
	new_trans.is_identity = false;
	return new_trans;
}

Trans Trans::apply_other_trans(Trans other_trans) {
	Trans new_trans = *this;
	new_trans.local_id = Trans::global_id++;
	new_trans.mat = other_trans.mat * new_trans.mat;
	new_trans.is_identity = false;
	return new_trans;
}

Trans Trans::skew(uint pushAxis, uint byAxis, float amount) {
	Trans new_trans = *this;
	new_trans.local_id = Trans::global_id++;
    new_trans.mat[byAxis][pushAxis] = amount;
	new_trans.is_identity = false;
	return new_trans;
}

Drawer::TransformsContainer::TransformsContainer() {
	this->buff_transforms = new Buffer(get_buff_desc());
}
void Drawer::TransformsContainer::try_push_transform(Trans trans) {
	if(trans.is_identity){
		curr_trans_buff_idx = -1;
		return;
	}
	// if(trans.local_id != this->prev_trans_id){
	if(trans.local_id != this->prev_trans_id){
		memcpy(
			&((float*)this->buff_transforms->cpu_data)[this->idx_buff_transforms * 16],
			(&trans.mat[0][0]),
			16 * sizeof(float)
		);
		// float* ptr = ((float*)this->buff_transforms->cpu_data);
		// for(int i = 0; i < 16; i++) {
		// 	float elem = (&trans.mat[0][0])[i];
		// 	ptr[this->idx_buff_transforms * 16 + i] = elem;
		// }
		this->idx_buff_transforms++;
	}

	curr_trans_buff_idx = this->idx_buff_transforms - 1;
	prev_trans_id = trans.local_id;
}
void Drawer::TransformsContainer::flush_transforms() {
	this->prev_trans_id = -1;
	if(this->idx_buff_transforms == 0){
		return;
	}
	this->buff_transforms->upload_sub_data(
		this->buff_transforms->cpu_data, 16 * sizeof(float) * this->idx_buff_transforms
	);
			
	this->idx_buff_transforms = 0;
}
void Drawer::TransformsContainer::reset() {
	this->idx_buff_transforms = 0;
}

Drawer::TriangleDrawer::TriangleDrawer(TransformsContainer* _transforms_container) {
	this->transforms_container = _transforms_container;
	this->buff_verts = new Buffer(get_buff_desc());

	this->buff_ptr = (float*)this->buff_verts->cpu_data;
	this->prev_buff_ptr = (float*)this->buff_verts->cpu_data;
	this->shader_prog= new ShaderProgram({
		new Shader("scene_deadline24_bicolor/drawer/drawer_triangle.vert"),
		new Shader("scene_deadline24_bicolor/drawer/drawer_triangle.frag")
	});
}
void Drawer::TriangleDrawer::push_vert(glm::vec3 pos, glm::vec4 colour, glm::vec2 uv, Trans trans) {
	*buff_ptr++ = pos.x;
	*buff_ptr++ = pos.y;
	*buff_ptr++ = pos.z;
	*buff_ptr++;
	*buff_ptr++ = colour.r;
	*buff_ptr++ = colour.g;
	*buff_ptr++ = colour.b;
	*buff_ptr++ = colour.w;
	*buff_ptr++ = uv.x;
	*buff_ptr++ = uv.y;
	*(int*)buff_ptr++ = this->transforms_container->curr_trans_buff_idx;
	*buff_ptr++;
	this->vert_cnt++;
}
void Drawer::TriangleDrawer::draw_triangle(TriDescr tri_descr) {
	if(!tri_descr.trans.is_identity) {
		this->transforms_container->try_push_transform(tri_descr.trans);
	}
	for(int i = 0; i < 3; i++) {
		this->push_vert(tri_descr.positions[i], tri_descr.colours[i], tri_descr.uvs[i], tri_descr.trans);
	}
}
void Drawer::TriangleDrawer::draw_triangles(TriDescr* tri_descr, Trans trans, int tri_cnt) {
	if(!trans.is_identity) {
		this->transforms_container->try_push_transform(trans);
	}
	for(int tri_id = 0; tri_id < tri_cnt; tri_id++) {
		for(int i = 0; i < 3; i++) {
			this->push_vert(
				tri_descr[tri_id].positions[i],
				tri_descr[tri_id].colours[i],
				tri_descr[tri_id].uvs[i],
				trans
			);
		}
	}
}
bool Drawer::TriangleDrawer::flush() {
	if(this->vert_cnt == 0) {
		return false;
	}
	this->shader_prog->use();
	if(WEngine->shader_manager->shader_just_reloaded || WEngine->frame <= 1){
	}
	this->shader_prog->setUniform("vert_buff", this->buff_verts);
	this->shader_prog->setUniform("transforms_buff", this->transforms_container->buff_transforms);
			
	int byte_cnt = this->vert_cnt * bytes_per_vert;

	this->buff_verts->upload_sub_data(
		this->buff_verts->cpu_data, byte_cnt, 0
	);
			
			
	// glTextureBarrier();
	glDrawArrays(GL_TRIANGLES, this->vert_cnt_accum, this->vert_cnt);

	this->reset();
	// this->vert_cnt_accum += this->vert_cnt;
	// this->vert_cnt = 0;
	// this->prev_buff_ptr = this->buff_ptr;
	
	return true;
}
void Drawer::TriangleDrawer::reset() {
	this->vert_cnt = 0;
	this->vert_cnt_accum = 0;
	this->buff_ptr = (float*)this->buff_verts->cpu_data;
	this->prev_buff_ptr = (float*)this->buff_verts->cpu_data;
}

void Drawer::LineDrawer::push_vert(glm::vec3 pos, glm::vec4 col, float sz, bool constant_width, glm::vec3 norm,
	Trans trans) {
	float* buff_ptr = (float*)this->buff_verts->cpu_data;
	buff_ptr = buff_ptr + vert_cnt * floats_per_vert;
	*buff_ptr++ = pos.x;
	*buff_ptr++ = pos.y;
	*buff_ptr++ = pos.z;
	*buff_ptr++;
			
	*buff_ptr++ = col.r;
	*buff_ptr++ = col.g;
	*buff_ptr++ = col.b;
	*buff_ptr++ = col.a;
			
	*buff_ptr++ = sz;
	*(int*)buff_ptr++ = constant_width;
	*(int*)buff_ptr++ = this->transforms_container->curr_trans_buff_idx;
	*buff_ptr++;
			
	*buff_ptr++ = norm.x;
	*buff_ptr++ = norm.y;
	*buff_ptr++ = norm.z;
	*buff_ptr++;
			
	vert_cnt++;
}

Drawer::LineDrawer::LineDrawer(TransformsContainer* _transforms_container) {
	this->transforms_container = _transforms_container;
	this->buff_verts = new Buffer(get_buff_desc());
			
	this->shader_prog = new ShaderProgram({
		new Shader("scene_deadline24_bicolor/drawer/drawer_line.vert"),
		new Shader("scene_deadline24_bicolor/drawer/drawer_line.frag")
	});
}
void Drawer::LineDrawer::draw_line(LineDescr line_descr) {
	using namespace glm;
	vec2 curr_norm = vec2(0,0);

			
	// if(!line_descr.trans.is_identity) {
	this->transforms_container->try_push_transform(line_descr.trans);
	// }
			
	this->push_vert(
		line_descr.positions[0],
		line_descr.cols[0],
		line_descr.width,
		line_descr.const_size,
		glm::vec3(curr_norm,0),
		line_descr.trans
	);
			
			
	this->push_vert(
		line_descr.positions[1],
		line_descr.cols[1],
		line_descr.width*2.0f,
		line_descr.const_size,
		glm::vec3(curr_norm,0),
		line_descr.trans
	);
}
bool Drawer::LineDrawer::flush() {
	if(this->vert_cnt == 0){
		return false;
	}
	this->shader_prog->use();
	if(WEngine->shader_manager->shader_just_reloaded || WEngine->frame <= 1) {
	}
	
	this->shader_prog->setUniform("vert_buff", this->buff_verts);
	this->shader_prog->setUniform("transforms_buff", this->transforms_container->buff_transforms);
			
	int byte_cnt = this->vert_cnt * bytes_per_vert;
	this->buff_verts->upload_sub_data(
		this->buff_verts->cpu_data, byte_cnt, 0
	);
		
	// glTextureBarrier();
	glDrawArrays(GL_TRIANGLES, 0, (this->vert_cnt)*3);
	this->reset();
	
	return true;
}
void Drawer::LineDrawer::reset() {
	this->vert_cnt = 0;
	this->vert_idx_curr_polyline_start = 0;
	polyline_start_and_ends.resize(0);
	vert_cnt = 0;
}

Drawer::PolyLineDrawer::PolyLineDrawer(TransformsContainer* _transforms_container) {
	this->transforms_container = _transforms_container;
	this->buff_verts = new Buffer(get_buff_desc());
			
	this->shader_prog = new ShaderProgram({
		new Shader("scene_deadline24_bicolor/drawer/drawer_polyline.vert"),
		new Shader("scene_deadline24_bicolor/drawer/drawer_polyline.frag")
	});
}
void Drawer::PolyLineDrawer::push_vert(CurvePt pt_descr) {
	float* buff_ptr = (float*)this->buff_verts->cpu_data;
	buff_ptr = buff_ptr + vert_cnt * floats_per_vert;
	*buff_ptr++ = pt_descr.pos.x;
	*buff_ptr++ = pt_descr.pos.y;
	*buff_ptr++ = pt_descr.pos.z;
	*buff_ptr++;
			
	*buff_ptr++ = pt_descr.col.r;
	*buff_ptr++ = pt_descr.col.g;
	*buff_ptr++ = pt_descr.col.b;
	*buff_ptr++ = pt_descr.col.a;
			
	*buff_ptr++ = pt_descr.sz;
	*(int*)buff_ptr++ = -1;
	*buff_ptr++;
	*buff_ptr++;
			
	*buff_ptr++;
	*buff_ptr++;
	*buff_ptr++;
	*buff_ptr++;
			
	vert_cnt++;
}
void Drawer::PolyLineDrawer::end_polyline(TransformsContainer* transforms_container, Trans trans) {
	auto get_orthogonal_basis = []( glm::vec3 z ) -> glm::mat3 {
		z = normalize( z );
		glm::vec3 up = abs( z.y ) < 0.99 ? glm::vec3( 0.0, 1.0, 0.0 ) : glm::vec3( 0.0, 0.0, 1.0 );
		glm::vec3 x = normalize( cross( up, z ) );
		return glm::mat3( x, cross( z, x ), z );
	};
			
	int start_idx = this->vert_idx_curr_polyline_start;
	int end_idx = this->vert_cnt;
			
	if(start_idx == end_idx) {
		return;
	}
			
	transforms_container->try_push_transform(trans);
	int transforms_buff_idx = transforms_container->curr_trans_buff_idx;
	
	bool do_loop = false;
	{
		float* start_ptr = (float*)this->buff_verts->cpu_data + start_idx * floats_per_vert;
		float* end_ptr = (float*)this->buff_verts->cpu_data + (end_idx-1) * floats_per_vert;
		glm::vec3 start_pos = glm::vec3(
			start_ptr[0],
			start_ptr[1],
			start_ptr[2]
		);
					
		glm::vec3 curr_pos = glm::vec3(
			end_ptr[0],
			end_ptr[1],
			end_ptr[2]
		);

		if(glm::length(start_pos - curr_pos) < 0.0004f) {
			do_loop = true;
			// buff_ptr_next = start_ptr + floats_per_vert;
			// buff_ptr_prev = buff_ptr;
			// buff_ptr_prev = buff_ptr - floats_per_vert;
		}
	}
			
	glm::vec3 first_vert = glm::vec3(0);
	for(int i = start_idx; i < end_idx; i++) {
		float* buff_ptr = (float*)this->buff_verts->cpu_data;
		buff_ptr = buff_ptr + i * floats_per_vert;
				
		float* buff_ptr_next;
		float* buff_ptr_prev;
		if(i == start_idx) {
			buff_ptr_next = buff_ptr + floats_per_vert;
			buff_ptr_prev = buff_ptr;
		} else if (i != end_idx - 1){
			buff_ptr_next = buff_ptr + floats_per_vert;
			buff_ptr_prev = buff_ptr - floats_per_vert;
		} else {
			buff_ptr_next = buff_ptr;
			buff_ptr_prev = buff_ptr - floats_per_vert;
		}

		if(i == end_idx - 1) {
		}
				
		glm::vec3 prev_pos = glm::vec3(
			buff_ptr_prev[0],
			buff_ptr_prev[1],
			buff_ptr_prev[2]
		);
				
		glm::vec3 next_pos = glm::vec3(
			buff_ptr_next[0],
			buff_ptr_next[1],
			buff_ptr_next[2]
		);
		using namespace glm;
				
		// vec4 prev_vert_proj = WEngine->camera->P * WEngine->camera->V * vec4(prev_pos,1);
		// vec4 next_vert_proj = WEngine->camera->P * WEngine->camera->V * vec4(next_pos,1);
		//
		// vec3 prev_vert_ndc = vec3(prev_vert_proj) / prev_vert_proj.w;
		// vec3 next_vert_ndc = vec3(next_vert_proj) / next_vert_proj.w;
		// 		
		// vec2 curr_tan = normalize(vec2(next_vert_ndc) - vec2(prev_vert_ndc));
		// 		
		// vec2 curr_norm = vec2(curr_tan.y, -curr_tan.x);
		// float curr_width = *(buff_ptr + 8);
		// curr_norm *= curr_width;
		//
		// buff_ptr[12] = curr_norm.x;
		// buff_ptr[13] = curr_norm.y;

		((int*)buff_ptr)[10] = start_idx;
		((int*)buff_ptr)[11] = end_idx - 1;
		
		((int*)buff_ptr)[12] = glm::max(i - 1, start_idx);
		((int*)buff_ptr)[13] = glm::min(i + 1,end_idx-1);
		((int*)buff_ptr)[14] = transforms_buff_idx;
		((int*)buff_ptr)[15] = 0;


		if(do_loop){
			if(i == start_idx){
				// ((int*)buff_ptr)[15] = end_idx - 1;
				((int*)buff_ptr)[12] = end_idx - 2;
			} else if(i == end_idx - 1) {
				((int*)buff_ptr)[13] = start_idx+1;
				// ((int*)buff_ptr)[15] = 1;
				// ((int*)buff_ptr)[15] = start_idx;
				// ((int*)buff_ptr)[15] = start_idx+1;
			} 
		} else {
			// if(i == start_idx){
			// 	((int*)buff_ptr)[15] = -2;
			// } else if(i == end_idx - 1) {
			// 	((int*)buff_ptr)[15] = -3;
			// } 
		}

		if(i == end_idx - 1){
			((int*)buff_ptr)[15] = 1;
		}
	}
	this->polyline_start_and_ends.push_back({ start_idx, end_idx });
	this->vert_idx_curr_polyline_start = this->vert_cnt;
}
bool Drawer::PolyLineDrawer::flush() {
	end_polyline(this->transforms_container);
	if(this->vert_cnt == 0) {
		return false;
	}
	this->shader_prog->use();
			
	if(WEngine->shader_manager->shader_just_reloaded || WEngine->frame <= 1) {
	}
	this->shader_prog->setUniform("vert_buff", this->buff_verts);
	this->shader_prog->setUniform("transforms_buff", this->transforms_container->buff_transforms);
	
	int byte_cnt = this->vert_cnt * bytes_per_vert;
	this->buff_verts->upload_sub_data(
		this->buff_verts->cpu_data, byte_cnt, 0
	);
			
	// glTextureBarrier();
	glDrawArrays(GL_TRIANGLES, 0, (this->vert_cnt-1)*6*10 );
	this->reset();
	return true;
}
void Drawer::PolyLineDrawer::reset() {
	this->vert_cnt = 0;
	this->vert_idx_curr_polyline_start = 0;
	polyline_start_and_ends.resize(0);
	vert_cnt = 0;
}



Drawer::PointDrawer::PointDrawer(TransformsContainer* _transforms_container) {
	this->transforms_container = _transforms_container;
	auto buff_desc = get_buff_desc();
	// buff_desc.dont_bindless = true;
	this->buff = new Buffer(buff_desc);

	this->buff_ptr = (float*)this->buff->cpu_data;
	this->prev_buff_ptr = (float*)this->buff->cpu_data;
	this->shader_prog= new ShaderProgram({
		new Shader("scene_deadline24_bicolor/drawer/drawer_point.vert"),
		new Shader("scene_deadline24_bicolor/drawer/drawer_point.frag")
	});
}
void Drawer::PointDrawer::push_vert(glm::vec3 pos, glm::vec4 colour, float sz, bool is_constant_size, Trans trans) {
	*buff_ptr++ = pos.x;
	*buff_ptr++ = pos.y;
	*buff_ptr++ = pos.z;
	*buff_ptr++ = sz;
	*buff_ptr++ = colour.r;
	*buff_ptr++ = colour.g;
	*buff_ptr++ = colour.b;
	*buff_ptr++ = colour.w;
	*(int*)buff_ptr++ = is_constant_size;
	*(int*)buff_ptr++ = transforms_container->curr_trans_buff_idx;
	*buff_ptr++ = 0;
	*buff_ptr++ = 0;
	this->vert_cnt++;
}
void Drawer::PointDrawer::draw_point(PointDescr point_descr) {
	this->transforms_container->try_push_transform(point_descr.trans);
	this->push_vert(point_descr.pos, point_descr.col, point_descr.sz, point_descr.const_size, point_descr.trans);
}
bool Drawer::PointDrawer::flush() {
	if(this->vert_cnt == 0) {
		return false;
	}
	this->shader_prog->use();
			
	if(WEngine->shader_manager->shader_just_reloaded || WEngine->frame <= 1) {
	}
	
	this->shader_prog->setUniform("vert_buff", this->buff);
	this->shader_prog->setUniform("transforms_buff", this->transforms_container->buff_transforms);

	// int byte_cnt = this->vert_cnt * bytes_per_vert;
	int byte_cnt = bytes_per_vert * this->vert_cnt;
	// int float_offs_from_start = this->prev_buff_ptr - (float*)this->buff->cpu_data;
	// int byte_offs_from_start = float_offs_from_start * sizeof(float);
			
	this->buff->upload_sub_data(
		(float*)this->buff->cpu_data, byte_cnt, 0
	);

	// glTextureBarrier();
	glDrawArrays(GL_TRIANGLES, 0, this->vert_cnt * 3 * 16 * 2/2);
	
	this->reset();

	return true;
}
void Drawer::PointDrawer::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;
}

Drawer::Drawer() {
	this->transforms_container = new TransformsContainer();
	this->tri_drawer = new TriangleDrawer(this->transforms_container);
	this->line_drawer = new LineDrawer(this->transforms_container);
	this->point_drawer = new PointDrawer(this->transforms_container);
	this->poly_line_drawer = new PolyLineDrawer(this->transforms_container);
	this->text_drawer = new TextDrawer3D(this->transforms_container);
}
void Drawer::reset() {
	WEngine->editor->gui_dbg_print("Drawer vert count: ", this->verts_drawn_this_frame_count);
	WEngine->editor->gui_dbg_print("Drawer drawcall count: ", this->drawcalls_this_frame_count);
	
	current_curve_pts.resize(0);
	this->transforms_container->reset();
	this->poly_line_drawer->reset();
	tri_drawer->reset();
	point_drawer->reset();
	line_drawer->reset();
	this->text_drawer->reset_end_of_frame();
	this->verts_drawn_this_frame_count = 0;
	this->drawcalls_this_frame_count = 0;
}
void Drawer::flush() {
	if(this->current_curve_pts.size() > 0) {
		end_curve();
	}
	this->transforms_container->flush_transforms();
	if(this->point_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
	if(this->tri_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
	if(this->line_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
	if(this->poly_line_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
	// flush_text()
	// if(this->text_drawer->flush()){
	// 	this->drawcalls_this_frame_count += 1;
	// }
	if(this->text_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
	
}


void Drawer::flush_tris() {
	this->transforms_container->flush_transforms();
	if(this->tri_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
}
void Drawer::flush_points() {
	this->transforms_container->flush_transforms();
	if(this->point_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
}
void Drawer::flush_lines() {
	this->transforms_container->flush_transforms();
	if(this->line_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
}
void Drawer::flush_curves() {
	if(this->current_curve_pts.size() > 0) {
		end_curve();
	}
	this->transforms_container->flush_transforms();
	if(this->poly_line_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
}

void Drawer::flush_text() {
	this->transforms_container->flush_transforms();
	if(this->text_drawer->flush()){
		this->drawcalls_this_frame_count += 1;
	}
}

void Drawer::draw_text(TextDescr text_descr) {
	this->transforms_container->try_push_transform(text_descr.trans);
	this->text_drawer->draw(text_descr);
}

void Drawer::push_curve_pt(CurvePt curve_pt) {
	// this->current_curve_pts.push_back(curve_pt);
	this->poly_line_drawer->push_vert(curve_pt);
}
void Drawer::end_curve(Trans trans) {
	this->verts_drawn_this_frame_count += this->poly_line_drawer->vert_cnt - this->poly_line_drawer->vert_idx_curr_polyline_start;
	this->poly_line_drawer->end_polyline(this->transforms_container, trans);
}
void Drawer::draw_point(PointDescr point_descr) {
	this->verts_drawn_this_frame_count += 1;
	this->point_drawer->draw_point(point_descr);
	last_draw_type = DrawType::POINT;
}
void Drawer::draw_line(LineDescr line_descr) {
	this->verts_drawn_this_frame_count += 2;
	this->line_drawer->draw_line(line_descr);
	last_draw_type = DrawType::LINE;
}
void Drawer::draw_rect(RectangDescr rect_descr) {
	LineDescr line_descr = {
		.positions = {
			rect_descr.pos - vec3(rect_descr.sz.x,0,0),
			rect_descr.pos + vec3(rect_descr.sz.x,0,0)
		},
		.width = rect_descr.sz.y,
		.cols = {rect_descr.col, rect_descr.col},
		.const_size = rect_descr.const_size,
		.trans = rect_descr.trans
	};
	this->draw_line(line_descr);
}
void Drawer::draw_tri(TriDescr tri_descr) {
	this->verts_drawn_this_frame_count += 3;
	this->tri_drawer->draw_triangle(tri_descr);
	last_draw_type = DrawType::TRIANGLE;
}
void Drawer::draw_shape(ShapeDescr shape_descr) {
	
	Shape* shape = shape_descr.shape;
	if(shape->dirty) {
		shape->triangulate();
	}
	static std::vector<TriDescr> triangles;
	triangles.resize(0);
	for( int i = 0; i < shape->triangulated_vertices.size()/3; i++) {
		triangles.push_back( {
			.positions = {(shape->triangulated_vertices)[i*3],(shape->triangulated_vertices)[i*3 + 1], (shape->triangulated_vertices)[i*3 + 2]},
			.colours = {shape_descr.col, shape_descr.col, shape_descr.col},
			.trans = shape_descr.trans
		} );
	}
	if(triangles.size() > 0){
		this->verts_drawn_this_frame_count += triangles.size() * 3;
		this->tri_drawer->draw_triangles(
			&triangles[0], shape_descr.trans, triangles.size()
		);
	}
}

void Drawer::set_font(Font* _font) {
	this->text_drawer->set_font(_font);
}

Shape::Shape() {
	for(int i = 0; i < 20; i++){
		this->mapbox_polygon.push_back(std::vector<Point>());
	}
	std::vector<Point>* vec = &this->mapbox_polygon[0];
	this->vertices = (std::vector<glm::vec3>*)vec;
}

void Shape::push_vert(glm::vec3 pos) {
	this->mapbox_polygon[this->shape_idx].push_back({pos.x, pos.y, pos.z});
	this->dirty = true;
}

void Shape::triangulate() {
	this->triangulated_vertices.resize(0);
	std::vector<unsigned> triangles = 
		mapbox::earcut<uint32_t>(this->mapbox_polygon);
	for(int i = 0; i < triangles.size(); i++) {
		if(this->shape_idx == 0){
			std::array<float, 3> vert = this->mapbox_polygon[0][triangles[i]];
			this->triangulated_vertices.push_back(
				glm::vec3(vert[0], vert[1], vert[2])
			);
		} else {
			int tri_idx = triangles[i];
			int running_sz = 0;
			for(int p = 0; p < this->shape_idx; p++){
				auto& poly = this->mapbox_polygon[p];
				int poly_sz = poly.size();
				// indexing "hell"
				if(tri_idx < running_sz + poly_sz){
					std::array<float, 3> vert = poly[tri_idx - running_sz];
					this->triangulated_vertices.push_back(
						glm::vec3(vert[0], vert[1], vert[2])
					);
					break;
				}
				running_sz += poly_sz;
			}
		}
	}
	this->dirty = false;
}

void Shape::reset() {
	this->shape_idx = 0;
	for(auto& poly : this->mapbox_polygon){
		poly.resize(0);
	}
	// this->mapbox_polygon[0].resize(0);
	this->dirty = true;
}

void Shape::start_next_shape() {
	this->shape_idx++;
}

Drawer::TextDrawer3D::TextDrawer3D(TransformsContainer* _transforms_container){
	// const size_t buff_len = 10000;
	this->text_buff = new Buffer({.byte_len = megabyte_in_bytes * 10, .subdata_disabled = false, .name = "Buff TextDrawer"});
	this->buff_ptr = (float*)this->text_buff->cpu_data;
	this->shader_prog = new ShaderProgram({ 
	new Shader( "scene_deadline24_bicolor/drawer/drawer_text.vert"), 
	new Shader("scene_deadline24_bicolor/drawer/drawer_text.frag") 
	});
	this->transforms_container = _transforms_container;
	// TransformsContainer* transforms_container
}

void Drawer::TextDrawer3D::set_font(Font* _font) {
	this->font = _font;
}

bool Drawer::TextDrawer3D::flush() {
	if(this->char_cnt == 0){
		return false;
	}

	this->shader_prog->use();
	this->shader_prog->setUniform("verts", this->text_buff);
	this->shader_prog->setUniform("font_tex", this->font->atlas_tex);
	this->shader_prog->setUniform("buff_transforms", this->transforms_container->buff_transforms);

	int vert_cnt_accum = verts_per_char * this->char_cnt_accum;
	int vert_cnt_curr = verts_per_char * this->char_cnt;
	int byte_cnt_curr = vert_cnt_curr * bytes_per_vert;

	float* curr_buff_ptr = (float*)this->text_buff->cpu_data;
	int byte_offs_from_start = 0;

	this->text_buff->upload_sub_data(
		curr_buff_ptr, byte_cnt_curr, byte_offs_from_start
	);

	glDrawArrays(GL_TRIANGLES, vert_cnt_accum, vert_cnt_curr);

	this->reset_end_of_frame();
	return true;
}

void Drawer::TextDrawer3D::reset_end_of_frame() {
	this->char_cnt_accum = 0;
	this->char_cnt = 0;
	this->buff_ptr = (float*)this->text_buff->cpu_data;
}

void Drawer::TextDrawer3D::draw(TextDescr& desc) {
	for(char& ch : desc.text){
		// ch = abs(ch%);
		// ch = max(ch,0);
		if(ch < 0){
			ch = 0;
		}
		if(ch > 254){
			ch = 254;
		}
	}
	// glm::vec2 starting_offs = desc.pos;
	glm::vec2 starting_offs = glm::vec2(0);
	glm::vec2 offs = starting_offs;
	float fs_scale = 1.0f/ (font->ascender - font->descender);

	float space_advance = font->w_glyphs[int(' ')].advance * desc.whitespace_scale;

	float vertical_step = fs_scale * font->line_height * desc.sz;

	float aspect = float(WEngine->RESX)/float(WEngine->RESY);
	const float advance_mult = desc.sz * desc.kern_scale * 1.0f/aspect;
	const glm::vec2 quad_mult = glm::vec2(1.0f/aspect, 1.0f) * desc.sz * fs_scale;

	auto get_advance = [&](int a, int b) -> float {
		if(b >= 255 || a >= 255){
			return 0.0;
		}
		float advance = font->w_glyphs[a].advance;
		if(b < 0) {
			return advance;
		}
		float kern = font->w_glyphs[a].kern_advance[b];
		if(!desc.do_kerning) {
			kern = 0.0f;
		}
		return (advance + kern) * advance_mult;
	};

	std::array<float,50> row_widths = { };
	// if(this->centered) {
	float max_row_w = 0.0;
	{
		float curr_row_w = 0.0;
		// float total_height = vertical_step;
		float total_height = 0.0f;
		// float acc_offs = 0.0;
		int i = 0;
		int row = 0;
		for(auto ch : desc.text) {
			WGlyph glyph = font->w_glyphs[int(ch)];

			if(ch == '\r') {
			} else if(ch == '\n') {
				row_widths[row] = curr_row_w;
				total_height += vertical_step;
				if(curr_row_w > max_row_w) {
					max_row_w = curr_row_w;
				}
				curr_row_w = 0.0;
				row++;
			} else if(ch == ' ') {
				float advance = space_advance;
				curr_row_w += fs_scale * advance * desc.sz * 1.0f/aspect;
			} else {
					if(i < (int)desc.text.size() - 1) {
						curr_row_w += get_advance(int(desc.text[i]), int(desc.text[i + 1]));
					} else {
						curr_row_w += get_advance(int(desc.text[i]), 0)*1.0;
					}
			}

			if(i == (int)desc.text.size() - 1) {
				row_widths[row] = curr_row_w;
				total_height -= vertical_step*0.5f;
			}
			i++;
		}
		if(curr_row_w > max_row_w) {
			max_row_w = curr_row_w;
		}

		if(desc.anchor != TextAnchorr::TOP_LEFT) {
			glm::vec2 accum_offs = glm::vec2( max_row_w, total_height );

			glm::vec2 anchor_offs = glm::vec2(0.0f,0.0f);
			switch (desc.anchor) {
				case TextAnchorr::CENTER:
					anchor_offs = glm::vec2(0,0);
					break;
				case TextAnchorr::TOP:
					anchor_offs  = glm::vec2(0,1);
					break;
				case TextAnchorr::TOP_RIGHT:
					anchor_offs  = glm::vec2(1,1);
					break;
				case TextAnchorr::TOP_LEFT:
					anchor_offs  = glm::vec2(-1,1);
					break;
				case TextAnchorr::BOT:
					anchor_offs  = glm::vec2(0,-1);
					break;
				case TextAnchorr::BOT_RIGHT:
					anchor_offs  = glm::vec2(1,-1);
					break;
				case TextAnchorr::BOT_LEFT:
					anchor_offs  = glm::vec2(-1,-1);
					break;
				case TextAnchorr::RIGHT:
					anchor_offs  = glm::vec2(1,0);
					break;
				case TextAnchorr::LEFT:
					anchor_offs = glm::vec2(-1,0);
					break;
			}
			anchor_offs *= glm::vec2(0.5f,-0.5f);
			anchor_offs += 0.5f;

			starting_offs.x -= accum_offs.x * anchor_offs.x;
			starting_offs.y += accum_offs.y * anchor_offs.y;
			// starting_offs.x -= max_row_w * 0.5;
			offs.x = starting_offs.x;
			offs.y = starting_offs.y;
		}
	}


	int i = -1;
	int chars = 0;
	int row = 0;

	auto center = [&](int _row) {
		offs.x += max_row_w*0.5;
		offs.x -= row_widths[_row]*0.5f;
	};

	if(desc.centered) {
		center(0);
	}
	for(char ch : desc.text) {
		i++;
		WGlyph glyph = font->w_glyphs[int(ch)];

		// glm::vec2 uv_coords = glm::vec2(
		// 	glyph.atlas_bound_right - glyph.atlas_bound_left,
		// 	glyph.atlas_bound_top - glyph.atlas_bound_bot
		// )/canvas_sz;

		if(ch == '\r') {
		} else if(ch == '\n') {
			row++;
			offs.x = starting_offs.x;
			if(desc.centered) {
				center(row);
			}
			offs.y -= vertical_step;
		} else if(ch == ' ') {
			float advance = space_advance;
			if(i < desc.text.size() -1) {
				// !!!! TODO: Advance

			}
			offs.x += fs_scale * advance * desc.sz * 1.0f/aspect;
		} else {
			vec2 bot_l_uv = vec2(glyph.atlas_bound_left, glyph.atlas_bound_bot);
			vec2 top_l_uv = vec2(glyph.atlas_bound_left, glyph.atlas_bound_top);
			vec2 bot_r_uv = vec2(glyph.atlas_bound_right, glyph.atlas_bound_bot);
			vec2 top_r_uv = vec2(glyph.atlas_bound_right, glyph.atlas_bound_top);

			vec2 bot_l_quad = vec2(glyph.plane_bound_left, glyph.plane_bound_bot) * quad_mult + offs;
			vec2 top_l_quad = vec2(glyph.plane_bound_left, glyph.plane_bound_top) * quad_mult + offs;
			vec2 bot_r_quad = vec2(glyph.plane_bound_right, glyph.plane_bound_bot) * quad_mult + offs;
			vec2 top_r_quad = vec2(glyph.plane_bound_right, glyph.plane_bound_top) * quad_mult + offs;
			
			std::array<glm::vec2, 6> quad_positions = {
				bot_l_quad,
				bot_r_quad,
				top_r_quad,
				bot_l_quad,
				top_r_quad,
				top_l_quad
			};
			std::array<glm::vec2, 6> quad_uvs = {
				bot_l_uv,
				bot_r_uv,
				top_r_uv,
				bot_l_uv,
				top_r_uv,
				top_l_uv
			};

			for(int i = 0; i < 6; i++){
				*buff_ptr++ = quad_positions[i][0];
				*buff_ptr++ = quad_positions[i][1];
				*buff_ptr++ = float(desc.outline);
				*(int*)buff_ptr++ = this->transforms_container->curr_trans_buff_idx;
				*buff_ptr++ = desc.bg_col.x;
				*buff_ptr++ = desc.bg_col.y;
				*buff_ptr++ = desc.bg_col.z;
				*buff_ptr++ = desc.bg_col.w;
				*buff_ptr++ = quad_positions[i][0];
				*buff_ptr++ = quad_positions[i][1];
				*buff_ptr++ = quad_uvs[i][0];
				*buff_ptr++ = quad_uvs[i][1];
				*buff_ptr++ = desc.col.r;
				*buff_ptr++ = desc.col.g;
				*buff_ptr++ = desc.col.b;
				*buff_ptr++ = desc.col.w;
				*buff_ptr++ = desc.pos.x;
				*buff_ptr++ = desc.pos.y;
				*buff_ptr++ = desc.pos.z;
				*(int*)buff_ptr++ = int(desc.const_size);
				// *buff_ptr++ = desc.pos.w;
			}

			chars++;
			if(i < desc.text.size() - 1) {
				offs.x += get_advance(int(desc.text[i]), int(desc.text[i + 1]));
			}
		}
	}
	// for(int i = 0; i < )



	this->char_cnt += chars;
}

