#include "pch.h"

// #include <ranges>

// #include "audio/AudioMixer.h"
#include "Camera.h"
#include "Engine.h"
#include "engine/LUT.h"
#include "gl/Framebuffer.h"
#include "gl/Shader.h"
#include "gl/ShaderManager.h"
#include "gl/ShaderProgram.h"
#include "gl/WGL.h"
#include "gl/Buffer.h"

#include "EditorNew.h"


// #define min(a, b) (a<b ? a : b)


#include <shellapi.h>
#include <stacktrace>

#include "WEntity.h"
#include "IO.h"
#include "Light.h"
#include "TexturePool.h"
#include "model/ModelNode.h"

KeyState::KeyState(bool pressed, bool just_pressed, bool just_unpressed): down(pressed), just_pressed(just_pressed),
	just_unpressed(just_unpressed) {}


void Editor::init() {
	this->prog_physics_dbg = new ShaderProgram(
		{
			new Shader("engine/dbg_physics_lines.vert"),
			new Shader("engine/dbg_physics_lines.frag"),
		}
	);


	this->physics_debug_drawer_buff = new Buffer({
		.byte_len = 10 * 2048 * 4, .subdata_disabled = false, .name = "Buff Physics Debug Drawer"
	});

	#ifdef DBG_PRINT
	this->printf_buffer = new Buffer(BuffDesc{.
		byte_len = 200000 * 4,
		.subdata_disabled = true,
		.mappable_write = false,
		.mappable_read = true,
		.persistennt = true,
		.write_from_shader = true,
		.name = "Printf buff"
	});
	this->printf_buffer->bind_as_SSBO(1);
	#endif

}

Editor::Editor() {
	this->timePaused = false;
	this->times_since_notification_messages.resize(this->max_notification_messages);
	this->notification_messages.resize(this->max_notification_messages);
	for (float& t_since_notif : this->times_since_notification_messages) {
		t_since_notif = 100;
	}

	// todo: move to timeline constructor
	timeline.mFrameMin = 0;
	timeline.mFrameMax = 8000;
	timeline.lanes.reserve(1000);
	timeline.rampEdit.curve_names.resize(1000);
	timeline.rampEdit.mMax = ImVec2(1.f, 1.f);
	timeline.rampEdit.mMin = ImVec2(0.f, 0.f);

	
	this->cpu_timers.resize(40);
	
	
	// curve_points[0][0] = ImVec2(-10.f, 0);
	// curve_points[0][1] = ImVec2(20.f, 0.6f);
	// curve_points[0][2] = ImVec2(25.f, 0.2f);
	// curve_points[0][3] = ImVec2(70.f, 0.4f);
	// curve_points[0][4] = ImVec2(120.f, 1.f);
	// curve_point_counts[0] = 5;
	//
	// curve_points[1][0] = ImVec2(-50.f, 0.2f);
	// curve_points[1][1] = ImVec2(33.f, 0.7f);
	// curve_points[1][2] = ImVec2(80.f, 0.2f);
	// curve_points[1][3] = ImVec2(82.f, 0.8f);
	// curve_point_counts[1] = 4;
	//
	//
	// curve_points[2][0] = ImVec2(40.f, 0);
	// curve_points[2][1] = ImVec2(60.f, 0.1f);
	// curve_points[2][2] = ImVec2(90.f, 0.82f);
	// curve_points[2][3] = ImVec2(150.f, 0.24f);
	// curve_points[2][4] = ImVec2(200.f, 0.34f);
	// curve_points[2][5] = ImVec2(250.f, 0.12f);
	// curve_point_counts[2] = 6;
	// curve_is_visibles[0] = curve_is_visibles[1] = curve_is_visibles[2] = true;
	
	// timeline.sequences.push_back(Timeline::Sequence{ 0, 0, 30, false });
	// timeline.sequences.push_back(Timeline::Sequence{ 1, 1, 30, true });
	// timeline.sequences.push_back(Timeline::Sequence{ 3, 2, 60, false });
	// timeline.sequences.push_back(Timeline::Sequence{ 2, 3, 90, false });
	// timeline.sequences.push_back(Timeline::Sequence{ 4, 4, 99, false });
}

void Editor::handle_shader_reload() {
	bool pendingShaderReload = WEngine->shader_manager->pendingShaderReload.load();
	bool pending_reload_all_shaders = WEngine->shader_manager->pending_reload_all_shaders.load();
	std::mutex* shaderReloadMutex = &WEngine->shader_manager->shaderReloadMutex;
	std::vector<std::string>* shadersToReload = &WEngine->shader_manager->shadersToReload;
	if (pendingShaderReload) {
		// Sort the vector to bring duplicates together
		std::sort(shadersToReload->begin(), shadersToReload->end());

		// Use std::unique to remove adjacent duplicates
		auto uniqueEnd = std::unique(shadersToReload->begin(), shadersToReload->end());

		// Erase the duplicates from the vector
		shadersToReload->erase(uniqueEnd, shadersToReload->end());
		
		WEngine->shader_manager->shader_just_reloaded = true;
		// Sleep(40);
		std::vector<std::string> localShaders;

		// fill localShaders
		shaderReloadMutex->lock();
		WEngine->shader_manager->pendingShaderReload.store(false);
		WEngine->shader_manager->pending_reload_all_shaders.store(false);

		// int shadersCnt = shadersToReload->size();
		// for (int i = 0; i < shadersCnt; i++) {
		// 	int revIdx = shadersCnt - i - 1;
		// 	localShaders.push_back(shadersToReload->at(revIdx));
		// 	shadersToReload->pop_back();
		// }
		// shaderReloadMutex->unlock();
		shaderReloadMutex->unlock();

		if(pending_reload_all_shaders) {
			for (Shader* shader : WEngine->shader_manager->shaders) {
				shader->compile();
			}
			for (ShaderProgram* shader_program : WEngine->shader_manager->shader_programs) {
				shader_program->link();
				for (int i = 0; i < shader_program->uniforms_map.sz; i++) {
					shader_program->uniforms_map.vals[i] = glGetUniformLocation(
						shader_program->pid,
						shader_program->uniforms_map.keys[i].c_str()
					);
				}
			}
		} else {
			std::vector<ShaderProgram*> shader_progs_to_link;
			for(std::string& shader_path : *shadersToReload) {
				for(Shader* shader : WEngine->shader_manager->shaders) {
					if(shader->path == shader_path) {
						shader->compile();
						for(ShaderProgram* shader_prog : shader->shader_programs) {
							shader_progs_to_link.push_back(shader_prog);
						}
					}
				}
			}
			for (ShaderProgram* shader_program : shader_progs_to_link) {
				shader_program->link();
				for (int i = 0; i < shader_program->uniforms_map.sz; i++) {
					shader_program->uniforms_map.vals[i] = glGetUniformLocation(
						shader_program->pid,
						shader_program->uniforms_map.keys[i].c_str()
					);
				}
			}
		}
		shadersToReload->resize(0);
		this->gui_dbg_print_msg("Shader Reloaded");
	}
}

// KeyState Editor::get_key(Key key) {
// 	return this->get_key(uint64_t(key));
// }

void Editor::gui_dbg_print_msg(std::string_view msg) {
	int idx = this->notification_message_idx = (this->notification_message_idx + 1) % this->max_notification_messages;
	this->times_since_notification_messages[idx] = 0;
	this->notification_messages[idx] = msg;
}

void Editor::start_timer_query(std::string_view name) {
	#ifdef EDITOR
	// gl_timer_queries - string -> [10 queries]
	// queries_used_this_frame - string
	
	if (there_is_debug_group_pushed) {
		glPopDebugGroup();
	}
	
	uint32_t curr_query;
	auto create_query = [&](std::string_view _name) {
		int query_cnt = this->gl_timer_queries.size();
		this->gl_timer_query_indices[query_cnt] = std::string(_name);
		std::array<uint32_t, 10> query_arr;
		for (int i = 0; i < 10; i++) {
			// lol
			uint32_t temp_q;
			glGenQueries(1, &temp_q);
			query_arr[i] = temp_q;
		}
		curr_query = query_arr[0]; // assume frame 0
		this->gl_timer_queries[&_name[0]] = query_arr;
	};

	if (this->gl_timer_queries.contains(&name[0])) {
		auto find_query_used_this_frame = [&](std::string_view _name) -> bool {
			const bool found = std::find(
				this->queries_used_this_frame.begin(),
				this->queries_used_this_frame.end(),
				SIMDString<64>(_name)
			) != this->queries_used_this_frame.end();
			return found;
		};

		// int idx 
		const bool found_this_frame = find_query_used_this_frame(name);
		// this->gl_timer_queries.contains(&name[0])

		if(found_this_frame){
			int max_idx = -1;
			for(auto& q : this->queries_used_this_frame) {
				if(q.starts_with(name)) {
					if(q.length() == name.length() + 2) {
						if(isdigit(q.back())) {
							int digit = q.back() - '0';
							if(digit > max_idx) {
								max_idx = digit;
							}
						}
					} else {
						if(0 > max_idx) {
							max_idx = 0;
						}
					}
				}
			}
			max_idx++;
			// std::string
			SIMDString<64> found_name(name);
			SIMDString<64> append;
			if(max_idx > 0) {
				append += " ";
				append += std::to_string(max_idx);
				found_name += append;
			}
			
			this->queries_used_this_frame.push_back( found_name );
			
			if(this->gl_timer_queries.contains(&found_name[0])) {
				curr_query = this->gl_timer_queries[&found_name[0]][WEngine->frame % 10];
			} else {
				create_query(&found_name[0]);
			}
		} else {
			// curr_query = this->gl_timer_queries[&name[0]];
			curr_query = this->gl_timer_queries[&name[0]][WEngine->frame % 10];
			this->queries_used_this_frame.push_back(&name[0]);
		}
	} else {
		create_query(name);
	}
	
	glQueryCounter(curr_query, GL_TIMESTAMP);

	glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, &name[0]);

	this->there_is_debug_group_pushed = true;

	#endif
}

void Editor::start_cpu_timer(std::string_view name) {
	#ifdef EDITOR
			// if(thi)
			int timer_idx = this->curr_cpu_timer_idx++;
			CPUTimer& timer = this->cpu_timers[timer_idx];
			
			timer.time_miliseconds = 0;
			timer.name = &name[0];
			timer.start_time = std::chrono::steady_clock::now();
	#endif
}

void Editor::end_cpu_timer() {
	#ifdef EDITOR
			if(this->curr_cpu_timer_idx > 0){
				this->cpu_timers[this->curr_cpu_timer_idx - 1].end_time = std::chrono::steady_clock::now();
			}
	#endif
}

GUIDbgVal* Editor::get_dbg_val(std::string_view name) {
#ifdef EDITOR
	bool contains = false;

	GUIDbgVal* dbg_val = nullptr;
	for (int i = 0; i < this->gui_dbg_vals.size(); i++) {
		dbg_val = &this->gui_dbg_vals[i];
		if (dbg_val->name == name) {
			contains = true;
			break;
		}
	}
	if (!contains) {
		this->gui_dbg_vals.push_back(
			GUIDbgVal{std::string(name)}
		);
		dbg_val = &this->gui_dbg_vals[this->gui_dbg_vals.size() - 1];
	}
	return dbg_val;
#else
	return nullptr;
#endif
}

void Editor::gui_dbg_print(std::string_view name, glm::vec2 value) {
#ifdef EDITOR
	GUIDbgVal* dbg_val = get_dbg_val(name);
	dbg_val->v2_val = value;
	dbg_val->typ = GUICtrlType::Vec2;
#endif
}

void Editor::gui_dbg_print(std::string_view name, float value) {
#ifdef EDITOR
	GUIDbgVal* dbg_val = get_dbg_val(name);
	dbg_val->f_val = value;
	dbg_val->typ = GUICtrlType::Float;
#endif
}

void Editor::gui_dbg_print(std::string_view name, int value) {
#ifdef EDITOR
	GUIDbgVal* dbg_val = get_dbg_val(name);
	dbg_val->i_val = value;
	dbg_val->typ = GUICtrlType::Int;
#endif
}

void Editor::gui_dbg_print(std::string_view name, uint value) {
#ifdef EDITOR
	this->gui_dbg_print(name, (int)value);
#endif
}

void Editor::gui_dbg_print(std::string_view name, glm::vec3 value) {
#ifdef EDITOR
	GUIDbgVal* dbg_val = get_dbg_val(name);
	dbg_val->v3_val = value;
	dbg_val->typ = GUICtrlType::Vec3;
#endif
}

