#include "imguiManager.h"
#include "application_class.h"
#include "stats.h"

imguiManager::imguiManager()
{
	io = nullptr;
	m_frameTimeHistoryIndex = 0;

	current_fps_ = 0;
	min_fps_ = 0;
	max_fps_ = 0;
	draw_calls_ = 0;
	total_vertex_count_ = 0;
	total_triangle_count_ = 0;
	visible_triangle_count_ = 0;
	current_frame_time_ = 0;

	showObjectWindow = false;
	showTerrainWindow = false;
	showLightWindow = false;
	showOldSceneWindow = false;
	showEngineSettingsWindow = false;
	showLogWindow = false;
	showStatsWindow = false;
	show_inspector_window_ = false;
    
	// Initialiser l'historique des frametimes � z�ro
	for (int i = 0; i < FRAME_HISTORY_COUNT; i++)
	{
		m_frameTimeHistory[i] = 0.0f;
	}

	widgets_ = {
		{&showObjectWindow, [&](){WidgetObjectWindow();}},
		{&showEngineSettingsWindow, [&](){WidgetEngineSettingsWindow();}},
		{&showTerrainWindow, [&](){WidgetTerrainWindow();}},
		{&showLightWindow, [&](){WidgetLightWindow();}},
		{&showLogWindow, [&](){WidgetLogWindow();}},
		{&showOldSceneWindow, [&](){WidgetRenderWindow(ImVec2(800, 600));}},
		{&showStatsWindow, [&](){WidgetRenderStats();}},
		{&show_inspector_window_, [&](){WidgetInspectorWindow();}}
	};
}

imguiManager::~imguiManager()
{
}

bool imguiManager::IncrementBuildVersionInConfig(const std::string& filepath) {
	std::ifstream file_in(filepath);
	if (!file_in) R_FALSE // fichier introuvable

	std::vector<std::string> lines;
	std::string line;

	// Lire toutes les lignes pour les manipuler
	while (std::getline(file_in, line)) {
		if (line.find("VER=") == 0) {
			// Extraire la version actuelle
			std::string ver_str = line.substr(4);
			try {
				BUILD_VERSION_VER = std::stoi(ver_str);
				if (BUILD_VERSION_STATE == "Dev")
				{
					BUILD_VERSION_VER += 1;
					line = "VER=" + std::to_string(BUILD_VERSION_VER);
				}
			} catch (...) {
				LOG_ERROR("Error converting VER to integer");
				R_FALSE // erreur conversion
			}
		}
		lines.push_back(line);
	}
	file_in.close();

	// Si pas trouvé VER=, on l'ajoute
	if (BUILD_VERSION_VER == -1) {
		BUILD_VERSION_VER = 0;
		lines.push_back("VER=0");
	}

	// Réécrire tout le fichier
	std::ofstream file_out(filepath, std::ios::trunc);
	if (!file_out)
	{
		LOG_ERROR("Error writing to file");
		R_FALSE
	}

	for (const auto& l : lines) {
		file_out << l << "\n";
	}
	file_out.close();
	R_TRUE
}

