#pragma once
#ifndef _IMGUI_MANAGER_H_
#define _IMGUI_MANAGER_H_

#include "Logger.h"
#include "sceneManager.h"
#include "fps_limiter.h"
#include "macro.h"

#include <imgui.h>
#include <imgui_impl_dx11.h>
#include <imgui_impl_win32.h>
#include <windows.h>
#include <deque>
#include <functional>

#include "render_texture_class.h"
#include "scene_manager.h"
#include "ecs/entity_manager.h"

#include "version.h"

class application_class;
class stats;

/**
 * Struct to hold widget entries for ImGui.
 * This struct contains a pointer to a boolean that determines if the widget should be shown,
 * and a function that will be called when the widget is rendered.
 */
struct widget_entry
{
	bool* show;
	std::function<void()> func;
};

struct ComponentEntry {
	const char* name;
	std::function<void()> addFunc;
};

struct CachedEntity
{
	std::string name;
	int id;
};

class imguiManager
{
public:

	/**
	 * Constructor for imguiManager class.
	 * Initializes the ImGui manager with default values.
	 */
	imguiManager();
	~imguiManager();

	/**
	 * Load the build version from a configuration file.
	 * The configuration file should contain a line with the build version in the format "VER=XXX...".
	 * If the line is not found, it will be added with a version of 0.
	 * The build version will be incremented by 1 each time this function is called.
	 * @param filepath The path to the configuration file.
	 * @return True if the build version was loaded successfully, otherwise false.
	 */
	bool IncrementBuildVersionInConfig(const std::string& filepath);

	/**
	 * Initializes the ImGui manager.
	 * @param hwnd Handle to the window where ImGui will be rendered.
	 * @param device Pointer to the Direct3D 11 device.
	 * @param deviceContext Pointer to the Direct3D 11 device context.
	 * @return True if initialization was successful, otherwise false.
	 */
	bool Initialize(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* deviceContext);
	/**
	 * Shuts down the ImGui manager.
	 */
	void Shutdown();
	/**
	 * Renders the ImGui interface.
	 */
	void Render();
	/**
	 * Starts a new ImGui frame.
	 */
	void NewFrame();
	/**
	 * Sets up the ImGui dockspace.
	 * This function creates a dockspace for the ImGui interface.
	 */
	void SetupDockspace();

	// Widgets
	/**
	 * Creates a slider widget to control the speed of the demo spinning cube.
	 * @param speed 
	 */
	void WidgetSpeedSlider(float* speed);
	/**
	 * Creates a button widget.
	 */
	void WidgetButton();
	/**
	 * Shows the FPS in a widget.
	 */
	void WidgetFPS();
	/**
	 * Create a widget to add a button wich will add an object to the scene.
	 */
	void WidgetAddObject();

	/**
	 * create a window to display the object list and allow to add objects to the scene.
	 */
	void WidgetObjectWindow();
	/**
	 * Create a window to display the terrain generation options.
	 */
	void WidgetTerrainWindow();
	/**
	 * Create a window to display the light settings.
	 */
	void WidgetLightWindow();
	/**
	 * Create a window to display the Engine settings.
	 */
	void WidgetEngineSettingsWindow();
	/**
	 * Create a window to display the scene.
	 * This window isn't used anymore.
	 */
	void WidgetRenderWindow(ImVec2 availableSize);
	/**
	 * Create a window to display the log messages from the Logger.
	 */
	void WidgetLogWindow();
	/**
	 * Create a window to display the stats of the engine.
	 * This includes FPS, draw calls, triangle count, etc.
	 * As well as the GPU, CPU information and RAM information.
	 */
	void WidgetRenderStats();
	/**
	 * Function to render the ImGui widgets.
	 * This function use the struct widget_entry to render the widgets that are registered.
	 * @return True if the ImGui widgets were rendered successfully, otherwise false.
	 */
	bool ImGuiWidgetRenderer();

	/**
	 * Create a window to display the inspector.
	 * This window shows the components of the selected entity and allows to edit them.
	 */
	void WidgetInspectorWindow();

	/**
	 * set the Old scene window size.
	 * @param size 
	 */
	void SetWindowSize(ImVec2 size) { windowSize = size; }
	/**
	 * Get the current window size.
	 * @return The current window size as an ImVec2.
	 */
	ImVec2 GetWindowSize() const { return windowSize; }

	/**
	 * Set the application class pointer for the ImGui manager.
	 * @param app 
	 */
	void SetApp(std::shared_ptr<application_class> app) { app_ = app; }

	// Shader toggles

	bool m_EnableCelShading;

private:

	// --------------------------------------------- //
	// ----------------- Functions ----------------- //
	// --------------------------------------------- //
	/**
	 * Update the cached entity list if there is a new, delete or rename entity
	 */
	void UpdateCachedEntitiesIfNeeded();

	// --------------------------------------------- //
	// ----------------- Variables ----------------- //
	// --------------------------------------------- //

	std::vector<widget_entry> widgets_;

	std::shared_ptr<application_class> app_;
	scene_manager* scene_manager_;
	stats* stats_;
	ecs::EntityManager* entity_manager_;

	std::shared_ptr<ecs::Entity> sky_entity_shared_ptr_;
	
	bool showObjectWindow;
	bool showTerrainWindow;
	bool showLightWindow;
	bool showOldSceneWindow;
	bool showEngineSettingsWindow;
	bool showLogWindow;
	bool showStatsWindow;
	bool show_inspector_window_;

	int m_SideCount = 0;

	static const int FRAME_HISTORY_COUNT = 3600; // 1min secondes à 60 FPS
	float m_frameTimeHistory[FRAME_HISTORY_COUNT] = {};
	int m_frameTimeHistoryIndex = 0;

	bool m_isPhyiscsEnabled = false;
	bool m_isGravityEnabled = false;

	ImGuiIO* io;

	ID3D11Device* m_device;
	ID3D11DeviceContext* m_deviceContext;
	ImVec2 windowSize;

	render_texture_class* m_renderTexture;

	const std::deque<Logger::LogEntry>& logBuffer = Logger::Get().GetLogBuffer();

	int current_fps_, min_fps_, max_fps_, draw_calls_, visible_triangle_count_;
	float current_frame_time_, min_frame_time_, max_frame_time_ ;

	std::shared_ptr<int> total_vertex_count_;
	std::shared_ptr<int> total_triangle_count_;

	// gpu information
	char card_name_[128];
	int video_memory_ = 0;

	// cpu information
	std::string cpu_name_;
	std::string version_driver_;

	ecs::EntityID m_selected_entity_id = 0; // ID of the currently selected entity
	std::vector<CachedEntity> cachedEntities_;
	bool entityListDirty = true;

	int BUILD_VERSION_VER = -1;
};

#endif