void Editor::gui_dbg_print(std::string_view name, glm::mat4 value) {
	#ifdef EDITOR
	GUIDbgVal* dbg_val = get_dbg_val(name);
	dbg_val->m4_val = value;
	dbg_val->typ = GUICtrlType::Mat4;
	#endif
}


void Editor::add_gui_control(std::string_view name, bool* value_ptr, GUIControlArgs args) {
	args.type = GUICtrlType::Bool;
	#ifdef EDITOR
	bool already_exists = false;
	for (GUIControl& gui_ctrl : this->gui_controls) {
		if (gui_ctrl.name == name) {
			already_exists = true;
			gui_ctrl.range_min = args.range_min;
			gui_ctrl.range_max = args.range_max;
			break;
		}
	}
	if (!already_exists) {
		this->gui_controls.push_back(GUIControl(std::string(name), value_ptr, args));
	}
	#endif
}

void Editor::add_gui_control(std::string_view name, glm::vec2* value_ptr, GUIControlArgs args) {
	args.type = GUICtrlType::Vec2;
	this->add_gui_control(name, &value_ptr->x, args);
}

void Editor::add_gui_control(std::string_view name, glm::vec3* value_ptr, GUIControlArgs args) {
	args.type = GUICtrlType::Vec3;
	this->add_gui_control(name, &value_ptr->x, args);
}

// TODO: aliasing, cast to char*
void Editor::add_gui_control(std::string_view name, float* value_ptr, GUIControlArgs args) {
	#ifdef EDITOR
	bool already_exists = false;
	for (GUIControl& gui_ctrl : this->gui_controls) {
		if (gui_ctrl.name == name) {
			already_exists = true;
			gui_ctrl.range_min = args.range_min;
			gui_ctrl.range_max = args.range_max;
			break;
		}
	}
	if (!already_exists) {
		this->gui_controls.push_back(GUIControl(std::string(name), value_ptr, args));
	}
	#endif
}

void Editor::add_gui_control(std::string_view name, int* value_ptr, GUIControlArgs args) {
	#ifdef EDITOR
	bool already_exists = false;
	for (GUIControl& gui_ctrl : this->gui_controls) {
		if (gui_ctrl.name == name) {
			gui_ctrl.range_min = args.range_min;
			gui_ctrl.range_max = args.range_max;
			already_exists = true;
			break;
		}
	}

	if (!already_exists) {
		args.type = GUICtrlType::Int;
		this->gui_controls.push_back(GUIControl(std::string(name), value_ptr, args));
	}
	#endif
}

void Editor::expose_lut(LUT* lut) {
	// lut->lut_paths.push_back(lut);
	this->luts.push_back(lut);
}

#include <psapi.h>

__declspec(noinline) void Editor::start_frame() {
	#ifdef EDITOR

	// clock_t newClock;
	// newClock = clock();
	// long new_cpu_time = clock();
	this->cpu_timestamp_start_of_frame = clock();
	chrono_cpu_timestamp_start_of_frame = std::chrono::high_resolution_clock::now();


	for (float& t_since_notif_message : this->times_since_notification_messages) {
		t_since_notif_message += WEngine->delta_time_internal;
	}
	this->frame_times[
		this->frame_time_index = (this->frame_time_index + 1) % this->frame_times.size()
	] = WEngine->delta_time_internal;

	if (WEngine->io->get_key(Key::F1).just_pressed) {
		if (WEngine->camera->noclip = !WEngine->camera->noclip) {
			this->gui_dbg_print_msg("Noclip ON");
		} else {
			this->gui_dbg_print_msg("Noclip OFF");
		}
	}
	if (WEngine->io->get_key(Key::F2).just_pressed) {
		if (this->physics_debugger_enabled = !this->physics_debugger_enabled) {
			this->gui_dbg_print_msg("Physics Debug ON");
		} else {
			this->gui_dbg_print_msg("Physics Debug OFF");
		}
	}
	if (!this->renderingPaused) {
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplWin32_NewFrame();
		ImGui::NewFrame();
		if (!this->imgui_menu_collapsed) {
			ImGui::Begin("Ctrl");

			
			// draw_title("DBG PRINT VALS");
			ImGui::Checkbox("Pause time", &timePaused);
			
			HWND& hwnd = WEngine->wgl->hwnd;
			if (ImGui::Checkbox("On top", &windowOnTop)) {
				if (windowOnTop)
					SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
				else
					SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
			}

			ImGui::SameLine();


			ImGui::SameLine();

			if (ImGui::Button("Pause Rendering")) {
				if(this->renderingPaused) {
					this->toEndPausedRendering = true;
				} else {
					this->toStartPausedRendering = true;
				}
			};
		}
		
	} 
	
	this->handle_shader_reload();
	#endif
}

void Editor::draw_physics_debug() {
	glLineWidth(2.0f);
	if (!this->physics_debugger_enabled) {
		return;
	}
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	float* physics_drawer_ptr = (float*)this->physics_debug_drawer_buff->cpu_data;

	auto get_box = [](
		glm::vec3 bounds_min,
		glm::vec3 bounds_max
	) -> std::array<glm::vec3, 24> {
		std::array<glm::vec3, 24> ret;

		// Define the vertices of the cube
		glm::vec3 vertices[8] = {
			{bounds_min.x, bounds_min.y, bounds_min.z}, // 0
			{bounds_max.x, bounds_min.y, bounds_min.z}, // 1
			{bounds_max.x, bounds_max.y, bounds_min.z}, // 2
			{bounds_min.x, bounds_max.y, bounds_min.z}, // 3
			{bounds_min.x, bounds_min.y, bounds_max.z}, // 4
			{bounds_max.x, bounds_min.y, bounds_max.z}, // 5
			{bounds_max.x, bounds_max.y, bounds_max.z}, // 6
			{bounds_min.x, bounds_max.y, bounds_max.z}  // 7
		};

		// Define the indices of the cube edges
		const int edges[12][2] = {
			{0, 1}, {1, 2}, {2, 3}, {3, 0}, // Bottom face
			{4, 5}, {5, 6}, {6, 7}, {7, 4}, // Top face
			{0, 4}, {1, 5}, {2, 6}, {3, 7}  // Connecting edges
		};

		// Fill the ret array with lines
		for (int i = 0; i < 12; ++i) {
			ret[i * 2] = vertices[edges[i][0]];
			ret[i * 2 + 1] = vertices[edges[i][1]];
		}

		return ret;
	};

	auto get_circ = [](
		glm::vec3 pos,
		float rad
	) -> std::array<glm::vec3, 24> {
		std::array<glm::vec3, 24> ret;

		for (int i = 0; i < 24; ++i) {
			float idx = float(i) / 24.0f;
			ret[i] = glm::vec3(
				pos.x + sin(idx * tau) * rad,
				pos.y + cos(idx * tau) * rad,
				pos.z
			);
		}

		return ret;
	};

	auto get_line = [](
		glm::vec3 start_pos,
		glm::vec3 end_pos
	) -> std::array<glm::vec3, 2> {
		return {start_pos, end_pos};
	};

	auto get_star = [](
		glm::vec3 pos,
		float w
	) -> std::array<glm::vec3, 6> {
		std::array<glm::vec3, 6> ret;

		int i = 0;
		ret[i++] = pos + glm::vec3(w, 0, 0);
		ret[i++] = pos + glm::vec3(-w, 0, 0);

		ret[i++] = pos + glm::vec3(0, w, 0);
		ret[i++] = pos + glm::vec3(0, -w, 0);

		ret[i++] = pos + glm::vec3(0, 0, w);
		ret[i++] = pos + glm::vec3(0, 0, -w);

		// ret[i++] = pos + glm::vec3(0,w,w);
		// ret[i++] = pos + glm::vec3(-w,-w,0);
		//
		// ret[i++] = pos + glm::vec3(w,w,0);
		// ret[i++] = pos + glm::vec3(0,-w,-w);

		return ret;
	};

	auto push_verts = [&](auto& verts, int& lines_cnt, glm::vec4 col, bool obscure) {
		for (glm::vec3 vert : verts) {
			*physics_drawer_ptr++ = vert.x;
			*physics_drawer_ptr++ = vert.y;
			*physics_drawer_ptr++ = vert.z;
			*physics_drawer_ptr++ = float(obscure);
			*physics_drawer_ptr++ = col.x;
			*physics_drawer_ptr++ = col.y;
			*physics_drawer_ptr++ = col.z;
			*physics_drawer_ptr++ = col.w;
			lines_cnt++;
		}
	};
	int lines_cnt = 0;

	bool newly_picked_entity_is_light = false;
	std::pair<float, void*> newly_picked_entity;
	newly_picked_entity.first = 1000.0;
	newly_picked_entity.second = nullptr;


	for (std::pair<const long, WEntity*> it : WEngine->entitties) {
		WEntity* game_object = it.second;

		glm::vec3 pos = game_object->get_pos();
		glm::vec3 pos_ndc = WEngine->camera->worldspace_to_ndc(pos);

		if (pos_ndc.z < 0.0) {
			float sz = 1. / glm::abs(pos_ndc.z);
			float len = glm::length(WEngine->io->mouse_ndc - glm::vec2(pos_ndc.x, pos_ndc.y));
			if (len < sz * 0.5) {
				if (len < newly_picked_entity.first) {
					newly_picked_entity = {len, game_object};
				}
			}
		}
	}

	for (int i = 0; i <= WEngine->alloc_lights.max_slot; i++) {
		if (WEngine->alloc_lights.slot_active[i]) {
			Light* light = &WEngine->alloc_lights.arr[i];

			glm::vec3 pos = light->pos;
			glm::vec3 pos_ndc = WEngine->camera->worldspace_to_ndc(pos);

			if (pos_ndc.z < 0.0) {
				float sz = 1. / glm::abs(pos_ndc.z);
				float len = glm::length(WEngine->io->mouse_ndc - glm::vec2(pos_ndc.x, pos_ndc.y));
				if (len < sz * 0.5) {
					if (len < newly_picked_entity.first) {
						newly_picked_entity = {len, light};
						newly_picked_entity_is_light = true;
					}
				}
			}
			// auto verts_line = get_circ(light.pos, 0.1f);
			// push_verts(verts,lines_cnt, glm::vec4(1,0.2,0,1), true);
		}

	}

	for (std::pair<const long, WEntity*> it : WEngine->entitties) {
		// static std::vector<>
		WEntity* game_object = it.second;

		if (game_object->physics_active) {
			auto bounds = game_object->jph_body->GetWorldSpaceBounds();
			auto box_verts = get_box(
				glm::vec3(bounds.mMin.GetX(), bounds.mMin.GetY(), bounds.mMin.GetZ()),
				glm::vec3(bounds.mMax.GetX(), bounds.mMax.GetY(), bounds.mMax.GetZ())
			);
			push_verts(box_verts, lines_cnt, glm::vec4(1), true);
		} else {
			const float box_sz = 0.01;
			glm::vec3 pos = game_object->get_pos();
			auto verts = get_star(pos, 0.5f);
			push_verts(verts, lines_cnt, glm::vec4(0, 1, 0, 1), false);
		}

		glm::vec3 pos = game_object->get_pos();

		// WObj<WEntity> me = WObj<WEntity>(game_object);
		WObj me_wobj = game_object;

		// SIMDString<> tt = std::to_string(game_object->game_obj_id).c_str();
		SIMDString<512> tt = std::to_string(game_object->game_obj_id).c_str();
		// WEngine->camera
		glm::vec3 pos_ndc = WEngine->camera->worldspace_to_ndc(pos);
		glm::vec4 bg_col = glm::vec4(0, 0, 0, 1);
		if (pos_ndc.z < 0.0) {
			// float sz = 1./glm::length(WEngine->camera->pos - pos);
			if (WEngine->editor->selected_entity.has_value() && !this->selected_entity_is_light) {
				if (me_wobj == (WEntity*)WEngine->editor->selected_entity.value()) {
					bg_col.b = 1.0f;
				}
			}

			float sz = 1. / glm::abs(pos_ndc.z);
			if (!WEngine->io->mouse_grabbed) {

				if (newly_picked_entity.second != nullptr) {
					if (!newly_picked_entity_is_light && WObj((WEntity*)newly_picked_entity.second) == me_wobj) {
						if (WEngine->io->mouse_rmb_just_pressed) {
							WEngine->editor->selected_entity = game_object;
							this->selected_entity_is_light = false;
						}
						bg_col.r = 1.0f;
					}
				}
			}

			// TextDescriptor t = TextDescriptor{
			// 	.pos = glm::vec2(pos_ndc.x, pos_ndc.y),
			// 	.col = glm::vec4(1, 1, 1, 1),
			// 	.sz = sz,
			// 	.bg_col = bg_col,
			// 	.text = tt
			// };
			// WEngine->text_drawer->draw(t);
		}
	}

	for (int i = 0; i <= WEngine->alloc_lights.max_slot; i++) {
		if (!WEngine->alloc_lights.slot_active[i]) {
			continue;
		}
		Light* light = &WEngine->alloc_lights.arr[i];
		auto verts = get_circ(light->pos, 0.2f);
		push_verts(verts, lines_cnt, glm::vec4(1, 0.2, 0, 1), true);
		auto verts_star = get_star(light->pos, 0.2f);
		push_verts(verts_star, lines_cnt, glm::vec4(1, 0.2, 0, 1), true);

		glm::vec3 pos = light->pos;

		WObj me_wobj = light;

		SIMDString<512> tt = std::to_string(me_wobj.get()->alloc_slot).c_str();

		// WEngine->camera
		glm::vec3 pos_ndc = WEngine->camera->worldspace_to_ndc(pos);
		glm::vec4 bg_col = glm::vec4(0, 0, 0, 1);
		if (pos_ndc.z < 0.0) {
			// float sz = 1./glm::length(WEngine->camera->pos - pos);
			if (WEngine->editor->selected_entity.has_value() && this->selected_entity_is_light) {
				if (me_wobj == (Light*)WEngine->editor->selected_entity.value()) {
					bg_col.b = 1.0f;
				}
			}

			float sz = 1. / glm::abs(pos_ndc.z);
			if (!WEngine->io->mouse_grabbed) {

				if (newly_picked_entity.second != nullptr) {
					if (newly_picked_entity_is_light && me_wobj == (Light*)newly_picked_entity.second) {
						if (WEngine->io->mouse_rmb_just_pressed) {
							WEngine->editor->selected_entity = light;
							this->selected_entity_is_light = true;
						}
						bg_col.r = 1.0f;
					}
				}
			}

			SIMDString<> name = " ";
			if (light->type == LightType::POINT) {
				name += "PL";
				name += std::to_string(i);
			} else if (light->type == LightType::DIRECTIONAL) {
				name += "DL";
				name += std::to_string(i);
			}

			// TextDescriptor t = TextDescriptor{
			// 	.pos = glm::vec2(pos_ndc.x, pos_ndc.y),
			// 	.col = glm::vec4(1, 1, 1, 1),
			// 	.sz = sz,
			// 	.bg_col = bg_col,
			// 	.text = name.c_str()
			// };
			// WEngine->text_drawer->draw(t);
		}
		// auto verts_line = get_circ(light.pos, 0.1f);
		// push_verts(verts,lines_cnt, glm::vec4(1,0.2,0,1), true);
	}

	glNamedBufferSubData(
		this->physics_debug_drawer_buff->pid,
		0,
		(physics_drawer_ptr - (float*)this->physics_debug_drawer_buff->cpu_data) * sizeof(float),
		this->physics_debug_drawer_buff->cpu_data
	);
	prog_physics_dbg->use();
	prog_physics_dbg->setUniform("buff", this->physics_debug_drawer_buff);
	if (depth_texture) {
		prog_physics_dbg->setUniform("depth_tex", depth_texture);
	} else {
		prog_physics_dbg->setUniform("depth_tex", 0);
	}

	glDrawArrays(GL_LINES, 0, lines_cnt);

	glDisable(GL_BLEND);
	// WEngine->text_drawer->flush();


}