bool imguiManager::Initialize(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* deviceContext)
{
    LOG_INIT("Initializing imgui");

    m_device = device;
    m_deviceContext = deviceContext;

    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    io = &ImGui::GetIO();
    io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    io->ConfigFlags |= ImGuiConfigFlags_DockingEnable;
	io->FontGlobalScale = 1.0f;

    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX11_Init(m_device, m_deviceContext);

    // Appliquer un th�me sombre de base
    ImGui::StyleColorsDark();
    
    // D�finir les couleurs pour une interface de type �diteur sobre
    ImGuiStyle& style = ImGui::GetStyle();
    
    // Palette de couleurs sobres inspir�e des �diteurs modernes
 //    ImVec4 background_dark = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);    // Fond fonc�
 //    ImVec4 background = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);         // Fond principal
 //    ImVec4 background_light = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);   // Fond clair
 //    ImVec4 accent = ImVec4(0.14f, 0.44f, 0.80f, 0.50f);             // Accent bleu
 //    ImVec4 accent_light = ImVec4(0.14f, 0.44f, 0.80f, 1.00f);       // Accent bleu vif
	// ImVec4 text = ImVec4(1.0f, 1.0f, 1.0f, 1.00f);                // Texte plus blanc
	// ImVec4 text_dim = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
    
    // Ajustements de style g�n�raux
	style.WindowPadding = ImVec2(4.0f, 4.0f);       // Moins de padding dans les fen�tres
	style.FramePadding = ImVec2(4.0f, 3.0f);        // Moins de padding dans les cadres
	style.ItemSpacing = ImVec2(4.0f, 3.0f);         // Moins d'espace entre les widgets
	style.ItemInnerSpacing = ImVec2(3.0f, 3.0f);    // Moins d'espace interne
	style.DisplayWindowPadding = ImVec2(0.0f, 0.0f); // Pas de padding pour l'affichage
	style.DisplaySafeAreaPadding = ImVec2(0.0f, 0.0f);
    style.TouchExtraPadding = ImVec2(0.0f, 0.0f);
    style.IndentSpacing = 20.0f;
    style.ScrollbarSize = 14.0f;
    style.GrabMinSize = 10.0f;
    
    // Arrondis
    style.WindowRounding = 4.0f;
    style.ChildRounding = 4.0f;
    style.FrameRounding = 3.0f;
    style.PopupRounding = 4.0f;
    style.ScrollbarRounding = 9.0f;
    style.GrabRounding = 3.0f;
    style.TabRounding = 4.0f;
    
    // Couleurs principales
    style.Colors[ImGuiCol_Text] = TEXT_COLOR;
    style.Colors[ImGuiCol_TextDisabled] = TEXT_DIM_COLOR;
    style.Colors[ImGuiCol_WindowBg] = BG_GRAY;
    style.Colors[ImGuiCol_ChildBg] = BG_DK_GRAY;
    style.Colors[ImGuiCol_PopupBg] = BG_DK_GRAY;
    style.Colors[ImGuiCol_Border] = BORDER_COLOR;
    style.Colors[ImGuiCol_BorderShadow] = BLK;
    
    // Encadrements
    style.Colors[ImGuiCol_FrameBg] = BG_LT_GRAY;
    style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
    style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);
    
    // Titres
    style.Colors[ImGuiCol_TitleBg] = BG_DK_GRAY;
    style.Colors[ImGuiCol_TitleBgActive] = ACCENT_COLOR;
    style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.12f, 0.12f, 0.12f, 0.90f);
    
    // �l�ments de menu
    style.Colors[ImGuiCol_MenuBarBg] = BG_DK_GRAY;
    style.Colors[ImGuiCol_ScrollbarBg] = BG_DK_GRAY;
    style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f);
    style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
    style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    
    // Boutons et interactions
    style.Colors[ImGuiCol_CheckMark] = ACCENT_LT_COLOR;
    style.Colors[ImGuiCol_SliderGrab] = ACCENT_COLOR;
    style.Colors[ImGuiCol_SliderGrabActive] = ACCENT_LT_COLOR;
    style.Colors[ImGuiCol_Button] = BG_LT_GRAY;
    style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);
    style.Colors[ImGuiCol_ButtonActive] = ACCENT_COLOR;
    
    // En-t�tes et onglets
    style.Colors[ImGuiCol_Header] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
    style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
    style.Colors[ImGuiCol_HeaderActive] = ACCENT_COLOR;
    
    style.Colors[ImGuiCol_Separator] = ImVec4(0.28f, 0.28f, 0.28f, 1.00f);
    style.Colors[ImGuiCol_SeparatorHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f);
    style.Colors[ImGuiCol_SeparatorActive] = ACCENT_COLOR;
    
    style.Colors[ImGuiCol_Tab] = BG_DK_GRAY;
    style.Colors[ImGuiCol_TabHovered] = ACCENT_COLOR;
    style.Colors[ImGuiCol_TabActive] = ACCENT_COLOR;
    style.Colors[ImGuiCol_TabUnfocused] = BG_DK_GRAY;
    style.Colors[ImGuiCol_TabUnfocusedActive] = BG_GRAY;
    
    // Autres �l�ments 
    style.Colors[ImGuiCol_DockingPreview] = ACCENT_COLOR;
    style.Colors[ImGuiCol_DockingEmptyBg] = BG_LT_GRAY;

	// Charger une police avec une meilleure nettet�
	ImFontConfig fontConfig;
	fontConfig.OversampleH = 2; // Sur�chantillonnage horizontal
	fontConfig.OversampleV = 2; // Sur�chantillonnage vertical
	fontConfig.PixelSnapH = true; // Alignement sur la grille de pixels
	fontConfig.RasterizerMultiply = 1.2f; // L�g�re augmentation de l'�paisseur
	io->Fonts->AddFontDefault(&fontConfig);

	io->Fonts->Build();

	// OU charger une police personnalis�e (d�commenter si vous avez la police)
	// io->Fonts->AddFontFromFileTTF("assets/fonts/roboto.ttf", 16.0f, &fontConfig);
    

	unsigned char* pixels;
	int width, height;
	io->Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);

	// initialize the scene manager
	scene_manager_ = new scene_manager;
	if (!scene_manager_->initialize(app_.get())) {
		LOG_ERROR("Failed to initialize scene manager");
		R_FALSE
	}

	stats_ = app_->get_stats();

	total_triangle_count_ = stats_->get_triangle_count_ptr();
	total_vertex_count_ = stats_->get_vertex_count_ptr();

	entity_manager_ = app_->get_entity_manager();

	// Get the exe parent directory to find config.txt ../exefolder
	char exePath[MAX_PATH];
	GetModuleFileNameA(NULL, exePath, MAX_PATH);
	std::string exeDir = std::string(exePath);
	// Retirer la partie exe filename (tout après le dernier slash)
	size_t lastSlash = exeDir.find_last_of("\\/");
	if (lastSlash != std::string::npos) {
		exeDir = exeDir.substr(0, lastSlash);
	} else {
		exeDir = "";
	}

	// Retirer encore le dernier dossier (rebrousser dans le parent) : aller dans le dossier parent
	lastSlash = exeDir.find_last_of("\\/");
	if (lastSlash != std::string::npos) {
		exeDir = exeDir.substr(0, lastSlash + 1);  // garder slash de fin pour concat
	} else {
		exeDir = "";
	}
	
	std::string configPath = "config.txt"; // ou chemin complet
	configPath = exeDir + BUILD_VERSION_TYPE + "\\" + configPath;
	if (!IncrementBuildVersionInConfig(configPath)) {
		LOG_WARNING("Failed to increment build version in config.txt");
	}

	// update the build version text in the inverse build type folder
	configPath = "config.txt";
	configPath = exeDir + BUILD_VERSION_INVERSE + "\\" + configPath;
	LOG_INFO("Inverse build type config path: " + configPath);
	if (!IncrementBuildVersionInConfig(configPath)) {
		LOG_WARNING("Failed to increment build version in config.txt of inverse build type");
	}

	// get the sky entity shared ptr from the application
	sky_entity_shared_ptr_ = app_->get_sky_entity_shared_ptr();
	if (!sky_entity_shared_ptr_) {
		LOG_WARNING("Sky entity shared ptr is null");
	}
    
    LOG_INIT("imgui initialized");
    
    R_TRUE
}

