#pragma once

#include "Engine.h"

class Buffer;
class LUT;

enum class GUICtrlType {
	Float,
	Int,
	Vec2,
	Vec3,
	Mat4,
	Bool
};

struct GUIControlArgs {
	float range_min = 0;
	float range_max = 0;
	GUICtrlType type = GUICtrlType::Float;
	float precision = 0.01;
	std::function<void()> callback;
	bool is_colour;
};

// template<typename T>
struct GUIDbgVal {
	std::string name;
	// T value;

	GUICtrlType typ;

	union {
		float f_val;
		int i_val;
		glm::vec3 v3_val;
		glm::vec2 v2_val;
		glm::mat4 m4_val;
	};
};

class GUIControl {
	public:
		std::string name;
		void* value_ptr;
		GUICtrlType type;
		float range_min;
		float range_max;
		float precision;
		std::function<void()> callback;
		bool is_colour = false;
		GUIControl(std::string name, void* value_ptr, GUIControlArgs args);
};

struct TimerQueryState {
	double delta_time;
	double percentage_total;
	double delta_time_display;
};


struct ImGuiIO;
class Engine;

static const char* SequencerItemTypeNames[] = { "Camera","Music", "ScreenEffect", "FadeIn", "Animation" };


class Editor {
	private:
		void display_imgui_shader_errors();
		void display_debug_g_buffer();

	
	public:
		Editor();
		// Engine* engine = nullptr;

		bool timePaused = false;
		bool toStartPausedRendering = false;
		bool toEndPausedRendering = false;
		bool renderingPaused = false;
		bool windowOnTop = false;
		bool imgui_menu_collapsed = false;
		bool finished = false;

		bool entitties_debugger_enabled = true;
		bool physics_debugger_enabled = false;
		bool g_buffer_debugger_enabled = true;

		bool there_is_debug_group_pushed = false;

		volatile clock_t cpu_timestamp_start_of_frame = 0;
		std::chrono::steady_clock::time_point chrono_cpu_timestamp_start_of_frame;
		SIMDString<128> curr_cpu_timer_name;
	
		struct CPUTimer{
			int time_miliseconds;
			SIMDString<128> name;
			std::chrono::time_point<std::chrono::steady_clock> start_time;
			std::chrono::time_point<std::chrono::steady_clock> end_time;
		};

		int curr_cpu_timer_idx = 0;
		smallvec<CPUTimer, 10> cpu_timers;
	
		volatile float cpu_time_curr_frame = 0;

		ImGuiIO* imgui_io;

		std::array<float, 500> frame_times = {};
		int frame_time_index;
		double music_time = 0;
		double verse_time = 0;

		Buffer* printf_buffer;
		Buffer* physics_debug_drawer_buff;

		ShaderProgram* prog_physics_dbg;
		Texture* depth_texture = nullptr;
		Framebuffer* g_buff = nullptr;
	
		Timeline timeline;

		void init();

		void handle_shader_reload();

		std::vector<LUT*> luts;
		std::unordered_map<std::string, TimerQueryState> gl_timer_query_cached_values;
		std::unordered_map<std::string, std::array<uint32_t, 10>> gl_timer_queries;
		std::vector<SIMDString<64>> queries_used_this_frame;
		std::map<uint32_t, std::string> gl_timer_query_indices;
		uint32_t timer_query_fallback_idx = 0;
		void start_timer_query(std::string_view name);
		void start_cpu_timer(std::string_view name);

		void end_cpu_timer();

		bool selected_entity_is_light = false;
		std::optional<void*> selected_entity = std::nullopt;
		std::optional<WObj<Light>> selected_light = std::nullopt;


		std::vector<GUIControl> gui_controls;
		std::vector<GUIDbgVal> gui_dbg_vals;

		GUIDbgVal* get_dbg_val(std::string_view name);

		void gui_dbg_print(std::string_view name, glm::vec2 value);
		void gui_dbg_print(std::string_view name, float value);
		void gui_dbg_print(std::string_view name, int value);
		void gui_dbg_print(std::string_view name, uint value);
		void gui_dbg_print(std::string_view name, glm::vec3 value);
		void gui_dbg_print(std::string_view name, glm::mat4 value);

		void add_gui_control(std::string_view name, bool* value_ptr, GUIControlArgs args = {});
		void add_gui_control(std::string_view name, glm::vec2* value_ptr, GUIControlArgs args = {});
		void add_gui_control(std::string_view name, glm::vec3* value_ptr, GUIControlArgs args = {});
		void add_gui_control(std::string_view name, float* value_ptr, GUIControlArgs args = {});
		void add_gui_control(std::string_view name, int* value_ptr, GUIControlArgs args = {});
	
		void expose_lut(LUT* lut);

		void gui_dbg_print_msg(std::string_view msg);

		std::vector<SIMDString<256>> notification_messages;
		std::vector<float> times_since_notification_messages;
		int notification_message_idx = 0;
		static constexpr int max_notification_messages = 20;

		__declspec(noinline) void start_frame();
		void draw_physics_debug();
		__declspec(noinline) void end_frame();
		static void __stdcall GLErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
			const GLchar* message, const void* userParam);
		// void handle_mouse();
		void do_imgui();
		void setImGuiTheme();
};