__declspec(noinline) void Editor::end_frame() {
	ZoneScopedN("EDITOR END FRAME");
	#ifdef EDITOR
	long t = clock();
	

	auto end = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> duration = std::chrono::duration_cast<std::chrono::duration<double>>(
		end - this->chrono_cpu_timestamp_start_of_frame);
	float duration_miliseconds_float = float(duration.count()) * 1000.0f;

	this->cpu_time_curr_frame = glm::mix(
		this->cpu_time_curr_frame,
		duration_miliseconds_float,
		// (float(t - this->cpu_timestamp_start_of_frame)/CLOCKS_PER_SEC) * 1000.0f,
		0.05f
	);

	if (this->there_is_debug_group_pushed) {
		glPopDebugGroup();
		this->there_is_debug_group_pushed = false;
		// there_is_debug_group_pushed
	}
	if(!this->renderingPaused) {
		this->draw_physics_debug();
		this->do_imgui();
	}
	
	this->queries_used_this_frame.resize(0);
	// if (this->get_key(Key::Squirly).just_pressed) {
	if (
		WEngine->io->get_key(Key::Squirly).just_pressed ||
		WEngine->io->get_key(Key(223)).just_pressed
		) {
		this->imgui_menu_collapsed = !this->imgui_menu_collapsed;
	}
	
	this->curr_cpu_timer_idx = 0;
	#endif
}

void __stdcall Editor::GLErrorCallback(GLenum source,
	GLenum type,
	GLuint id,
	GLenum severity,
	GLsizei length,
	const GLchar* message,
	const void* userParam) {

	if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
		return;

	if (
		(type == 33360 && id == 131218) || // shader recompile lol
		(type == 33356 && id == 1282) ||   // invalid query buffer
		false
	)
		return;
	const char* severityStr = severity == GL_DEBUG_SEVERITY_NOTIFICATION
		                          ? "Notification"
		                          : severity == GL_DEBUG_SEVERITY_LOW
		                          ? "Low"
		                          : severity == GL_DEBUG_SEVERITY_MEDIUM
		                          ? "Med"
		                          : severity == GL_DEBUG_SEVERITY_HIGH
		                          ? "High"
		                          : "";

	wlog_err("---------- GL ERROR ----------");
	// std::basic_stacktrace<std::allocator<std::stacktrace_entry>> stacktrace = std::stacktrace::current();st
	std::string stacktrace = std::to_string(std::stacktrace::current());
	// std::cout << std::stacktrace::current() << '\n';
	// fprintf(stderr, " -- GL: %s type = 0x%x, severity = %s, \n message = %s\n",
	// 	(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
	// 	type, severityStr, message);
	// char buffer[1024];  // Adjust size accordingly
	// sprintf(buffer, " -- GL: %s type = 0x%x, severity = %s, \n message = %s\n",
	// 				(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message);
	// sprintf()

	#ifdef DISTRIB
		int msgboxID = MessageBox(
				NULL,
				message,
				"gl error",
				MB_OK | MB_ICONERROR
		);
	#endif
	//
	// int msgboxIDb = MessageBox(
	// 		NULL,
	// 		stacktrace.c_str(),
	// 		"gl error stacktrace",
	// 		MB_OK | MB_ICONERROR
	// );
	
	// __debugbreak();
	int a = 0;
}



// void Editor::handle_mouse() {
// 	WEngine->io->handle_mouse();
// }