void imguiManager::Shutdown()
{
	LOG_SHUTDOWN("Shutting down imgui");
	ImGui_ImplDX11_Shutdown();
	ImGui_ImplWin32_Shutdown();
	ImGui::DestroyContext();
	LOG_SHUTDOWN("imgui shutdown");
}

void imguiManager::Render()
{
	ImGui::Render();

	//app->get_direct_3d()->turn_z_buffer_off();
	//app->get_direct_3d()->enable_alpha_blending();
	
	ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

	//app->get_direct_3d()->disable_alpha_blending();
	//app->get_direct_3d()->turn_z_buffer_on();
}

void imguiManager::NewFrame()
{
	ImGui_ImplDX11_NewFrame();
	ImGui_ImplWin32_NewFrame();
	ImGui::NewFrame();
}

void imguiManager::SetupDockspace() {
    BASIC_DOCK_STYLE_1
    // Configuration de la fen�tre principale
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
    ImGuiViewport* viewport = ImGui::GetMainViewport();
    ImGui::SetNextWindowPos(viewport->Pos);
    ImGui::SetNextWindowSize(viewport->Size);
    ImGui::SetNextWindowViewport(viewport->ID);
    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
    window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;

    ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
    
    ImGui::Begin("DockSpace", nullptr, window_flags);
    
    // Pop des styles apr�s avoir cr�� la fen�tre principale
    ImGui::PopStyleVar(4); // 4 car nous avons pouss� 4 variables de style
    ImGui::PopStyleColor();

    // Configuration du DockSpace
    ImGuiID dockspace_id = ImGui::GetID("MainDockSpace");
    ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode;
    ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);

    if (ImGui::BeginMenuBar()) {
        if (B_MENU("Windows")) {
            M_ITEM_LP("Object Window", &showObjectWindow);
            M_ITEM_LP("Terrain Window", &showTerrainWindow);
            M_ITEM_LP("Light Window", &showLightWindow);
            M_ITEM_LP("Engine Settings Window", &showEngineSettingsWindow);
            M_ITEM_LP("Log Window", &showLogWindow);
            M_ITEM_LP("render Stats", &showStatsWindow);
            E_MENU
        }

    	if (B_MENU("Scene")) {
    		if (M_ITEM_L("Save Scene")) {
    			scene_manager_->save_scene();
    		}
    		if (M_ITEM_L("Save Scene As...")) {
    			scene_manager_->save_scene_as();
    		}
    		if (M_ITEM_L("Load Scene")) {
    			scene_manager_->load_scene();
    		}
    		E_MENU
    	}

    	const float PAD_X = 14.0f;
    	float menuBarHeight = ImGui::GetFrameHeight();

    	std::string versionText = std::string("Build-") + BUILD_VERSION_TYPE + "-" +
			std::to_string(BUILD_VERSION_MAJOR) + "." +
			std::to_string(BUILD_VERSION_MINOR) + "." +
			std::to_string(BUILD_VERSION_PATCH) + "." +
			std::to_string(BUILD_VERSION_VER) + "-" +
			BUILD_VERSION_STATE;

    	ImVec2 textSize = ImGui::CalcTextSize(versionText.c_str());
    	float windowWidth = ImGui::GetWindowContentRegionMax().x;

    	// Rectangle ajusté parfaitement à la hauteur
    	float rectWidth = textSize.x + PAD_X * 2;
    	float rectX = ImGui::GetWindowPos().x + windowWidth - rectWidth - 10.0f; // marge droite
    	float rectY = ImGui::GetWindowPos().y;
    	float offsetX = textSize.x * 0.2f;

    	ImVec2 rectMin(rectX - offsetX, rectY);
    	ImVec2 rectMax(rectX + rectWidth - offsetX, rectY + menuBarHeight);

    	// Fond arrondi parfaitement aligné avec la barre
    	ImGui::GetForegroundDrawList()->AddRectFilled(
			rectMin, rectMax, DEBUG_MODE_COLOR, menuBarHeight / 2.0f
		);

    	// Texte centré verticalement dans le rectangle
    	ImVec2 textPos(
			rectMin.x + PAD_X,
			rectMin.y + (menuBarHeight - textSize.y) / 2.0f
		);

    	ImGui::GetForegroundDrawList()->AddText(
			ImGui::GetFont(), ImGui::GetFontSize(),
			textPos, IM_COL32(255,255,255,255), versionText.c_str()
		);

        ImGui::EndMenuBar();
    }

    END
}

void imguiManager::WidgetSpeedSlider(float* speed)
{
	F_SLIDER("Speed", speed, 0.0f, 100.0f);
}