void Editor::do_imgui() {
	ZoneScopedN("EDITOR IMGUI");
	#ifdef EDITOR
	HWND& hwnd = WEngine->wgl->hwnd;

	//  ------------------------------ UTILS ------------------------------ //
	//  ------------------------------ UTILS ------------------------------ //
	//  ------------------------------ UTILS ------------------------------ //

	auto display_float = [&](float f) {
		ImGui::Text("%f", f);
	};
	auto display_int = [&](int i) {
		ImGui::Text("%i", i);
	};
	auto display_f2 = [&](glm::vec2 f) {
		ImGui::Text("vec2(%f, %f)", f.x, f.y);
	};
	auto display_f3 = [&](glm::vec3 f) {
		ImGui::Text("vec3(%f, %f, %f)", f.x, f.y, f.z);
	};
	auto display_f4 = [&](glm::vec4 f) {
		ImGui::Text("vec4(%f, %f, %f, %f)", f.x, f.y, f.z, f.w);
	};
	auto display_quat = [&](glm::quat f) {
		display_f4(glm::vec4(f.w, f.x, f.y, f.z));
	};
	auto display_m4 = [](glm::mat4 m) {
		ImGui::Text("mat4( ");
		float* v_ptr = &m[0][0];

		for (int k = 0; k < 16; k++) {
			if (k % 4 != 0) {
				ImGui::SameLine();
			} else {
				ImGui::Text("   ");
				ImGui::SameLine();
			}
			float float_v = v_ptr[k];
			ImGui::Text("%f, ", float_v);
		}
		ImGui::Text(")");
	};

	const float slider_speed = 0.01;
	auto slider_f = [&](std::string_view name, float* f) {
		// ImGui::SliderFloat(name, f);
		ImGui::DragFloat(&name[0], f, slider_speed);
	};
	auto slider_f2 = [&](std::string_view name, glm::vec2* f) {
		ImGui::DragFloat2(&name[0], &f->x, slider_speed);
	};
	auto slider_f3 = [&](std::string_view name, glm::vec3* f) {
		ImGui::DragFloat3(&name[0], &f->x, slider_speed);
	};
	auto slider_f4 = [&](std::string_view name, glm::vec4* f) {
		ImGui::DragFloat4(&name[0], &f->x, slider_speed);
	};


	//  ------------------------------ RENDER FNS-------------------------- //
	//  ------------------------------ RENDER FNS-------------------------- //
	//  ------------------------------ RENDER FNS-------------------------- //

	
	
	auto DrawColoredRectangle = [&](float percentage, ImVec4 color, float rounding) -> void {
		ImGuiWindow* window = ImGui::GetCurrentWindow();
		// ImVec2 cursorPos = window->DC.CursorPos;
		ImVec2 cursorPos = ImGui::GetCursorScreenPos();
		float windowWidth = ImGui::GetWindowWidth();

		ImDrawList* drawList = ImGui::GetWindowDrawList();
		drawList->AddDrawCmd();

		ImVec2 rectSize = ImVec2(windowWidth * percentage, ImGui::GetTextLineHeight());
		// ImVec2 rectPos = ImVec2(cursorPos.x, cursorPos.y + window->DC.CurrentLineTextBaseOffset);
		ImVec2 rectPos = ImVec2(cursorPos.x, cursorPos.y + window->DC.CurrLineTextBaseOffset);


		drawList->AddRectFilled(
			rectPos,
			ImVec2(rectPos.x + rectSize.x, rectPos.y + rectSize.y),
			IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255),
			rounding
		);
	};
	auto draw_title = [&](std::string_view title, std::optional<std::string_view> string_fmt_param = std::nullopt){
		DrawColoredRectangle(1, ImVec4(0.2,0.2,0.2,1), 10.0f);
		if(string_fmt_param.has_value()){
			ImGui::Text(&title[0], &string_fmt_param.value()[0]);
		} else {
			ImGui::Text(&title[0]);
		}
	};
	
	auto display_selected_light = [&](Light* light) {
		SIMDString<> name = "";
		if (light->type == LightType::POINT) {
			name += "Point light ";
			// name += std::to_string(i);
		} else if (light->type == LightType::DIRECTIONAL) {
			name += "Directional light ";
			// name += std::to_string(i);
		}
		name += std::to_string(WEngine->alloc_lights.get_index(light));
		ImGui::SeparatorText("");
		ImGui::SeparatorText(name.c_str());
		ImGui::SeparatorText("");
		slider_f3("Pos", &light->pos);
		slider_f3("Dir", &light->dir);
		slider_f("Near", &light->near_plane);
		slider_f("Far", &light->far_plane);
		slider_f("Intensity", &light->intensity);
		slider_f("Radius", &light->radius);
		slider_f3("Target", &light->target);
		// slider_f3("Target", &light->colour);
		// ImGui::ColorEdit3("Col", &light->colour[0]);
		ImGui::ColorPicker3("Col", &light->colour[0], ImGuiColorEditFlags_Float);
		// ImGui::ColorEdit3("Col", &light->colour[0], ImGuiColorEditFlags_Float);
		// ImGui::SeparatorText(name.c_str());
		ImGui::SeparatorText("");
	};
	auto display_gui_dbg_vals = [&]() {
		if(this->gui_dbg_vals.size() > 0){
			draw_title("DBG PRINT VALS");
		}
		for (GUIDbgVal& dbg_val : this->gui_dbg_vals) {
			ImGui::Text(dbg_val.name.c_str());
			ImGui::SameLine();
			if (dbg_val.typ == GUICtrlType::Float) {
				ImGui::Text(" : ");
				ImGui::SameLine();
				display_float(dbg_val.f_val);
			}else if (dbg_val.typ == GUICtrlType::Int){
				ImGui::Text(" : ");
				ImGui::SameLine();
				display_int(dbg_val.i_val);
			} else if (dbg_val.typ == GUICtrlType::Vec2) {
				ImGui::Text(" : ");
				ImGui::SameLine();
				display_f2(dbg_val.v2_val);
			} else if (dbg_val.typ == GUICtrlType::Vec3) {
				ImGui::Text(" : ");
				ImGui::SameLine();
				display_f3(dbg_val.v3_val);
			} else if(dbg_val.typ == GUICtrlType::Mat4){
				ImGui::Text(" : ");
				ImGui::SameLine();
				display_m4(dbg_val.m4_val);
			}
			// ctrl.
		}
	};
	auto display_gui_controls = [&]() {
		ZoneScopedN("IMGUI GUI CONTROLS");
		draw_title("USER CTRLS");
		for (GUIControl& ctrl : this->gui_controls) {
			if (ctrl.type == GUICtrlType::Float) {
				if (ctrl.range_min == 0 && ctrl.range_max == 0) {
					if (ImGui::DragFloat(&ctrl.name[0], (float*)ctrl.value_ptr, ctrl.precision)) {
						if (ctrl.callback) {
							ctrl.callback();
						}
					}
				} else {
					// ImGui::DragFloat(&ctrl.name[0], ctrl.value_ptr, 0.01, ctrl.range_min, ctrl.range_min);
					if (ImGui::SliderFloat(&ctrl.name[0], (float*)ctrl.value_ptr, ctrl.range_min, ctrl.range_max, "%.3f")) {
						if (ctrl.callback) {
							ctrl.callback();
						}
					}
				}
			} else if (ctrl.type == GUICtrlType::Vec2) {
				if (ctrl.range_min == 0 && ctrl.range_max == 0) {
					if (ImGui::DragFloat2(&ctrl.name[0], (float*)ctrl.value_ptr, ctrl.precision)) {
						if (ctrl.callback) {
							ctrl.callback();
						}
					}
				} else {
					// ImGui::DragFloat(&ctrl.name[0], ctrl.value_ptr, 0.01, ctrl.range_min, ctrl.range_min);
					if (ImGui::SliderFloat2(&ctrl.name[0], (float*)ctrl.value_ptr, ctrl.range_min, ctrl.range_max, "%.3f")) {
						if (ctrl.callback) {
							ctrl.callback();
						}
					}
				}
			} else if (ctrl.type == GUICtrlType::Vec3) {
				if(ctrl.is_colour) {
					ImGui::ColorPicker3(
						&ctrl.name[0], 
						(float*)ctrl.value_ptr, 
					ImGuiColorEditFlags_Float 
						| ImGuiColorEditFlags_NoDragDrop
						| ImGuiColorEditFlags_DisplayRGB
						);
				} else {
					if (ctrl.range_min == 0 && ctrl.range_max == 0) {
						if (ImGui::DragFloat3(&ctrl.name[0], (float*)ctrl.value_ptr, ctrl.precision)) {
							if (ctrl.callback) {
								ctrl.callback();
							}
						}
					} else {
						// ImGui::DragFloat(&ctrl.name[0], ctrl.value_ptr, 0.01, ctrl.range_min, ctrl.range_min);
						if (ImGui::SliderFloat3(&ctrl.name[0], (float*)ctrl.value_ptr, ctrl.range_min, ctrl.range_max, "%.3f")) {
							if (ctrl.callback) {
								ctrl.callback();
							}
						}
					}
				}
			} else if (ctrl.type == GUICtrlType::Int) {
				if (ctrl.range_min == 0 && ctrl.range_max == 0) {
					if (ImGui::DragInt(&ctrl.name[0], (int*)ctrl.value_ptr, ctrl.precision)) {
						if (ctrl.callback) {
							ctrl.callback();
						}
					}
				} else {
					// ImGui::DragFloat(&ctrl.name[0], ctrl.value_ptr, 0.01, ctrl.range_min, ctrl.range_min);
					if (ImGui::SliderInt(&ctrl.name[0], (int*)ctrl.value_ptr, (int)ctrl.range_min, (int)ctrl.range_max)) {
						if (ctrl.callback) {
							ctrl.callback();
						}
					}
				}
			} else if (ctrl.type == GUICtrlType::Bool) {
				bool* val = (bool*)ctrl.value_ptr;
				if (ImGui::Checkbox(&ctrl.name[0], val)) {
					// *val = !val;
					if (ctrl.callback) {
						ctrl.callback();
					}
				}
			}
		}

	};
	auto draw_timer_queries = [&]() {
		ZoneScopedN("IMGUI TIMER QUERIES");
		uint64_t prev_res;

		const SIMDString<64>* prev_name = nullptr;
		bool a_query_failed = false;

		while (!a_query_failed && this->timer_query_fallback_idx != WEngine->frame % 10 && WEngine->frame > 20) {
			int i = 0;
			for (SIMDString<64>& name : this->queries_used_this_frame) {
			// }
			// for (auto& timer_q_index : this->gl_timer_query_indices) {
				// auto& q = &this->gl_timer_queries[timer_q_index.second];
				// const std::string& name = timer_q_index.second;
				uint32_t query_gl_id = this->gl_timer_queries[&name[0]][this->timer_query_fallback_idx];
				uint64_t query_res = 0;
				if (i <= 2) {
					glGetQueryObjectui64v(query_gl_id, GL_QUERY_RESULT_NO_WAIT, &query_res);
				} else {
					glGetQueryObjectui64v(query_gl_id, GL_QUERY_RESULT, &query_res);
				}

				if (query_res == 0) {
					a_query_failed = true;
					break;
				} else {
					if (i == 0) {
						prev_res = query_res;
					} else if (i >= 2) {
						double delta_time = double(int64_t(query_res) - int64_t(prev_res)) / double(1000000.0);

						std::string ss = std::string(&(*prev_name)[0]);
						if (!this->gl_timer_query_cached_values.contains(ss)) {
							this->gl_timer_query_cached_values[ss] = TimerQueryState(0, 0, 0);
						} else {
							TimerQueryState& query_state = this->gl_timer_query_cached_values[ss];
							query_state.delta_time = std::lerp(query_state.delta_time, delta_time, 0.06);
							// query_state.delta_time = std::lerp(query_state.delta_time, delta_time, 1.0);
						}
					}

					prev_res = query_res;
					prev_name = &name;
					i++;
				}
			}
			if (!a_query_failed) {
				this->timer_query_fallback_idx++;
				this->timer_query_fallback_idx = this->timer_query_fallback_idx % 10;
			}
		}

		if (WEngine->frame % 15 == 0) {
			double total_t = 0.0;
			int i = 0;
			
			for (SIMDString<64>& name : this->queries_used_this_frame) {
				TimerQueryState& q = this->gl_timer_query_cached_values[&name[0]];
				total_t += q.delta_time;
			}
			for (SIMDString<64>& name : this->queries_used_this_frame) {
				TimerQueryState& q = this->gl_timer_query_cached_values[&name[0]];
				q.delta_time_display = q.delta_time;
				q.percentage_total = q.delta_time / total_t;
			}
		}

		double total_gpu_time_ms = 0.0;
		for (auto& timer_q_index : this->gl_timer_query_indices) {
			const std::string& name = timer_q_index.second;
			if (this->gl_timer_query_cached_values.contains(name)) {
				// const std::string& name = q.first;
				auto q = this->gl_timer_query_cached_values[name];
				double delta_time = q.delta_time_display;
				// double percentage_time = q.percentage_total;
				total_gpu_time_ms += delta_time;
			}
		}
		
		draw_title("GPU TIMES");
		ImGui::Text("GPU: %f ms", total_gpu_time_ms);
		
		for (auto& name : this->queries_used_this_frame) {
		// for (auto& timer_q_index : this->gl_timer_query_indices) {
			// const std::string& name = timer_q_index.second;
			if (this->gl_timer_query_cached_values.contains(std::string(&name[0]))) {
				// const std::string& name = q.first;
				auto q = this->gl_timer_query_cached_values[&name[0]];
				double delta_time = q.delta_time_display;
				double percentage_time = q.percentage_total;

				
				DrawColoredRectangle(percentage_time, ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0);
				// ImGui::Text("Query ");
				// ImGui::SameLine();
				ImGui::Text("%fms. %d %%", delta_time, int(percentage_time * 100));
				ImGui::SameLine();
				ImGui::Text(&name[0]);
				// ImGui::SameLine();
				// ImGui::Text(": ");
			}
		}


		draw_title("CPU TIMES");
		
		ImGui::Text("CPU: %f ms", (float)this->cpu_time_curr_frame);

		for(int cpu_timer_query_idx = 0; cpu_timer_query_idx < this->curr_cpu_timer_idx; cpu_timer_query_idx++){
			// auto startko
			CPUTimer& timer = this->cpu_timers[cpu_timer_query_idx];
			long long nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(timer.end_time - timer.start_time).count();
			double miliseconds = nanoseconds / double(1000000);
			
			ImGui::Text("%fms.", miliseconds);
			ImGui::SameLine();
			ImGui::Text(&timer.name[0]);
			// WEngine->editor->gui_dbg_print("time elapsed", (float)miliseconds);
		}
	};
	auto draw_total_ms_time_history = [&]() {
		ZoneScopedN("IMGUI MS TIME SPENT");
		// draw frametime history
		float min_frametime = FLT_MAX;
		float max_frametime = FLT_MIN;
		for (auto v : this->frame_times) {
			min_frametime = glm::min(v, min_frametime);
			max_frametime = glm::max(v, max_frametime);
		}
		min_frametime *= 1000.0;
		max_frametime *= 1000.0;

		static SIMDString<> frametime_text;
		sprintf(
			(char*)frametime_text.c_str(),
			"min %d max %d curr %d",
			(int)min_frametime,
			(int)max_frametime,
			(int)(WEngine->delta_time * 1000.0f)
		);
		// frametime_text =
		// static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
		ImGui::PlotLines(
			"Frame Times",
			&this->frame_times[0],
			this->frame_times.size(),
			0,
			frametime_text.c_str(),
			FLT_MIN,
			FLT_MAX,
			ImVec2(0, 40.0f)
		);
		// ImGui::PlotHistogram(
		// 	"Histogram",
		// 	&this->frame_times[0],
		// 	this->frame_times.size(),
		// 	0,
		// 	frametime_text.c_str(),
		// 	0.0f,
		// 	1.0f,
		// 	ImVec2(0, 80.0f)
		// );
	};
	auto draw_cpu_mem = [&]() {
		ZoneScopedN("IMGUI CPU MEM");
		// this->gui_dbg_print("CPU time", (float)this->cpu_time_curr_frame);

		static HANDLE proc_handle = GetCurrentProcess();
		// proc_handle = GetCurrentProcess();
		PROCESS_MEMORY_COUNTERS_EX pmc;
		GetProcessMemoryInfo(proc_handle, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
		SIZE_T virtualMemUsed = pmc.PrivateUsage;
		SIZE_T physicalMemUsed = pmc.WorkingSetSize;
		float virtualMemUsedMB = static_cast<float>(virtualMemUsed) / (1024 * 1024);
		float physicalMemUsedMB = static_cast<float>(physicalMemUsed) / (1024 * 1024);

		// ImGui::Text("MEM virt: %f mb", virtualMemUsedMB);
		// ImGui::Text("MEM phys: %f mb", physicalMemUsedMB);

		#define GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX 0x9048
		#define GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX 0x9049

		// GLint total_mem_kb = 0;
		// glGetIntegerv(GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX, &total_mem_kb);
		//
		// GLint cur_avail_mem_kb = 0;
		// glGetIntegerv(GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX, &cur_avail_mem_kb);

		// ImGui::Text("GPU_MEM: %d/%d mb", (total_mem_kb - cur_avail_mem_kb)/1024, (total_mem_kb)/1024 );
		
		draw_title("MEM");

		auto get_tex_sz_mb = [&](Texture* tex) -> float {
				int tex_sz_bytes = (tex->resx * tex->resy * tex->resz) * tex->bytes_per_comp * tex->components;
				int tex_sz_kb = tex_sz_bytes / 1024;
				float tex_sz_mb = float(tex_sz_kb) / 1024;
				return tex_sz_mb;
				// total_tex_mem_mb += tex_sz_mb;
		};
		float total_tex_mem_mb = 0.0f;
		for(int i = 0; i <= WEngine->alloc_textures.max_slot; i++) {
			if(WEngine->alloc_textures.slot_active[i]) {
				Texture& tex = WEngine->alloc_textures.arr[i];
				float tex_sz_mb = get_tex_sz_mb(&tex);
				total_tex_mem_mb += tex_sz_mb;
			}
		}
		ImGui::Text("MEM tex: %.3fmb", total_tex_mem_mb);
		// for(int i = 0; i < )
		
		float total_tex_pool_mem_mb = 0.0f;
		for(TexturePool::TempTex& tex : WEngine->texture_pool->tex_pool) {
			float tex_sz_mb = get_tex_sz_mb(tex.tex.ptr);
			total_tex_pool_mem_mb += tex_sz_mb;
		}
		ImGui::Text("MEM texpool: %.3fmb", total_tex_pool_mem_mb);
		

		float total_buff_mem_mb = 0.0f;

		for(Buffer* buff : WEngine->buffers) {
			const float buff_len_mb = float(buff->byte_len) / 1024.0f / 1024.0f;
			// if(buff->name.size() > 0) {
			// 	ImGui::Text("Buff, %s", buff->name);
			// 	ImGui::Text("MEM buff: %.3fmb", buff_len_mb);
			// }
			total_buff_mem_mb += buff_len_mb;
		}

		ImGui::Text("MEM buff: %.3fmb", total_buff_mem_mb);
		ImGui::Text("MEM uploaded this frame: %.3fmb", double(WEngine->bytes_upload_this_frame) / (1024.0 * 1024.0));
		ImGui::Text("FBs: %d Textures: %d", WEngine->framebuffers.size(), WEngine->alloc_textures.alloc_cnt);
		ImGui::Text("En titties: %d Lights: %d", WEngine->alloc_game_object.alloc_cnt, WEngine->alloc_lights.alloc_cnt);
		

		auto body_stats = WEngine->physics.physics_system->GetBodyStats();
		ImGui::Text("Physics bodies cnt: %d", body_stats.mNumBodies);


	};
	auto draw_debug_print = [&]() {
		ZoneScopedN("IMGUI DEBUG PRINT");
		int curr_back_buff_idx = (WEngine->frame + 3) % 10;
		int print_buffer_idx = 0;
		// static int im_offs = 0;
		// ImGui::SliderInt("STD430 OFFS", &im_offs, -100, 100);
		print_buffer_idx += curr_back_buff_idx * (1 + 150 * (16 + 1) + 0);


		// if(this->mapped_buff)
		if (this->printf_buffer->mapped_buff == nullptr) {
			// this->printf_buffer->mapped_buff = glMapNamedBufferRange(this->printf_buffer->pid, 0, this->printf_buffer->byte_len, 0);
			this->printf_buffer->mapped_buff = glMapNamedBufferRange(this->printf_buffer->pid, 0,
				this->printf_buffer->byte_len, GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT);
		}

		float* float_print_buffer_ptr = (float*)this->printf_buffer->mapped_buff;
		uint32_t total_vals_cnt = std::bit_cast<uint32_t>(float_print_buffer_ptr[print_buffer_idx++]);
		
		draw_title("SHADER PRINT()");

		ImGui::Text("Dbg vals cnt: %d", (int)total_vals_cnt);
		ImGui::Text("---------");

		enum PrintType {
			Float  = 0,
			Float2 = 1,
			Float3 = 2,
			Float4 = 3,
			Mat4   = 4,
			Mat3   = 5,
			Mat2   = 6,
			Uint   = 7,
			Int    = 8,
			Uint2  = 9,
			Uint3  = 10,
			Uint4  = 11,
			Int2   = 12,
			Int3   = 13,
			Int4   = 14,
		};
		// for(int i = 0; i < std::min(total_vals_cnt,40); i++)
		for (int i = 0; i < glm::min((int)total_vals_cnt, 150); i++) {
			uint32_t type = std::bit_cast<uint32_t>(float_print_buffer_ptr[print_buffer_idx++]);
			uint32_t temp_idx = print_buffer_idx;

			// int print_type
			if (type == PrintType::Float) {
				float float_v = float_print_buffer_ptr[temp_idx++];
				ImGui::Text(" f: %f", float_v);
			} else if (type == PrintType::Float2) {
				float float_v = float_print_buffer_ptr[temp_idx++];
				float float_v_b = float_print_buffer_ptr[temp_idx++];
				ImGui::Text(": vec2(%f, %f)", float_v, float_v_b);
			} else if (type == PrintType::Float3) {
				float float_v = float_print_buffer_ptr[temp_idx++];
				float float_v_b = float_print_buffer_ptr[temp_idx++];
				float float_v_c = float_print_buffer_ptr[temp_idx++];
				ImGui::Text(": vec3(%f, %f, %f)", float_v, float_v_b, float_v_c);
			} else if (type == PrintType::Float4) {
				float float_v = float_print_buffer_ptr[temp_idx++];
				float float_v_b = float_print_buffer_ptr[temp_idx++];
				float float_v_c = float_print_buffer_ptr[temp_idx++];
				float float_v_d = float_print_buffer_ptr[temp_idx++];
				ImGui::Text(": vec4(%f, %f, %f, %f)", float_v, float_v_b, float_v_c, float_v_d);
			} else if (type == PrintType::Mat4) {
				ImGui::Text(": ");
				ImGui::SameLine();
				glm::mat4 m;
				for (int k = 0; k < 16; k++) {
					float float_v = float_print_buffer_ptr[temp_idx++];
					(&m[0][0])[k] = float_v;
				}
				display_m4(m);
			} else if (type == PrintType::Mat3) {
				ImGui::Text(": mat3( ");
				for (int k = 0; k < 9; k++) {
					if (k % 3 != 0) {
						ImGui::SameLine();
					} else {
						ImGui::Text("   ");
						ImGui::SameLine();
					}
					float float_v = float_print_buffer_ptr[temp_idx++];
					ImGui::Text("%f, ", float_v);
				}
			} else if (type == PrintType::Mat2) {
				ImGui::Text(": mat2( ");
				for (int k = 0; k < 4; k++) {
					if (k % 2 != 0) {
						ImGui::SameLine();
					} else {
						ImGui::Text("   ");
						ImGui::SameLine();
					}
					float float_v = float_print_buffer_ptr[temp_idx++];
					ImGui::Text("%f, ", float_v);
				}
				ImGui::Text(")");
			} else if (type == PrintType::Uint) {
				uint32_t uint_v = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(" uint: %u", uint_v);
			} else if (type == PrintType::Int) {
				float float_v = float_print_buffer_ptr[temp_idx++];
				int32_t int_v = std::bit_cast<uint32_t>(float_v);
				ImGui::Text(" int: %d", int_v);
			} else if (type == PrintType::Uint2) {
				uint32_t uint_v = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				uint32_t uint_v_b = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(": uvec2(%u, %u)", uint_v, uint_v_b);
			} else if (type == PrintType::Uint3) {
				uint32_t uint_v = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				uint32_t uint_v_b = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				uint32_t uint_v_c = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(": uvec3(%u, %u, %u)", uint_v, uint_v_b, uint_v_c);
			} else if (type == PrintType::Uint4) {
				uint32_t int_v = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				uint32_t int_v_b = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				uint32_t int_v_c = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				uint32_t int_v_d = std::bit_cast<uint32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(": uvec4(%u, %u, %u, %u)", int_v, int_v_b, int_v_c, int_v_d);
			} else if (type == PrintType::Int2) {
				int32_t int_v = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				int32_t int_v_b = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(": ivec2(%d, %d)", int_v, int_v_b);
			} else if (type == PrintType::Int3) {
				int32_t int_v = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				int32_t int_v_b = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				int32_t int_v_c = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(": ivec3(%d, %d, %d)", int_v, int_v_b, int_v_c);
			} else if (type == PrintType::Int4) {
				int32_t int_v = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				int32_t int_v_b = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				int32_t int_v_c = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				int32_t int_v_d = std::bit_cast<int32_t>(float_print_buffer_ptr[temp_idx++]);
				ImGui::Text(": ivec4(%d, %d, %d, %d)", int_v, int_v_b, int_v_c, int_v_d);
			}
			print_buffer_idx += 16;
		}

	};
	auto display_textures_and_fbs = [&]() {
		ZoneScopedN("IMGUI TEXTURES AND FBS");
		static bool textures_collapsed = false;
		auto display_tex = [&](Texture* tex, bool is_fb, bool selected = true) {
			
			ImVec2 im_win_sz = ImGui::GetWindowSize();
			uint32_t texRes[2] = {tex->resx, tex->resy};
			if (!is_fb) {
				if (tex->name.length() > 0) {
					ImGui::Text(tex->name.c_str());
				} else {
					ImGui::Text("Nameless tex");
				}
			}

			int tex_sz_bytes = (tex->resx * tex->resy * tex->resz) * tex->bytes_per_comp * tex->components;
			int tex_sz_kb = tex_sz_bytes / 1024;
			float tex_sz_mb = float(tex_sz_kb) / 1024;

			if(tex->resz == 1) {
				ImGui::Text("%dx%d | %.3fmb | %s",
					tex->resx,
					tex->resy,
					tex_sz_mb,
					tex->internal_format_name.c_str()
				);
			} else {
				ImGui::Text("%dx%dx%d | %.3fmb | %s",
					tex->resx,
					tex->resy,
					tex->resz,
					tex_sz_mb,
					tex->internal_format_name.c_str()
				);

			}

			if(textures_collapsed && !selected) {
				return;
			} 
			if(tex->resz == 1) {
				uint32_t newTexRes[2] = {tex->resx, tex->resy};
				int maxSz = im_win_sz.x;
				if (texRes[0] > maxSz) {
					float ratioImWinxTexSzX = float(texRes[0]) / float(maxSz);
					newTexRes[0] = uint32_t(float(newTexRes[0]) / ratioImWinxTexSzX);
					newTexRes[1] = uint32_t(float(newTexRes[1]) / ratioImWinxTexSzX);
				}
				ImVec2 sz = ImVec2(float(newTexRes[0]), float(newTexRes[1]));
				float tex_res_limit = 300;
				if(newTexRes[1] > tex_res_limit) {
					sz.x /= newTexRes[1]/float(tex_res_limit);
					sz.y /= newTexRes[1]/float(tex_res_limit);
				}
				ImVec2 uv = ImVec2(0, 1);
				ImVec2 uv1 = ImVec2(1, 0);
				// ImGui::Image((void*)(intptr_t)tex->pid, sz, uv, uv1);
				ImGui::Image((void*)(intptr_t)tex->imgui_texview_pid, sz, uv, uv1);
				// ImGui::Button("R");

				bool all_channels_selected = glm::all(glm::equal(tex->imgui_debug_swizzle, glm::bvec4(1)));
				glm::bvec4 swizz = tex->imgui_debug_swizzle;
				if(all_channels_selected) {
					swizz = glm::bvec4(0);
				}

				for(int i = 0; i < 5; i++) {
					const char* text = "R";
					if(i < 4 && i >= tex->components) {
						continue;
					}
				
					if(i == 1) {
						text = "G";
					} else if (i == 2) {
						text = "B";
					} else if (i == 3) {
						text = "A";
					} else if (i == 4) {
						text = "All";
					}
					ImVec4 selected_col = ImVec4(1,1,1,0.5);
					ImVec4 unselected_col = ImVec4(0.2,0.2,0.2,0.01);

					ImVec4 col;

					if(i < 4) {
						col = all_channels_selected ? unselected_col : (swizz[i] ? selected_col : unselected_col);
					} else {
						col = all_channels_selected ? selected_col : unselected_col;
					}

					// ImGui::PushStyleCol();
					ImGui::PushStyleColor(ImGuiCol_Button, col);
					// if(ImGui::ColorButton(text, col, ImGuiColorEditFlags)){
					// if(ImGui::ButtonEx(text, ImVec2(0,0), ImGuiButtonFlags_PressedOnDefault_)){
					ImGui::PushID(125 + tex->pid);
					if(ImGui::Button(text)){
						if(i < 4) {
							tex->imgui_debug_swizzle = glm::bvec4(false);
							tex->imgui_debug_swizzle[i] = true;
							
							auto chan = i == 0 ? GL_RED : i == 1 ? GL_GREEN : i == 2 ? GL_BLUE : i == 3 ? GL_ALPHA : 0;
							
							// GLint const Swizzle[] = { chan, chan, chan, GL_ONE };
							GLint Swizzle[] = { chan, chan, chan, GL_ONE };

							if(i<3) {
								// Swizzle = { GL_ZERO, GL_ZERO, GL_ZERO, GL_ONE };
								Swizzle[0] = GL_ZERO;
								Swizzle[1] = GL_ZERO;
								Swizzle[2] = GL_ZERO;
								Swizzle[i] = chan;
							} else {
								Swizzle[0] = GL_ALPHA;
								Swizzle[1] = GL_ALPHA;
								Swizzle[2] = GL_ALPHA;
							}
							
							glTextureParameteriv(
								tex->imgui_texview_pid, GL_TEXTURE_SWIZZLE_RGBA, Swizzle
							);
						} else {
							tex->imgui_debug_swizzle = glm::bvec4(true);
							
							GLint const Swizzle[] = {
								GL_RED,
								GL_GREEN,
								GL_BLUE,
								GL_ONE
							};
							
							glTextureParameteriv(
								tex->imgui_texview_pid, GL_TEXTURE_SWIZZLE_RGBA, Swizzle
							);
							
						}
					}
					ImGui::PopID();
					
					// ImGui::PopStyleColor(ImGuiCol_Button);
					ImGui::PopStyleColor();
					// if(ImGui::RadioButton(text, swizz[i])) {
					// if(ImGui::ColorButton(text, ImVec4(1,1,1,1), swizz[i])) {
					// if()
					// 	
					// }
					if(i != 4) {
						ImGui::SameLine();
					}
					// ;
					// ImGui::SameLine();
					
				}
				
			}
			
			ImGui::Text(" ");
			//                        val texRatio = tex.res.x/tex.res.y
		};

		ImGui::Begin("FB Textures");
		
		//if(ImGui::CheckBox())
		
		ImGui::Checkbox("Collapse", &textures_collapsed);

		static int selected_fb_texture = -1;
		int i = 0;
		for (Framebuffer* fb : WEngine->framebuffers) {

			ImVec2 initialPos = ImGui::GetCursorPos();
			// ImGui::GetCursorStartPos();
			if (fb->name.length() > 0) {
				ImGui::Text(fb->name.c_str());
			} else {
				ImGui::Text("Nameless FB");
			}
			for (Texture* tex : fb->textures) {
				display_tex(tex, true, selected_fb_texture == i);

				ImVec2 finalPos = ImGui::GetCursorPos();
				auto scroll = ImGui::GetScrollY();
				// Calculate the rectangle dimensions
				ImVec2 windowPos = ImGui::GetWindowPos();
				ImVec2 windowSize = ImGui::GetWindowSize();
				ImVec2 rectMin = ImVec2(windowPos.x + initialPos.x, windowPos.y + initialPos.y);

				ImVec2 rectMax = ImVec2(windowPos.x + windowSize.x, windowPos.y + finalPos.y);

				rectMin.y -= scroll;
				rectMax.y -= scroll;

				// Check if the mouse is inside the rectangle
				ImVec2 mousePos = ImGui::GetMousePos();
				
				if(selected_fb_texture != i) {
					if (ImGui::IsMouseHoveringRect(rectMin, rectMax)) {
						if(ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
							selected_fb_texture = i;
						}
						// Draw a semi-transparent rectangle
						ImU32 color = IM_COL32(255, 255, 255, 100); // RGBA
						ImGui::GetWindowDrawList()->AddRectFilled(rectMin, rectMax, color);
					}
				}
			}
			if (fb->depth_texture) {
				ImGui::Text("Depth tex");
				display_tex(fb->depth_texture, true, selected_fb_texture == i);
				i++;
			}

			i++;
			
			ImGui::Text("___________________________");
			// ImGui::Text("_____");
			// ImGui::Text("_____");
			// ImGui::Text("_____");
		}
		ImGui::End();

		ImGui::Begin("Non-FB Textures");
		
		ImGui::Checkbox("Collapse", &textures_collapsed);

		for (int tex_idx = 0; tex_idx <= WEngine->alloc_textures.max_slot; tex_idx++) {
			if (WEngine->alloc_textures.slot_active[tex_idx]) {
				ImVec2 initialPos = ImGui::GetCursorPos();
				Texture* tex = &WEngine->alloc_textures.arr[tex_idx];
				if (tex->bound_fb == nullptr) {
					display_tex(tex, false, selected_fb_texture == i);
					

					ImVec2 finalPos = ImGui::GetCursorPos();
					auto scroll = ImGui::GetScrollY();
					// Calculate the rectangle dimensions
					ImVec2 windowPos = ImGui::GetWindowPos();
					ImVec2 windowSize = ImGui::GetWindowSize();
					ImVec2 rectMin = ImVec2(windowPos.x + initialPos.x, windowPos.y + initialPos.y);

					ImVec2 rectMax = ImVec2(windowPos.x + windowSize.x, windowPos.y + finalPos.y);

					rectMin.y -= scroll;
					rectMax.y -= scroll;

					// Check if the mouse is inside the rectangle
					ImVec2 mousePos = ImGui::GetMousePos();
					
					if(selected_fb_texture != i) {
						if (ImGui::IsMouseHoveringRect(rectMin, rectMax)) {
							if(ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
								selected_fb_texture = i;
							}
							// Draw a semi-transparent rectangle
							ImU32 color = IM_COL32(255, 255, 255, 100); // RGBA
							ImGui::GetWindowDrawList()->AddRectFilled(rectMin, rectMax, color);
						}
					}
					
					ImGui::Text("___________________________");
					// ImGui::Text("_____");
					// ImGui::Text("_____");
					// ImGui::Text("_____");
					// ImGui::Text("_____");
				}
			}
			i++;
		}

		ImGui::End();
		
		ImGui::Begin("Buffers");
		
		{
			draw_title("SSBO Bindings");
			int ssbo_idx = 0;
			for(Buffer* buff : WEngine->wgl->ssbo_bindings) {
				if(buff != nullptr){
					ImGui::Text("SSBO %i: ", ssbo_idx);
					ImGui::SameLine();
					ImGui::Text(&buff->name[0]);
				}
				ssbo_idx++;
			}
		
		}
		
		for(Buffer* buff : WEngine->buffers) {
			const float buff_len_mb = float(buff->byte_len) / 1024.0f / 1024.0f;
			// if(buff->name.size() > 0) {
			if(true) {
				// draw_title("GPU TIMES");
				draw_title("%s", &buff->name[0]);
				// ImGui::Text("-- %s -- ", &buff->name[0]);
				ImGui::Text("MEM buff: %.3fmb", buff_len_mb);
			}
		}
		ImGui::End();

	};
	
	auto display_shaders = [&](){
		ImGui::Begin("Shaders");
		
		static std::string shader_validation_errors;
		static std::vector<Shader*> shaders_with_validation_errors;
		
		
		if(ImGui::Button("Validate shaders")){
			for(Shader* shader : WEngine->shader_manager->shaders){
				std::string shader_code = shader->code;
				std::string file_path = "shader.glsl";
				std::ofstream shader_file(file_path);
				if (shader_file.is_open()) {
						shader_file << shader_code;
						shader_file.close();
				}
				
				wlog_info("VERIFYING SHADER: {}", shader->path);

				std::string command = "\"glslangValidator.exe\" "
															"-e main --auto-map-bindings --auto-map-locations "
															"--glsl-version 460 --no-link -S ";
				std::string shader_type_string;
				if(shader->shader_type == ShaderType::Fragment){
					shader_type_string = "frag";
				} else if(shader->shader_type == ShaderType::Compute){
					shader_type_string = "comp";
				} else if(shader->shader_type == ShaderType::Geometry){
					shader_type_string = "geom";
				} else if(shader->shader_type == ShaderType::Vertex){
					shader_type_string = "vert";
				}
				command += shader_type_string + " ";
				command += "\"" + file_path + "\"";
				
				{

					std::string cmd_line = command;

					SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };
					sei.lpFile = "cmd.exe";
					sei.lpParameters = (command).c_str();
					sei.nShow = SW_HIDE;  // Use SW_SHOW to display the command prompt

					SECURITY_ATTRIBUTES saAttr;
					saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
					saAttr.bInheritHandle = TRUE;
					saAttr.lpSecurityDescriptor = nullptr;
				
					HANDLE hReadPipe, hWritePipe;

					if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0)) {
						wlog_err("Error creating pipe.");
					}
				
					STARTUPINFO si = { sizeof(STARTUPINFO) };
					si.dwFlags = STARTF_USESTDHANDLES;
					si.hStdOutput = hWritePipe;

					PROCESS_INFORMATION pi;

					if (CreateProcess(nullptr, const_cast<LPSTR>(command.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)) {
						CloseHandle(hWritePipe);

						constexpr int MAX_BUFF_SZ = 4096 * 40;
						char buffer[MAX_BUFF_SZ];
						DWORD bytesRead;
						std::string data;
						while (ReadFile(hReadPipe, buffer, sizeof(buffer), &bytesRead, nullptr) && bytesRead > 0) {
							for(int i = 0; i < bytesRead; i++){
								data += buffer[i];
							}
							// std::cout.write(buffer, bytesRead);
						}

						size_t g = data.find("ERROR");
						if(g != std::string::npos){
							shader_validation_errors += "------ SHADER ";
							shader_validation_errors += shader->path;
							shader_validation_errors += " ------ \n";
							shader_validation_errors += data.c_str();
							shaders_with_validation_errors.push_back(shader);
						}

						WaitForSingleObject(pi.hProcess, INFINITE);
						CloseHandle(pi.hProcess);
						CloseHandle(pi.hThread);
					} else {
						std::cerr << "Error creating process." << std::endl;
					}
				}

			}
			
			for(Shader* shader : shaders_with_validation_errors){
				wlog_err("SHADER WITH VALIDATION ERROR: {}", shader->path);
			}
		}

		draw_title("Shader Programs");
		for(ShaderProgram* shader_program : WEngine->shader_manager->shader_programs) {
			std::string text;
			if(shader_program->vert_shader && shader_program->frag_shader){
				text += "Prog vert: ";
				text += shader_program->vert_shader->path;
				text += " frag: ";
				text += shader_program->frag_shader->path;
				ImGui::Text(text.c_str());
			}
			if(shader_program->comp_shader){
				text += "Prog comp: ";
				text += shader_program->comp_shader->path;
				ImGui::Text(text.c_str());
			}
		}
		if(shader_validation_errors.size() != 0){
			draw_title("Shader erorrs");
			ImGui::Text(shader_validation_errors.c_str());
		}
		
		draw_title("Shaders");
		for(Shader* shader : WEngine->shader_manager->shaders) {
			std::string shader_type_string;
			if(shader->shader_type == ShaderType::Fragment){
				shader_type_string = "frag";
			} else if(shader->shader_type == ShaderType::Compute){
				shader_type_string = "comp";
			} else if(shader->shader_type == ShaderType::Geometry){
				shader_type_string = "geom";
			} else if(shader->shader_type == ShaderType::Vertex){
				shader_type_string = "vert";
			}
			std::string shader_str = shader_type_string + " ";
			shader_str += shader->path;
			
			ImGui::Text(shader_str.c_str());
			if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
				ImGui::SetTooltip(shader->code.c_str());
			for(SIMDString<>& include_path : shader->shader_includes){
				ImGui::Text("     incl: ");
				ImGui::SameLine();
				ImGui::Text(include_path.c_str());
			}
		}
		
		
		
	
		ImGui::End();
	};

	auto draw_demo_stuff = [&]() {
		ZoneScopedN("IMGUI DEMO STUFF");
		ImGuiViewport* main_viewport = ImGui::GetMainViewport();

		// ImGui::SetNextWindowPos(ImVec2(0,WEngine->RESY - 30), ImGuiCond_Always);
		ImGui::SetNextWindowPos(ImVec2(0,WEngine->RESY - 70), ImGuiCond_Always);
		ImGui::SetNextWindowSize(ImVec2(WEngine->RESX, 0));
		ImGui::SetNextWindowSizeConstraints(
			ImVec2(0, 0),
			ImVec2(WEngine->RESX, WEngine->RESY)
		);
		// ImGui::SetNextWindowPos(ImVec2(0, 0.));

		bool open = true;
		ImGui::Begin(" aa", &open,
			ImGuiWindowFlags_NoCollapse |
				ImGuiWindowFlags_NoTitleBar |
				ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
			);
		// ImGui::SetCursorPos(ImVec2(0.8, 0));
		
		// ImGui::SetWindowWi

		float t = WEngine->time;
		// ImGui::Sli

		ImGui::SetNextItemWidth(WEngine->RESX);
		if(ImGui::SliderFloat("##Slider", &t, 0.0f, 300.0f, "%.3f" )) {
			WEngine->time = t;
		}
		// ImVec2(WEngine->RESX, 0.0f)
		// ImGui::DragFloat("t", &t,1,0,300,);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);
		// ImGui::DragFloat("t", &t);

		ImGui::End();
	};
	
	//  ------------------------------ CONTROL FLOW-------------------------- //
	//  ------------------------------ CONTROL FLOW-------------------------- //
	//  ------------------------------ CONTROL FLOW-------------------------- //

	this->display_imgui_shader_errors();

	// if (this->entitties_debugger_enabled) {
	if (!this->imgui_menu_collapsed) {
		ZoneScopedN("IMGUI ENTITIES");
		ImGui::Begin("En titties");
		bool b_true = true;
		bool b_false = false;

		auto display_selected_entity = [&](WEntity* entitty) {
			b_true = true;
			b_false = false;

			ImGui::Text("ID: %d", entitty->game_obj_id);
			// bool
			ImGui::Checkbox("Physics enabled", entitty->physics_active ? &b_true : &b_false);
			// ImGui::Text("ID: %d", entitty->game_obj_id);

			// ImGui::Text("JPH ID: %");
			// ImGui::SameLine();
			// display_quat(entitty->jph_body_id);

			if (ImGui::DragFloat3("Position", &entitty->position[0], slider_speed)) {
				entitty->set_pos(entitty->position);
			}
			glm::vec3 vel_temp = entitty->get_vel();
			if (ImGui::DragFloat3("Velocity", &vel_temp[0], slider_speed)) {
				entitty->set_vel(vel_temp);
			}
			if (ImGui::DragFloat4("Rotation", &entitty->rotation[0], slider_speed)) {
				entitty->set_rot(entitty->rotation);
			}

			if (ImGui::DragFloat3("Scale", &entitty->scale[0], slider_speed)) {
				entitty->set_scale(entitty->scale);
			}


			if (entitty->col_sound.has_value()) {
				ImGui::Text("Has collision sound");
			}

			if (entitty->model_node != nullptr) {
				ImGui::Text("Has model node");
			}


			ImGui::NewLine();
			ImGui::NewLine();
		};

		if (this->selected_entity.has_value()) {
			if (!this->selected_entity_is_light) {
				display_selected_entity((WEntity*)this->selected_entity.value());
			} else {
				display_selected_light((Light*)this->selected_entity.value());
			}

		}

		for (std::pair<const long, WEntity*> game_obj : WEngine->entitties) {
			bool entity_selected = false;
			if (selected_entity.has_value() && !this->selected_entity_is_light &&
				WObj((WEntity*)this->selected_entity.value()) == WObj(game_obj.second)
			) {
				entity_selected = true;
			}

			SIMDString<> name = "";
			name += "En titty ";
			name += std::to_string(game_obj.first);

			if (game_obj.second->model_node) {
				name += " [";
				name += game_obj.second->model_node->name.c_str();
				name += "]";
			}

			b_true = true;
			b_false = false;

			auto cb_selected = [&](bool do_select) {
				if (do_select) {
					this->selected_entity = game_obj.second;
					this->selected_entity_is_light = false;
				} else {
					this->selected_entity = std::nullopt;
				}
			};

			if (entity_selected) {
				if (ImGui::Checkbox(name.c_str(), &b_true)) {
					cb_selected(false);
				}
			} else {
				if (ImGui::Checkbox(name.c_str(), &b_false)) {
					cb_selected(true);
				}
			}


			if (entity_selected) {}
		}

		for (int i = 0; i < WEngine->alloc_lights.max_slot; i++) {
			if (WEngine->alloc_lights.slot_active[i]) {
				Light* light = &WEngine->alloc_lights.arr[i];

				// SIMDString<> name = "Light";
				SIMDString<> name = "";
				if (light->type == LightType::POINT) {
					name += "Point light ";
					name += std::to_string(i);
				} else if (light->type == LightType::DIRECTIONAL) {
					name += "Directional light ";
					name += std::to_string(i);
				}

				b_true = true;
				b_false = false;


				if (this->selected_light.has_value() && this->selected_light.value() == WObj(light)) {
					if (ImGui::Checkbox(name.c_str(), &b_true)) {
						this->selected_light = std::nullopt;
					}
					display_selected_light(light);
					// ImGui::SliderFloat3("Pos");

				} else {
					if (ImGui::Checkbox(name.c_str(), &b_false)) {
						this->selected_light = WObj(light);
					}
				}


			}

		}

		ImGui::End();
	}
	if (!this->imgui_menu_collapsed) {
		ZoneScopedN("IMGUI SEQUENCER");
		// --------- demo stuff ------------- //
		draw_demo_stuff();

		auto draw_timeline = [&](){
			static bool sequencer_collapsed = true;
				
			if (
				this->timeline.lanes.size() > 0 
				// && ImGui::CollapsingHeader("Sequencer", &sequencer_collapsed)
			) {
				ZoneScopedN("IMGUI ENTITIES");
				ImGui::Begin("Timeline");
				float yOffset = -10.0f; // Change this to whatever offset you want
				ImVec2 cursorPos = ImGui::GetCursorPos();
				cursorPos.y += yOffset;
				ImGui::SetCursorPos(cursorPos);
				// let's create the sequencer
				static int selected_lane = -1;
				static int firstFrame = 0;
				static bool expanded = true;
				static int currentFrame = 100;

				// ImGui::PushItemWidth(130);
				// ImGui::InputInt("Frame Min", &timeline.mFrameMin);
				// // ImGui::SameLine();
				// ImGui::InputInt("Frame ", &currentFrame);
				// // ImGui::SameLine();
				// ImGui::InputInt("Frame Max", &timeline.mFrameMax);
				// ImGui::PopItemWidth();
				this->timeline.selected_points.resize(0);
				DoSequencer(&timeline, &currentFrame, &expanded, &selected_lane, &firstFrame, ImSequencer::SEQUENCER_EDIT_STARTEND | ImSequencer::SEQUENCER_ADD | ImSequencer::SEQUENCER_DEL | ImSequencer::SEQUENCER_COPYPASTE | ImSequencer::SEQUENCER_CHANGE_FRAME);
				
				if(ImGui::Button("Save")) {
					this->timeline.serialize_to_json();
					WEngine->editor->gui_dbg_print_msg(std::string("Saved ") + this->timeline.json_path.string());
				}
				ImGui::SameLine();
				if(ImGui::Button("Load")) {
					this->timeline.deserialize_from_json();
					WEngine->editor->gui_dbg_print_msg(std::string("Loaded ") + this->timeline.json_path.string());
				}
				// adDraw(customDraw.index, draw_list, customDraw.customRect, customDraw.legendRect, customDraw.clippingRect, customDraw.legendClippingRect);
				ImGui::Text("selected curve: %d", selected_lane);
				
				ImGui::Text("curve pt len: %d", this->timeline.selected_points.size());
				for(ImCurveEdit::EditPoint& selelected_pt : this->timeline.selected_points) {
					ImVec2 curve_pt = this->timeline.rampEdit.curve_points[selelected_pt.curveIndex][selelected_pt.pointIndex].point;
					
					// ImGui::Text("curve pt idx: %d", selelected_pt.curveIndex);
					// ImGui::Text("curve pt: %f, %f", curve_pt.x, curve_pt.y);
					//
					// ImVec2 curve_val = timeline.rampEdit.GetCurvePointAtTime(selelected_pt.curveIndex, WEngine->time);
					// ImGui::Text("TIME PT: %f, %f", curve_val.x, curve_val.y);
				}
				
				if (selected_lane != -1) {
					const Lane &lane = timeline.lanes[selected_lane];
					// for(auto curve_pt : mySequence.rampEdit.curve_points[])
					const int curr_entry_pt_count = timeline.rampEdit.curve_point_counts[selected_lane];
					

					
					
					for(int curve_pt_idx = 0; curve_pt_idx < curr_entry_pt_count; curve_pt_idx++) {
						// const ImVec2* curve_pt = timeline.rampEdit.curve_points[selected_lane]->point;
						// ImGui::Text("curve pt: %f, %f", curve_pt->x, curve_pt->y);
						// mySequence.rampEdit.curve_points[0];
					}
					// mySequence.rampEdit.Get
					// ImGui::Text("I am a %s, please edit me", SequencerItemTypeNames[item.mType]);
					// switch (type) ....
				}
				
				ImGui::End();
			}
		};
		
		draw_timeline();
		
		// --------- gui controls ----------- //
		{
			for(LUT* lut : this->luts) {
					// ImGui::Text(&path[0]);
					static int currentItem = 0;
					if (ImGui::BeginCombo("Dropdown", lut->lut_paths[currentItem].c_str()))
					{
						for (int i = 0; i < lut->lut_paths.size(); i++)
						{
							bool isSelected = (currentItem == i);
							if (ImGui::Selectable(lut->lut_paths[i].c_str(), isSelected)) {
								currentItem = i;
								lut->load_lut(lut->lut_paths[currentItem].c_str());
							}

							if (isSelected)
								ImGui::SetItemDefaultFocus();
						}
						ImGui::EndCombo();
					}
					
			}
			display_gui_dbg_vals();
			// ImGui::DragFloat3("cam pos", &WEngine->camera->pos[0]);
			
			display_gui_controls();

			ImGui::End();
		}
		ImGui::ShowDemoWindow();


		// --------- gui stats ----------- //
		{
			ImGui::Begin("Stats");
			ImGui::SetCursorPos(ImVec2(0.8, 0));

			ImGui::Text(" ");
			

			// ImGui::Text(" ms/frame %.3f FPS %.1f", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
			ImGui::Text("FPS: %.1f  %.3f ms/f", ImGui::GetIO().Framerate, 1000.0f / ImGui::GetIO().Framerate);

			// draw_total_ms_time_history();
			draw_timer_queries();
			
			draw_cpu_mem();


			#ifdef DBG_PRINT
			draw_debug_print();
			#endif

			ImGui::SetWindowCollapsed(this->imgui_menu_collapsed);
			ImGui::End();
		}

		// --------- textures ----------- //
		{
			// ImVec2 im_win_sz = ImGui::GetWindowSize();
			// ImVec2 im_win_sz = ImGui::GetWindowSize();

			display_textures_and_fbs();


		}
		// --------- Shaders ----------- //
		{
			display_shaders();
		}

	}
	// bool truu = true;
	// ImGui::ShowDemoWindow(&truu);
	



	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(0, 0, WEngine->RESX, WEngine->RESY);

	ImGui::Render();

	// this->imgui_io = &ImGui::GetIO();
	// (void)this->imgui_io; // wot dose dis do?

	glViewport(0, 0, WEngine->RESX, WEngine->RESY);
	ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

	if (this->imgui_io->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
		ImGui::UpdatePlatformWindows();
		ImGui::RenderPlatformWindowsDefault();

		// Restore the OpenGL rendering context to the main window DC, since platform windows might have changed it.

		wglMakeCurrent(WEngine->wgl->hDC, WEngine->wgl->glContext);
	}
	#endif
}

GUIControl::GUIControl(std::string name, void* value_ptr, GUIControlArgs args)
	: name(name), value_ptr(value_ptr), type(args.type), range_min(args.range_min), range_max(args.range_max),
	precision(args.precision), callback(args.callback), is_colour(args.is_colour) {}



void Editor::display_imgui_shader_errors() {
	#ifdef EDITOR
	ShaderManager* shader_manager = WEngine->shader_manager;

	bool there_are_compilation_errors = false;
	for (Shader* shader : shader_manager->shaders) {
		if (!shader->compilation_success) {
			there_are_compilation_errors = true;
			break;
		}
	}
	for (ShaderProgram* shader_program : shader_manager->shader_programs) {
		if (!shader_program->link_success) {
			there_are_compilation_errors = true;
			break;
		}
	}

	if (this->times_since_notification_messages[notification_message_idx] < 2.0) {
		ImGui::SetNextWindowPos(ImVec2(0, 0.));
		ImGuiViewport* main_viewport = ImGui::GetMainViewport();
		ImGui::SetNextWindowPos(main_viewport->WorkPos, ImGuiCond_Always);
		ImGui::SetNextWindowSize(ImVec2(WEngine->RESX, 0));
		ImGui::SetNextWindowSizeConstraints(
			ImVec2(0, 0),
			ImVec2(WEngine->RESX, WEngine->RESY)
		);
		bool* open_ptr = (bool*)true;
		ImGui::Begin("0 ", open_ptr,
			ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize |
			ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove
		);
		for (int i = notification_message_idx; ;) {
			ImGui::Text(this->notification_messages[i].c_str());
			i = (i + max_notification_messages - 1) % max_notification_messages;
			if (i == notification_message_idx || this->times_since_notification_messages[i] >= 2.0) {
				break;
			}
		}

		// ImGui::Text("Shader Reloaded");
		ImGui::SetWindowCollapsed(true);
		ImGui::End();

	}
	if (there_are_compilation_errors) {
		ImGui::SetNextWindowPos(ImVec2(0, 0.));
		ImGuiViewport* main_viewport = ImGui::GetMainViewport();
		ImGui::SetNextWindowPos(main_viewport->WorkPos, ImGuiCond_Always);
		ImGui::SetNextWindowSize(ImVec2(WEngine->RESX, 0));
		ImGui::SetNextWindowSizeConstraints(
			ImVec2(0, 0),
			ImVec2(WEngine->RESX, WEngine->RESY)
		);

		bool* open_ptr = (bool*)true;
		ImGui::Begin("0 ", open_ptr,
			ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize |
			ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar
		);
		ImGui::SetWindowCollapsed(true);
		float red[3] = {1, 0, 0};
		ImGui::ColorEdit3(" ", red, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker);

		// for (ShaderProgram* shader_program : shader_manager->shader_programs) {
		// if (thereAreTextureErrors || thereAreShaderErrors || thereAreProgramErrors) {
		// 	ImGuiViewport* mainViewport = ImGui::GetMainViewport();
		// 	ImGui::SetN
		// 	// ImGui::SetNextWindowViewport(mainViewport);
		// 	ImGui::SetNextWindowPos(mainViewport.workPosX, mainViewport.workPosY, ImGuiCond.Always);

		for (Shader* shader : shader_manager->shaders) {
			if (!shader->compilation_success) {
				std::string title = "Error compiling shader ";
				title += shader->path;
				ImGui::Text(&title[0]);
				ImGui::Text(shader->error.c_str());
			}
		}
		for (ShaderProgram* shader_program : shader_manager->shader_programs) {
			if (!shader_program->link_success) {
				// std::string title = "Error linking program ";
				SIMDString title = "Error linking program ";
				if (shader_program->frag_shader) {
					title += shader_program->frag_shader->path;
					title += " ";
				}
				if (shader_program->vert_shader) {
					title += shader_program->vert_shader->path;
					title += " ";
				}
				if (shader_program->comp_shader) {
					title += shader_program->comp_shader->path;
					title += " ";
				}
				if (shader_program->geom_shader) {
					title += shader_program->geom_shader->path;
					title += " ";
				}
				ImGui::Text(&title[0]);
				ImGui::Text(shader_program->error);
			}
		}

		ImGui::SetWindowCollapsed(true);
		ImGui::End();
	}
	// }

	#endif
}