void imguiManager::WidgetButton()
{
	static int counter = 0;

	if (BUTTON("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
		counter++;
	S_LINE
	TEXT_V("counter = %d", counter)
}

void imguiManager::WidgetAddObject()
{
	if (C_HEADER("Objects"))
	{
		if (BUTTON("Add Cube"))
		{
			app_->add_cube();
		}
		S_LINE
		if (BUTTON("Import Object"))
		{
			// Open file dialog
			OPENFILENAME ofn;
			WCHAR szFile[260];
			ZeroMemory(&ofn, sizeof(ofn));
			ofn.lStructSize = sizeof(ofn);
			ofn.hwndOwner = NULL;
			ofn.lpstrFile = szFile;
			ofn.lpstrFile[0] = '\0';
			ofn.nMaxFile = sizeof(szFile);
			ofn.lpstrFilter = L"OBJ\0*.obj\0KOBJ\0*.kobj\0TXT\0*.txt";
			ofn.nFilterIndex = 1;
			ofn.lpstrFileTitle = NULL;
			ofn.nMaxFileTitle = 0;
			ofn.lpstrInitialDir = NULL;
			ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

			if (GetOpenFileName(&ofn))
			{
				std::wstring filepath = ofn.lpstrFile;

				WCHAR exePath[MAX_PATH];
				GetModuleFileName(NULL,exePath,MAX_PATH);
				std::wstring exeDir = exePath;
				size_t pos = exeDir.find_last_of(L"\\/");
				if(pos != std::wstring::npos)
				{
					exeDir = exeDir.substr(0, pos);
				}

				std::wstring targetDir = exeDir + L"\\Content\\Assets\\Kobject";


				DWORD ftyp = GetFileAttributesW(targetDir.c_str());
				if( ftyp == INVALID_FILE_ATTRIBUTES)
				{
					std::wstring contentDir = exeDir + L"\\Content";
					if (GetFileAttributes(contentDir.c_str()) == INVALID_FILE_ATTRIBUTES)
					{
						CreateDirectory(contentDir.c_str(), NULL);
					}
					std::wstring assetsDir = contentDir + L"\\Assets";
					if (GetFileAttributes(assetsDir.c_str()) == INVALID_FILE_ATTRIBUTES)
					{
						CreateDirectory(assetsDir.c_str(), NULL);
					}
					std::wstring kobjectDir = assetsDir + L"\\Kobject";
					if (GetFileAttributes(kobjectDir.c_str()) == INVALID_FILE_ATTRIBUTES)
					{
						CreateDirectory(kobjectDir.c_str(), NULL);
					}
				}

				size_t posFile = filepath.find_last_of(L"\\/");
				std::wstring filename = (posFile != std::wstring::npos) ? filepath.substr(posFile + 1) : filepath;

				std::wstring targetPath = targetDir + L"\\" + filename;

				if (!CopyFile(filepath.c_str(), targetPath.c_str(), FALSE))
				{
					// En cas d'erreur, vous pouvez g�rer ici l'erreur (par exemple afficher un message)
					MessageBox(NULL, L"Erreur lors de la copie du fichier.", L"Erreur", MB_OK);
				}
				else
				{
					// On r�cup�re le chemin relatif par rapport � exeDir
					std::wstring relativePath = targetPath.substr(exeDir.size());
        
					// Suppression du premier caract�re s'il s'agit d'un antislash
					if (!relativePath.empty() && (relativePath[0] == L'\\' || relativePath[0] == L'/'))
					{
						relativePath.erase(0, 1);
					}
        
					// Remplacer les antislashs par des slashs
					std::replace(relativePath.begin(), relativePath.end(), L'\\', L'/');
					app_->add_kobject(relativePath);
					// set entities list as dirty
					entityListDirty = true;
				}
				
			}

		}
		S_LINE
		TEXT_V("Number of cubes: %d", entity_manager_->GetEntityCount());
	}
}

void imguiManager::WidgetObjectWindow()
{
	BEGIN("Objects", &showObjectWindow)

	if (BUTTON("Add Cube")) { app_->add_cube(); }
	S_LINE
	if (BUTTON("Import Object"))
	{
		// Open file dialog
		OPENFILENAME ofn;
		WCHAR szFile[260];
		ZeroMemory(&ofn, sizeof(ofn));
		ofn.lStructSize = sizeof(ofn);
		ofn.hwndOwner = NULL;
		ofn.lpstrFile = szFile;
		ofn.lpstrFile[0] = '\0';
		ofn.nMaxFile = sizeof(szFile);
		ofn.lpstrFilter = L"OBJ\0*.obj\0KOBJ\0*.kobj\0TXT\0*.txt";
		ofn.nFilterIndex = 1;
		ofn.lpstrFileTitle = NULL;
		ofn.nMaxFileTitle = 0;
		ofn.lpstrInitialDir = NULL;
		ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

		if (GetOpenFileName(&ofn))
		{
			std::wstring filepath = ofn.lpstrFile;

			WCHAR exePath[MAX_PATH];
			GetModuleFileName(NULL,exePath,MAX_PATH);
			std::wstring exeDir = exePath;
			size_t pos = exeDir.find_last_of(L"\\/");
			if(pos != std::wstring::npos)
			{
				exeDir = exeDir.substr(0, pos);
			}

			std::wstring targetDir = exeDir + L"\\Content\\Assets\\Kobject";


			DWORD ftyp = GetFileAttributesW(targetDir.c_str());
			if( ftyp == INVALID_FILE_ATTRIBUTES)
			{
				std::wstring contentDir = exeDir + L"\\Content";
				if (GetFileAttributes(contentDir.c_str()) == INVALID_FILE_ATTRIBUTES)
				{
					CreateDirectory(contentDir.c_str(), NULL);
				}
				std::wstring assetsDir = contentDir + L"\\Assets";
				if (GetFileAttributes(assetsDir.c_str()) == INVALID_FILE_ATTRIBUTES)
				{
					CreateDirectory(assetsDir.c_str(), NULL);
				}
				std::wstring kobjectDir = assetsDir + L"\\Kobject";
				if (GetFileAttributes(kobjectDir.c_str()) == INVALID_FILE_ATTRIBUTES)
				{
					CreateDirectory(kobjectDir.c_str(), NULL);
				}
			}

			size_t posFile = filepath.find_last_of(L"\\/");
			std::wstring filename = (posFile != std::wstring::npos) ? filepath.substr(posFile + 1) : filepath;

			std::wstring targetPath = targetDir + L"\\" + filename;

			if (!CopyFile(filepath.c_str(), targetPath.c_str(), FALSE))
			{
				// En cas d'erreur, vous pouvez g�rer ici l'erreur (par exemple afficher un message)
				MessageBox(NULL, L"Erreur lors de la copie du fichier.", L"Erreur", MB_OK);
			}
			else
			{
				// On r�cup�re le chemin relatif par rapport � exeDir
				std::wstring relativePath = targetPath.substr(exeDir.size());
    
				// Suppression du premier caract�re s'il s'agit d'un antislash
				if (!relativePath.empty() && (relativePath[0] == L'\\' || relativePath[0] == L'/'))
				{
					relativePath.erase(0, 1);
				}
    
				// Remplacer les antislashs par des slashs
				std::replace(relativePath.begin(), relativePath.end(), L'\\', L'/');
				app_->add_kobject(relativePath);
				// set list as dirty since new entity has been added to the scene 
				entityListDirty = true;
			}
		}
	}
	S_LINE
	TEXT_V("Number of cubes: %d", entity_manager_->GetEntityCount())
	SEP
	
	UpdateCachedEntitiesIfNeeded();

	ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(4,2));

	for (auto& cachedEntitie : cachedEntities_)
	{
		bool isSelected = (cachedEntitie.id == m_selected_entity_id);
		if (SEL(cachedEntitie.name.c_str(), isSelected))
		{
			m_selected_entity_id = cachedEntitie.id;
			show_inspector_window_ = true;
		}
	}

	PSV
	END
}

void imguiManager::WidgetInspectorWindow()
{
	BEGIN("Inspector", &show_inspector_window_)

	auto entity = entity_manager_->GetEntityByID(m_selected_entity_id);
	if (entity)
	{
		auto identity = entity->GetComponent<ecs::IdentityComponent>();
		if (identity)
		{
			char name[256];
			strcpy_s(name, identity->GetName().c_str());
			if (I_TEX("Name",name,sizeof(name)))
			{
				identity->SetName(std::string(name));
			}
			TEXT_V("ID: %d", identity->GetId())
		}

		SEP

		// Lister et afficher les composants
		auto components = entity->GetAllComponents();
		for (auto& pair : components)
		{
			auto& comp = pair.second;
			// Utiliser le nom du type comme en-tete
			std::string compName = typeid(*comp).name();
			if (C_HEADER(compName.c_str()))
			{
				comp->OnImGuiRender();
			}
		}

		if (BUTTON("Delete Entity"))
		{
			entity_manager_->DestroyEntity(m_selected_entity_id);
			m_selected_entity_id = -1;
			show_inspector_window_ = false;
			entityListDirty = true;
		}
		S_LINE

		if (BUTTON("Add Component")) {
			O_POPUP("AddComponentPopup");
		}

		if (B_POPUP("AddComponentPopup")) {
			TEX("Select Component to Add:")
			SEP;

			ComponentEntry componentsEntry[] = {
				{"Transform Component",   [&]() { if (!entity->HasComponent<ecs::TransformComponent>()) entity->AddComponent<ecs::TransformComponent>(); }},
				{"Model Path Component",  [&]() { if (!entity->HasComponent<ecs::ModelPathComponent>()) entity->AddComponent<ecs::ModelPathComponent>(); }},
				{"Audio Component",       [&]() { if (!entity->HasComponent<ecs::AudioComponent>()) entity->AddComponent<ecs::AudioComponent>(); }},
				{"Physics Component",     [&]() { if (!entity->HasComponent<ecs::PhysicsComponent>()) entity->AddComponent<ecs::PhysicsComponent>(); }},
				{"Render Component",      [&]() { if (!entity->HasComponent<ecs::RenderComponent>()) entity->AddComponent<ecs::RenderComponent>(); }},
				{"Shader Component",      [&]() { if (!entity->HasComponent<ecs::ShaderComponent>()) entity->AddComponent<ecs::ShaderComponent>(); }},
				{"Lua Script Component",  [&]() { if (!entity->HasComponent<ecs::LuaScriptComponent>()) entity->AddComponent<ecs::LuaScriptComponent>(); }},
				{"Identity Component",    [&]() { if (!entity->HasComponent<ecs::IdentityComponent>()) entity->AddComponent<ecs::IdentityComponent>(); }}
				// Ajouter d'autres composants ici
			};

			for (auto& comp : componentsEntry) {
				if (M_ITEM_L(comp.name)) {
					comp.addFunc();
					ImGui::CloseCurrentPopup();
				}
			}

			E_POPUP
			
		}
	}
	else
	{
		TEX("No entity selected.")
	}

	END
}

void imguiManager::WidgetTerrainWindow()
{
	BEGIN("Terrain", &showTerrainWindow)

	TEXT_V("Number of terrain cubes: %d", app_->get_terrain_entity_count())
	SEP

	if (BUTTON("Generate Flat Terrain"))
	{
		app_->generate_terrain();
	}
	
	SEP

	// Input for the number of cubes on each side
	TEX("Number of cubes on each side: ")
	S_LINE
	ImGui::InputInt("##SideCount", &m_SideCount);
	if (m_SideCount < 1)
	{
		m_SideCount = 1;
	}
	
	SEP
	
	if (ImGui::Button("Generate BigCube Terrain"))
	{
		app_->create_big_cube(m_SideCount);
	}
	
	SEP
	
	if (ImGui::Button("Delete All Terrain Cubes"))
	{
		app_->delete_terrain();
	}
	
	
	END
}

bool imguiManager::ImGuiWidgetRenderer()
{
	// Start the Dear ImGui frame
	NewFrame();

	// Setup the dockspace
	SetupDockspace();

	//ImGui Widget

	// Read the widget list and call the function if the show variable is true
	for (const auto& entry : widgets_)
	{
		if (*entry.show == true) {entry.func();}
	}
	
	//render imgui
	Render();

	R_TRUE
}

void imguiManager::WidgetLightWindow()
{
	BEGIN("Light", &showLightWindow)
	

	// Sun light settings
	light_class* sunLight = app_->get_sun_light();
	// Direction input
	XMFLOAT3 direction = sunLight->GetDirection();
	float dir[3] = { direction.x, direction.y, direction.z };
	if (ImGui::DragFloat3("Sun Direction", dir))
	{
		sunLight->SetDirection(dir[0], dir[1], dir[2]);
	}
	// Color input
	XMFLOAT4 color = sunLight->GetDiffuseColor();
	float col[3] = { color.x, color.y, color.z };
	if (ImGui::ColorEdit3("Sun Color", col))
	{
		sunLight->SetDiffuseColor(col[0], col[1], col[2], 1.0f);
	}
	// Intensity input
	float intensity = sunLight->GetIntensity();
	if (ImGui::DragFloat("Sun Intensity", &intensity, 0.1f, 0.0f, 100.0f))
	{
		sunLight->SetIntensity(intensity);
	}


	SEP

	int index = 0;

	// Area light settings

	for(auto& light : app_->get_lights())
	{
		std::string headerName = "Light " + std::to_string(index);
		if (C_HEADER(headerName.c_str()))
		{
			XMVECTOR position = app_->get_light_position(index);
			XMVECTOR color = app_->get_light_color(index);
			float pos[3] = { XMVectorGetX(position), XMVectorGetY(position), XMVectorGetZ(position) };
			float col[3] = { XMVectorGetX(color), XMVectorGetY(color), XMVectorGetZ(color) };

			std::string posLabel = "Position##" + std::to_string(index);
			std::string colLabel = "Color##" + std::to_string(index);

			if (ImGui::DragFloat3(posLabel.c_str(), pos))
			{
				app_->set_light_position(index, XMVectorSet(pos[0], pos[1], pos[2], 0.0f));
			}

			if (ImGui::ColorEdit3(colLabel.c_str(), col))
			{
				app_->set_light_color(index, XMVectorSet(col[0], col[1], col[2], 0.0f));
			}

		}
		index++;
	};

	END
}

void imguiManager::WidgetEngineSettingsWindow()
{
	BEGIN("Engine Settings", &showEngineSettingsWindow)

	// Variables temporaires pour les settings
	static bool temp_v_sync = app_->get_vsync();
	static int temp_target_fps = app_->get_target_fps();
	static float temp_screen_depth = app_->get_screen_depth();
	static float temp_screen_near = app_->get_screen_near();
	static float temp_frustum_tolerance = app_->get_frustum_tolerance();
	static int temp_physics_tick_rate = app_->get_physics_tick_rate();
	static float temp_gravity_values[3] = {
		XMVectorGetX(app_->get_physics()->GetGravity()),
		XMVectorGetY(app_->get_physics()->GetGravity()),
		XMVectorGetZ(app_->get_physics()->GetGravity())
	};

	if (ImGui::BeginTabBar("Engine Settings"))
	{
		if (ImGui::BeginTabItem("General"))
		{
			ImGui::Checkbox("Vsync", &temp_v_sync);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Enable or disable Vsync");
			
			ImGui::InputInt("Target FPS", &temp_target_fps);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Set the target FPS for the engine");
			
			// Drag float input for screen depth and near with min and max values and update the app values on change
			ImGui::DragFloat("Screen Depth", &temp_screen_depth, 1.f, 0.1f, 10000.0f);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Set the screen depth");
			
			ImGui::DragFloat("Screen Near", &temp_screen_near, 1.f, 0.1f, 10000.0f);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Set the screen near");
			
			ImGui::EndTabItem();
		}
		
		if (ImGui::BeginTabItem("Culling Settings"))
		{

			ImGui::DragFloat("Frustum Tolerance", &temp_frustum_tolerance, 0.1f, 0.0f, 100.0f);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Set the frustum tolerance for culling");
			
			ImGui::EndTabItem();
		}

		if (ImGui::BeginTabItem("Physics Settings"))
		{
			
			ImGui::InputInt("Physics Tick Rate", &temp_physics_tick_rate);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Set the physics tick rate (Fixed Update Interval)");

			ImGui::DragFloat3("Gravity", temp_gravity_values);
			if (ImGui::IsItemHovered())
				ImGui::SetTooltip("Set the gravity vector for the physics engine");
			
			ImGui::EndTabItem();
		}

		if (ImGui::BeginTabItem("Skysphere Settings"))
		{

			if (sky_entity_shared_ptr_)
			{

				// get the transform component
				auto transform = sky_entity_shared_ptr_->GetComponent<ecs::TransformComponent>();
				auto render = sky_entity_shared_ptr_->GetComponent<ecs::RenderComponent>();
				auto shader = sky_entity_shared_ptr_->GetComponent<ecs::ShaderComponent>();
				if (transform)
				{
					// get the scale and display it as a drag float3 input (need to convert from XMVECTOR to float[3])
					XMVECTOR scale = transform->GetScale();
					float scale_values[3] = { XMVectorGetX(scale), XMVectorGetY(scale), XMVectorGetZ(scale) };
					if (ImGui::DragFloat3("Skysphere Scale", scale_values, 0.1f, 1.0f, 1000.0f))
					{
						transform->SetScale(XMVectorSet(scale_values[0], scale_values[1], scale_values[2], 0.0f));
					}
					if (ImGui::IsItemHovered())
					{
						ImGui::SetTooltip("Set the scale of the skysphere");
					}
				}

				if (render)
				{
					// display the render component default imguirender
					render->OnImGuiRender();
				}

				if (shader)
				{
					// display the render component default imguirender
					shader->OnImGuiRender();
				}
				
			}
			else
			{
				TEX("Sky entity not found.")
			}

			ImGui::EndTabItem();
			
		}
		
		ImGui::EndTabBar();
	}

	if (ImGui::Button("Apply Settings"))
	{
		app_->set_vsync(temp_v_sync);
		app_->set_target_fps(temp_target_fps);
		app_->update_screen_depth(temp_screen_depth);
		app_->update_screen_near(temp_screen_near);
		app_->set_frustum_tolerance(temp_frustum_tolerance);
		app_->set_physics_tick_rate(temp_physics_tick_rate);
		app_->get_physics()->SetGravity(XMVectorSet(temp_gravity_values[0], temp_gravity_values[1], temp_gravity_values[2], 0.0f));
	}
	S_LINE
	if (ImGui::Button("Reset Settings"))
	{
		temp_v_sync = app_->get_vsync();
		temp_target_fps = app_->get_target_fps();
		temp_screen_depth = app_->get_screen_depth();
		temp_screen_near = app_->get_screen_near();
		temp_frustum_tolerance = app_->get_frustum_tolerance();
		temp_physics_tick_rate = app_->get_physics_tick_rate();
		temp_gravity_values[0] = XMVectorGetX(app_->get_physics()->GetGravity());
		temp_gravity_values[1] = XMVectorGetY(app_->get_physics()->GetGravity());
		temp_gravity_values[2] = XMVectorGetZ(app_->get_physics()->GetGravity());
	}

	END
}

void imguiManager::WidgetLogWindow()
{
    BEGIN("Log Window" , &showLogWindow)

    // Filtre de recherche
    static ImGuiTextFilter filter;
    filter.Draw("Filter ", 180);

	S_LINE

	// Bouton pour ouvrir le fichier de log
	if (ImGui::Button("Open Log File"))
	{
		ShellExecuteA(NULL, "open", Logger::Get().m_logFilePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
	}

    // Place the menu on the same line as the filter
    S_LINE

    // Menu d�roulant pour les niveaux de log
    if (B_MENU("Log Levels"))
    {
        for (size_t i = 0; i < Logger::LogLevelCount; ++i)
        {
            bool isVisible = !Logger::Get().m_disabledLogLevels[i];
            if (C_BOX(Logger::Get().GetLogLevelInfo(static_cast<Logger::LogLevel>(i)).name, &isVisible))
            {
                Logger::Get().m_disabledLogLevels[i] = !isVisible;
            }
        }
        E_MENU
    }

	const auto& logBuffer = Logger::Get().GetLogBuffer();
	std::vector<Logger::LogEntry> logfiltered;
	int logCount = ulli_to_int(logBuffer.size());

    // Affichage des logs filtr�s
    ImGui::BeginChild("Log");

	for (const auto& log : logBuffer)
	{
		if (filter.PassFilter(log.message.c_str()) && !Logger::Get().m_disabledLogLevels[static_cast<size_t>(log.level)])
		{
			logfiltered.push_back(log);
		}
	}

	if (logfiltered.size() == 0)
	{
		TEX("No logs to display.")
	}
	else
	{
		ImGuiListClipper clipper;
		clipper.Begin(logCount);
		while (clipper.Step())
		{
			for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
			{
				if (i < logfiltered.size()) {
					const auto& log = logfiltered[i];
					ImGui::TextColored(Logger::Get().GetLogLevelInfo(log.level).color, log.message.c_str());
				}
				
			}
		}
		clipper.End();
	}

	// Scroll to the bottom
	if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
	{
		ImGui::SetScrollHereY(1.0f);
	}

    ImGui::EndChild();

    END
}

void imguiManager::WidgetRenderWindow(ImVec2 availableSize)
{
	ImGui::Begin("render Window");

	ImVec2 oldWindowSize = windowSize;
	windowSize = ImGui::GetContentRegionAvail();
	
	// Si la taille de la fen�tre a chang�, ajustez la taille de la fen�tre de l'application
	if (oldWindowSize.x != windowSize.x || oldWindowSize.y != windowSize.y)
	{
		app_->set_window_size(windowSize);
	}
	

	// Get the aspect ratio of the scene in app
	float aspectRatio = app_->get_aspect_ratio();
	// calculate the size of the window
	if (windowSize.x / windowSize.y > aspectRatio)
	{
		windowSize.x = windowSize.y * aspectRatio;
	}
	else
	{
		windowSize.y = windowSize.x / aspectRatio;
	}
	
	ID3D11ShaderResourceView* texture = app_->get_scene_texture()->GetShaderResourceView();
	if (texture)
	{
		
		// Affichez la scenne projet� sur texture dans une fen�tre ImGui
		// alpha blend is not enable to render the texture

		app_->get_direct_3d()->turn_z_buffer_off();
		app_->get_direct_3d()->enable_alpha_blending();
		
		ImGui::Image((ImTextureID)texture, windowSize, ImVec2(0, 0), ImVec2(1, 1), ImVec4(1, 1, 1, 1));
	}
	else
	{
		TEX("render texture is not available.")
	}

	END
}

void imguiManager::WidgetRenderStats()
{
    ImGui::Begin("render Stats");

	current_fps_ = stats_->get_current_fps();
	min_fps_ = stats_->get_min_fps();
	max_fps_ = stats_->get_max_fps();
	draw_calls_ = stats_->get_draw_calls();

	
	// total_vertex_count_ = stats_->get_total_vertex_count();
	// total_triangle_count_ = stats_->get_total_triangle_count();
	
	visible_triangle_count_ = stats_->get_visible_triangle_count();
	current_frame_time_ = stats_->get_frame_time();
	
    m_frameTimeHistory[m_frameTimeHistoryIndex] = current_frame_time_;
    m_frameTimeHistoryIndex = (m_frameTimeHistoryIndex + 1) % FRAME_HISTORY_COUNT;

    TEXT_V("FPS: %d", current_fps_)
    S_LINE
    TEXT_V("Min Fps: %d", min_fps_)
    S_LINE
    TEXT_V("Max Fps: %d", max_fps_)

    SEP

    // Trouver les valeurs min/max pour l'�chelle du graphique
    float frameTimeMin = FLT_MAX;
    float frameTimeMax = 0.0f;
    for (int i = 0; i < FRAME_HISTORY_COUNT; i++) {
        if (m_frameTimeHistory[i] > 0.0f) {
            frameTimeMin = min(frameTimeMin, m_frameTimeHistory[i]);
            frameTimeMax = max(frameTimeMax, m_frameTimeHistory[i]);
        }
    }
    // S'assurer d'avoir au moins une petite plage
    if (frameTimeMax == 0.0f) frameTimeMax = 0.033f; // ~30 FPS
    if (frameTimeMin == FLT_MAX) frameTimeMin = 0.0f;
    
    // Ajouter 10% de marge pour la lisibilit�
    float margin = (frameTimeMax - frameTimeMin) * 0.1f;
    frameTimeMin = max(0.0f, frameTimeMin - margin);
    frameTimeMax += margin;

    // Afficher le graphique
    TEXT_V("Frame Time: %.3f ms", current_frame_time_ * 1000.0f)
	ImGui::PlotLines("FrameTimeGraph", // Au lieu de cha�ne vide ""
				 m_frameTimeHistory, 
				 FRAME_HISTORY_COUNT, 
				 m_frameTimeHistoryIndex, 
				 "", 
				 frameTimeMin, 
				 frameTimeMax, 
				 ImVec2(0, 80));

    TEXT_V("Draw Calls: %d", draw_calls_)

    SEP

    TEX("Statistiques de rendu:")
    TEXT_V("Vertices total: %d", *total_vertex_count_)

    TEXT_V("Triangles total: %d", *total_triangle_count_)
    S_LINE
    TEXT_V("Triangles visibles: %d", visible_triangle_count_)

	app_->get_direct_3d()->get_video_card_info(card_name_, video_memory_);
	cpu_name_ = stats_->get_cpu_name();
	version_driver_ = stats_->get_gpu_driver_version(app_->get_direct_3d()->get_device());
	
	ImGui::Columns(3, "GPUCPURAMColumns", false);
	ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.33f);
	ImGui::SetColumnWidth(1, ImGui::GetWindowWidth() * 0.33f);

	// Premier collapsing header pour les informations GPU
	if (C_HEADER("Informations GPU"))
	{
		TEXT_V("Carte graphique: %s", card_name_)
		TEXT_V("Memoire video: %d Mo", video_memory_)
		TEXT_V("Pilote: %s", version_driver_.c_str())
	}

	ImGui::NextColumn();

	// Second collapsing header pour les informations CPU
	if (C_HEADER("Informations CPU"))
	{
		SYSTEM_INFO sysInfo;
		GetSystemInfo(&sysInfo);
		TEXT_V("Processeur: %s", cpu_name_.c_str())
		TEXT_V("Nombre de coeurs: %u", sysInfo.dwNumberOfProcessors)
		TEXT_V("Architecture: %s", (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) ? "x64" : "x86")
		TEXT_V("Taille de la page: %u octets", sysInfo.dwPageSize)
		TEXT_V("Taille du cache: %u octets", sysInfo.dwAllocationGranularity)
		TEXT_V("Type de processeur: %u", sysInfo.wProcessorLevel)
		TEXT_V("Version du processeur: %u", sysInfo.wProcessorRevision)
	}

	ImGui::NextColumn();

	if (C_HEADER("Informations RAM"))
	{
		MEMORYSTATUSEX mem_info;
		mem_info.dwLength = sizeof(MEMORYSTATUSEX);
		GlobalMemoryStatusEx(&mem_info);
		TEXT_V("Memoire totale: %llu Mo", mem_info.ullTotalPhys / (1024 * 1024))
		TEXT_V("Memoire disponible: %llu Mo", mem_info.ullAvailPhys / (1024 * 1024))
		TEXT_V("Memoire utilisee: %llu Mo", (mem_info.ullTotalPhys - mem_info.ullAvailPhys) / (1024 * 1024))
	}

	ImGui::Columns(1);

    END
}

void imguiManager::UpdateCachedEntitiesIfNeeded() {
	if (!entityListDirty) return;
	cachedEntities_.clear();
	auto entities = entity_manager_->GetAllEntities();
	for (auto& entity : entities) {
		auto identity = entity->GetComponent<ecs::IdentityComponent>();
		if (identity) {
			std::string name = identity->GetName();
			auto id = identity->GetId();
			if (name.empty())
				name = "Entity " + std::to_string(identity->GetId());
			cachedEntities_.push_back({name,id});
		}
	}
	entityListDirty = false;
}