void Editor::display_debug_g_buffer() {
	glLineWidth(2.0f);
	if (!this->physics_debugger_enabled) {
		return;
	}
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	float* physics_drawer_ptr = (float*)this->physics_debug_drawer_buff->cpu_data;

	auto get_box = [](
		glm::vec3 bounds_min,
		glm::vec3 bounds_max
	) -> std::array<glm::vec3, 24> {
		std::array<glm::vec3, 24> ret;

		// Define the vertices of the cube
		glm::vec3 vertices[8] = {
			{bounds_min.x, bounds_min.y, bounds_min.z}, // 0
			{bounds_max.x, bounds_min.y, bounds_min.z}, // 1
			{bounds_max.x, bounds_max.y, bounds_min.z}, // 2
			{bounds_min.x, bounds_max.y, bounds_min.z}, // 3
			{bounds_min.x, bounds_min.y, bounds_max.z}, // 4
			{bounds_max.x, bounds_min.y, bounds_max.z}, // 5
			{bounds_max.x, bounds_max.y, bounds_max.z}, // 6
			{bounds_min.x, bounds_max.y, bounds_max.z}  // 7
		};

		// Define the indices of the cube edges
		const int edges[12][2] = {
			{0, 1}, {1, 2}, {2, 3}, {3, 0}, // Bottom face
			{4, 5}, {5, 6}, {6, 7}, {7, 4}, // Top face
			{0, 4}, {1, 5}, {2, 6}, {3, 7}  // Connecting edges
		};

		// Fill the ret array with lines
		for (int i = 0; i < 12; ++i) {
			ret[i * 2] = vertices[edges[i][0]];
			ret[i * 2 + 1] = vertices[edges[i][1]];
		}

		return ret;
	};

	auto get_circ = [](
		glm::vec3 pos,
		float rad
	) -> std::array<glm::vec3, 24> {
		std::array<glm::vec3, 24> ret;

		for (int i = 0; i < 24; ++i) {
			float idx = float(i) / 24.0f;
			ret[i] = glm::vec3(
				pos.x + sin(idx * tau) * rad,
				pos.y + cos(idx * tau) * rad,
				pos.z
			);
		}

		return ret;
	};

	auto get_line = [](
		glm::vec3 start_pos,
		glm::vec3 end_pos
	) -> std::array<glm::vec3, 2> {
		return {start_pos, end_pos};
	};

	auto get_star = [](
		glm::vec3 pos,
		float w
	) -> std::array<glm::vec3, 6> {
		std::array<glm::vec3, 6> ret;

		int i = 0;
		ret[i++] = pos + glm::vec3(w, 0, 0);
		ret[i++] = pos + glm::vec3(-w, 0, 0);

		ret[i++] = pos + glm::vec3(0, w, 0);
		ret[i++] = pos + glm::vec3(0, -w, 0);

		ret[i++] = pos + glm::vec3(0, 0, w);
		ret[i++] = pos + glm::vec3(0, 0, -w);

		// ret[i++] = pos + glm::vec3(0,w,w);
		// ret[i++] = pos + glm::vec3(-w,-w,0);
		//
		// ret[i++] = pos + glm::vec3(w,w,0);
		// ret[i++] = pos + glm::vec3(0,-w,-w);

		return ret;
	};

	auto push_verts = [&](auto& verts, int& lines_cnt, glm::vec4 col, bool obscure) {
		for (glm::vec3 vert : verts) {
			*physics_drawer_ptr++ = vert.x;
			*physics_drawer_ptr++ = vert.y;
			*physics_drawer_ptr++ = vert.z;
			*physics_drawer_ptr++ = float(obscure);
			*physics_drawer_ptr++ = col.x;
			*physics_drawer_ptr++ = col.y;
			*physics_drawer_ptr++ = col.z;
			*physics_drawer_ptr++ = col.w;
			lines_cnt++;
		}
	};
	int lines_cnt = 0;
	for (std::pair<const long, WEntity*> it : WEngine->entitties) {
		// static std::vector<>
		WEntity* game_object = it.second;
		if (game_object->physics_active) {
			auto bounds = game_object->jph_body->GetWorldSpaceBounds();
			auto box_verts = get_box(
				glm::vec3(bounds.mMin.GetX(), bounds.mMin.GetY(), bounds.mMin.GetZ()),
				glm::vec3(bounds.mMax.GetX(), bounds.mMax.GetY(), bounds.mMax.GetZ())
			);
			push_verts(box_verts, lines_cnt, glm::vec4(1), true);
		} else {
			const float box_sz = 0.01;
			glm::vec3 pos = game_object->get_pos();
			auto verts = get_star(pos, 0.5f);
			push_verts(verts, lines_cnt, glm::vec4(0, 1, 0, 1), false);
		}
	}

	for (int i = 0; i < WEngine->alloc_lights.max_slot; i++) {
		if (WEngine->alloc_lights.slot_active[i]) {
			Light& light = WEngine->alloc_lights.arr[i];
			auto verts = get_circ(light.pos, 0.1f);
			push_verts(verts, lines_cnt, glm::vec4(1, 0.2, 0, 1), true);
			// auto verts_line = get_circ(light.pos, 0.1f);
			// push_verts(verts,lines_cnt, glm::vec4(1,0.2,0,1), true);
		}

	}

	glNamedBufferSubData(
		this->physics_debug_drawer_buff->pid,
		0,
		(physics_drawer_ptr - (float*)this->physics_debug_drawer_buff->cpu_data) * sizeof(float),
		this->physics_debug_drawer_buff->cpu_data
	);
	prog_physics_dbg->use();
	prog_physics_dbg->setUniform("buff", this->physics_debug_drawer_buff);
	if (depth_texture) {
		prog_physics_dbg->setUniform("depth_tex", depth_texture);
	} else {
		prog_physics_dbg->setUniform("depth_tex", 0);
	}

	glDrawArrays(GL_LINES, 0, lines_cnt);

	glDisable(GL_BLEND);
}


void Editor::setImGuiTheme() {
	ImVec4* colors = ImGui::GetStyle().Colors;
	colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 0.95f);
	colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
	colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.12f, 0.12f, 1.00f);
	colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f);
	colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.94f);
	colors[ImGuiCol_Border] = ImVec4(0.53f, 0.53f, 0.53f, 0.46f);
	colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
	colors[ImGuiCol_FrameBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.85f);
	colors[ImGuiCol_FrameBgHovered] = ImVec4(0.22f, 0.22f, 0.22f, 0.40f);
	colors[ImGuiCol_FrameBgActive] = ImVec4(0.16f, 0.16f, 0.16f, 0.53f);
	colors[ImGuiCol_TitleBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
	colors[ImGuiCol_TitleBgActive] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
	colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
	colors[ImGuiCol_MenuBarBg] = ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
	colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
	colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
	colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
	colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.48f, 0.48f, 0.48f, 1.00f);
	colors[ImGuiCol_CheckMark] = ImVec4(0.79f, 0.79f, 0.79f, 1.00f);
	colors[ImGuiCol_SliderGrab] = ImVec4(0.48f, 0.47f, 0.47f, 0.91f);
	colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.55f, 0.55f, 0.62f);
	colors[ImGuiCol_Button] = ImVec4(0.50f, 0.50f, 0.50f, 0.63f);
	colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.67f, 0.68f, 0.63f);
	colors[ImGuiCol_ButtonActive] = ImVec4(0.4f, 0.26f, 0.26f, 0.63f);
	colors[ImGuiCol_Header] = ImVec4(0.54f, 0.54f, 0.54f, 0.58f);
	colors[ImGuiCol_HeaderHovered] = ImVec4(0.64f, 0.65f, 0.65f, 0.80f);
	colors[ImGuiCol_HeaderActive] = ImVec4(0.25f, 0.25f, 0.25f, 0.80f);
	colors[ImGuiCol_Separator] = ImVec4(0.58f, 0.58f, 0.58f, 0.50f);
	colors[ImGuiCol_SeparatorHovered] = ImVec4(0.81f, 0.81f, 0.81f, 0.64f);
	colors[ImGuiCol_SeparatorActive] = ImVec4(0.81f, 0.81f, 0.81f, 0.64f);
	colors[ImGuiCol_ResizeGrip] = ImVec4(0.87f, 0.87f, 0.87f, 0.53f);
	colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.87f, 0.87f, 0.87f, 0.74f);
	colors[ImGuiCol_ResizeGripActive] = ImVec4(0.87f, 0.87f, 0.87f, 0.74f);
	colors[ImGuiCol_Tab] = ImVec4(0.01f, 0.01f, 0.01f, 0.86f);
	colors[ImGuiCol_TabHovered] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f);
	colors[ImGuiCol_TabActive] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
	colors[ImGuiCol_TabUnfocused] = ImVec4(0.02f, 0.02f, 0.02f, 1.00f);
	colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f);
	//colors[ImGuiCol_DockingPreview] = ImVec4(0.38f, 0.48f, 0.60f, 1.00f);
	//colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
	colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
	colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.68f, 0.68f, 0.68f, 1.00f);
	colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.77f, 0.33f, 1.00f);
	colors[ImGuiCol_PlotHistogramHovered] = ImVec4(0.87f, 0.55f, 0.08f, 1.00f);
	colors[ImGuiCol_TextSelectedBg] = ImVec4(0.47f, 0.60f, 0.76f, 0.47f);
	colors[ImGuiCol_DragDropTarget] = ImVec4(0.58f, 0.58f, 0.58f, 0.90f);
	colors[ImGuiCol_NavHighlight] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
	colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
	colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
	colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
	colors[ImGuiCol_WindowBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.5f);
	colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.5f);
	colors[ImGuiCol_TitleBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.5f);
	colors[ImGuiCol_TitleBgActive] = ImVec4(0.05f, 0.05f, 0.05f, 0.5f);
	colors[ImGuiCol_Border].w = 0.0f;
}
