#pragma once

#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <functional>
#include <queue>
#include <mutex>
#include <thread>
#include <stack>
#include <string>
#include <memory>
#include <atomic>

namespace Vivid2D::Render::Texture
{
	class Texture;
}

// 向前声明 spdlog::logger 以避免在头文件中包含其完整定义
namespace spdlog {
	class logger;
}

/**
 * @class RenderSystem
 * @brief 提供一个线程安全的、基于命令队列的 OpenGL API 封装层。
 * @details
 * 这是一个完全静态的类，旨在将来自任何线程的渲染调用排队，并在一个专用的渲染线程上执行它们。
 * 这样做可以确保所有 OpenGL 操作的线程安全性。它还提供了对 OpenGL 状态、着色器、缓冲区和纹理的
 * 高级管理功能，以及一个用于调试的错误检查机制。
 * 使用方法：在主线程或其他逻辑线程中调用公开的静态方法（如 RenderSystem::drawElements），
 * 这些方法会将渲染命令记录到队列中。在渲染线程的循环中，调用 RenderSystem::replayQueue()
 * 来执行所有排队的命令。
 */
class VIVID_2D_MYDLL_API RenderSystem {
public:
	// RenderSystem 是一个静态工具类，因此禁止实例化。
	RenderSystem() = delete;
	~RenderSystem() = delete;
	RenderSystem(const RenderSystem&) = delete;
	RenderSystem& operator=(const RenderSystem&) = delete;
	using GLADloader = void* (*)(const char* name);

	// ================== 日志系统管理 ==================
	/**
	 * @brief 初始化日志系统。必须在任何日志调用之前，在主函数开始时调用。
	 */
	static void InitializeLogging();

	/**
	 * @brief 关闭日志系统。应在主函数结束时调用以确保所有日志都被刷新。
	 */
	static void ShutdownLogging();

	// ================== 初始化与线程管理 ==================
	/**
	 * @brief 初始化并启动专用的渲染线程。
	 */
	static void initRenderThread();

	/**
	 * @brief 开始渲染系统的初始化阶段。在此阶段，调用可以直接在调用线程上执行。
	 */
	static void beginInitialization();

	/**
	 * @brief 结束渲染系统的初始化阶段。此后，所有调用都将被排队。
	 */
	static void finishInitialization();

	/**
	 * @brief 检查当前线程是否是渲染线程。
	 * @return 如果是渲染线程，则返回 true。
	 */
	static bool isOnRenderThread();

	/**
	 * @brief 检查当前是否处于初始化阶段。
	 * @return 如果是初始化阶段，则返回 true。
	 */
	static bool isInInitPhase();

	/**
	 * @brief 断言当前线程必须是渲染线程，否则程序将中止。
	 */
	static void assertOnRenderThread();

	/**
	 * @brief 断言当前线程必须是渲染线程或处于初始化阶段。
	 */
	static void assertOnRenderThreadOrInit();

	/**
	 * @brief 使用 GLAD 加载 OpenGL 函数指针。
	 * @param loader 一个函数指针，用于获取 OpenGL 函数的地址（例如 glfwGetProcAddress）。
	 */
	static void loadGLFunctions(GLADloader loader);

	// ================== 渲染命令队列 ==================
	/**
	 * @brief 将一个渲染调用（作为 lambda 函数）记录到命令队列中，以便稍后在渲染线程上执行。
	 * @param renderCall 要执行的渲染操作。
	 */
	static void recordRenderCall(std::function<void()>&& renderCall);

	/**
	 * @brief 在渲染线程上执行命令队列中的所有待处理渲染调用。
	 */
	static void replayQueue();

	// ================== OpenGL 状态管理封装 ==================
	/**
	 * @brief 启用一个 OpenGL 功能（例如 GL_BLEND）。
	 * @param capability 要启用的 OpenGL 功能枚举。
	 */
	static void enable(GLenum capability);

	/**
	 * @brief 禁用一个 OpenGL 功能。
	 * @param capability 要禁用的 OpenGL 功能枚举。
	 */
	static void disable(GLenum capability);

	/**
	 * @brief 将当前的渲染状态（如着色器、混合、视口等）推入状态堆栈。
	 */
	static void pushState();

	/**
	 * @brief 从状态堆栈中弹出并恢复上一个渲染状态。
	 */
	static void popState();

	/**
	 * @brief 获取当前状态堆栈的大小。
	 * @return 堆栈中的状态数量。
	 */
	static size_t getStateStackSize();

	/**
	 * @brief 设置清屏颜色。
	 */
	static void clearColor(float r, float g, float b, float a);

	/**
	 * @brief 清除指定的缓冲区（例如 GL_COLOR_BUFFER_BIT）。
	 * @param mask 要清除的缓冲区的位域掩码。
	 */
	static void clear(GLbitfield mask);

	/**
	 * @brief 设置 OpenGL 视口。
	 */
	static void viewport(int x, int y, int width, int height);

	// ================== VAO / VBO / EBO 操作 ==================
	/**
	 * @brief 生成一个顶点数组对象 (VAO)。
	 * @return 新创建的 VAO 的句柄。
	 */
	static GLuint GenVertexArrays();

	/**
	 * @brief 删除一个 VAO。
	 * @param vao 要删除的 VAO 的句柄。
	 */
	static void DeleteVertexArrays(GLuint vao);

	/**
	 * @brief 绑定一个 VAO。
	 * @param vao 要绑定的 VAO 的句柄。
	 */
	static void BindVertexArray(GLuint vao);

	/**
	 * @brief 生成一个缓冲区对象 (VBO/EBO)。
	 * @return 新创建的缓冲区的句柄。
	 */
	static GLuint GenBuffers();

	/**
	 * @brief 删除一个缓冲区对象。
	 * @param buffer 要删除的缓冲区的句柄。
	 */
	static void DeleteBuffers(GLuint buffer);

	/**
	 * @brief 绑定一个缓冲区对象。
	 * @param target 缓冲区的目标 (例如 GL_ARRAY_BUFFER)。
	 * @param buffer 要绑定的缓冲区的句柄。
	 */
	static void BindBuffer(GLenum target, GLuint buffer);

	/**
	 * @brief 将数据上传到当前绑定的缓冲区。
	 * @param target 缓冲区目标。
	 * @param size 数据的大小（以字节为单位）。
	 * @param data 指向数据的指针。
	 * @param usage 数据的预期用途 (例如 GL_STATIC_DRAW)。
	 */
	static void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);

	// ================== Uniform 设置 ==================
	static void uniform1i(GLint location, int value);
	static void uniform1f(GLint location, float value);
	static void uniform2f(GLint location, float x, float y);
	static void uniform3f(GLint location, float x, float y, float z);
	static void uniform4f(GLint location, float x, float y, float z, float w);
	static void uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
	static void uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
	static void uniform2f(GLint location, const glm::vec2& vec);
	static void uniform3f(GLint location, const glm::vec3& vec);
	static void uniform4f(GLint location, const glm::vec4& vec);
	static void uniformMatrix3(GLint location, const glm::mat3& matrix, bool transpose = false);
	static void uniformMatrix4(GLint location, const glm::mat4& matrix, bool transpose = false);

	/**
	 * @brief 获取着色器程序中 uniform 变量的位置。
	 * @param program 着色器程序的句柄。
	 * @param name uniform 变量的名称。
	 * @return uniform 变量的位置。
	 */
	static GLint getUniformLocation(GLuint program, const std::string& name);

	// ================== 绘制命令 ==================
	/**
	 * @brief 设置线宽。
	 */
	static void lineWidth(float width);

	/**
	 * @brief 使用索引进行绘制。
	 */
	static void drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);

	/**
	 * @brief 直接使用顶点数组进行绘制。
	 */
	static void drawArrays(GLenum mode, GLint first, GLsizei count);

	// ================== 混合模式 ==================
	/**
	 * @brief 启用混合。
	 */
	static void enableBlend();

	/**
	 * @brief 禁用混合。
	 */
	static void disableBlend();

	/**
	 * @brief 设置混合函数。
	 */
	static void blendFunc(GLenum sfactor, GLenum dfactor);

	/**
	 * @brief 设置默认的 Alpha 混合函数 (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)。
	 */
	static void defaultBlendFunc();

	// ================== 着色器程序管理 ==================
	/**
	 * @brief 创建一个空的着色器程序。
	 * @return 新程序的句柄。
	 */
	static GLuint createProgram();

	/**
	 * @brief 获取当前绑定的着色器程序。
	 * @return 当前程序的句柄。
	 */
	static GLint getCurrentProgram();

	/**
	 * @brief 将一个着色器附加到着色器程序。
	 */
	static void attachShader(GLuint program, GLuint shader);

	/**
	 * @brief 链接一个着色器程序。
	 */
	static void linkProgram(GLuint program);

	/**
	 * @brief 获取着色器程序的参数。
	 */
	static GLint getProgrami(GLuint program, GLenum pname);

	/**
	 * @brief 获取着色器程序的日志信息。
	 */
	static std::string getProgramInfoLog(GLuint program);

	/**
	 * @brief 从程序中分离一个着色器。
	 */
	static void detachShader(GLuint program, GLuint shader);

	/**
	 * @brief 删除一个着色器程序。
	 */
	static void deleteProgram(GLuint program);

	/**
	 * @brief 链接顶点和片段着色器以创建一个完整的程序。
	 */
	static GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);

	/**
	 * @brief 链接顶点、几何和片段着色器以创建一个完整的程序。
	 */
	static GLuint linkProgram(GLuint vertexShader, GLuint geometryShader, GLuint fragmentShader);

	/**
	 * @brief 激活一个着色器程序。
	 */
	static void useProgram(GLuint program);

	// ================== 着色器管理 ==================
	/**
	 * @brief 创建一个指定类型的着色器对象。
	 * @param type 着色器类型 (例如 GL_VERTEX_SHADER)。
	 * @return 新着色器对象的句柄。
	 */
	static GLuint createShader(GLenum type);

	/**
	 * @brief 为着色器设置源代码。
	 */
	static void shaderSource(GLuint shader, const std::string& source);

	/**
	 * @brief 编译一个着色器。
	 */
	static void compileShader(GLuint shader);

	/**
	 * @brief 获取着色器的参数。
	 */
	static GLint getShaderi(GLuint shader, GLenum pname);

	/**
	 * @brief 获取着色器的日志信息。
	 */
	static std::string getShaderInfoLog(GLuint shader);

	/**
	 * @brief 删除一个着色器。
	 */
	static void deleteShader(GLuint shader);

	/**
	 * @brief 从源代码编译一个指定类型的着色器。
	 * @return 编译后的着色器句柄。
	 */
	static GLuint compileShader(GLenum type, const std::string& source);

	// ================== 深度测试 ==================
	static void depthFunc(GLenum func);
	static void depthMask(bool flag);
	static void clearDepth(double depth);
	static void enableDepthTest();
	static void disableDepthTest();

	// ================== 纹理管理 ==================
	/**
	 * @brief 绑定一个 2D 纹理到当前活动的纹理单元。
	 * @param texture 要绑定的纹理的句柄。
	 */
	static void bindTexture(GLuint texture);

	/**
	* @brief 绑定一个 Texture 对象到指定的纹理单元。
	* @details 这是一个便利方法，它将激活纹理单元和绑定纹理两个操作合并为一个命令，
	*          并安全地提交到渲染线程。
	* @param texture 要绑定的 Texture 对象的常量引用。
	* @param textureUnit 要绑定的纹理单元索引 (0, 1, 2, ...)。
	*/
	static void bindTexture(const Vivid2D::Render::Texture::Texture& texture, int textureUnit = 0);
	static void getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels);
	/**
	 * @brief 激活一个纹理单元。
	 * @param texture 要激活的纹理单元 (例如 GL_TEXTURE0)。
	 */
	static void activeTexture(GLenum texture);
	/**
	 * @brief 生成一个纹理对象。
	 * @return 新纹理的句柄。
	 */
	static GLuint genTextures();

	/**
	 * @brief 删除一个纹理对象。
	 * @param texture 要删除的纹理的句柄。
	 */
	static void deleteTextures(GLuint texture);
	static void texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
	static void texParameteri(GLenum target, GLenum pname, GLint param);
	static void setTextureMinFilter(GLenum filter);
	static void setTextureMagFilter(GLenum filter);
	static void setTextureWrapS(GLenum wrap);
	static void setTextureWrapT(GLenum wrap);

	/**
	 * @brief 创建一个默认的 1x1 白色纹理。
	 * @return 默认纹理的句柄。
	 */
	static GLuint createDefaultTexture();

	// ================== 顶点属性 ==================
	static void enableVertexAttribArray(GLuint index);
	static void disableVertexAttribArray(GLuint index);
	static void vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);

	// ================== 工具方法 ==================
	static void readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels);
	/**
	 * @brief 检查是否支持某个 OpenGL 扩展。
	 */
	static bool isExtensionSupported(const std::string& extension);
	static void pixelStore(GLenum pname, GLint param);
	static std::string getVendor();
	static std::string getRenderer();
	static std::string getOpenGLVersion();
	static std::string getGLSLVersion();

	/**
	 * @brief 记录详细的 OpenGL 和驱动信息。
	 */
	static void logDetailedGLInfo();

	/**
	 * @brief 设置一套默认的 OpenGL 状态（例如启用混合，设置清屏色）。
	 */
	static void setupDefaultState();

	/**
	 * @brief 检查并记录任何发生的 OpenGL 错误。
	 * @param operation 导致检查的操作的描述性名称。
	 */
	static void checkGLError(const std::string& operation);

	// ================== 获取状态 ==================
	static int getViewportWidth();
	static int getViewportHeight();
	static glm::vec4 getClearColor();

	/**
	 * @brief 获取当前渲染命令队列中的命令数量。
	 * @return 队列大小。
	 */
	static size_t getQueueSize();

private:
	struct RenderState;
	// 私有的、直接执行GL调用的版本
	static void _enable(GLenum capability);
	static void _disable(GLenum capability);
	static void _pushState();
	static void _popState();
	static void _clearColor(float r, float g, float b, float a);
	static void _clear(GLbitfield mask);
	static void _viewport(int x, int y, int width, int height);
	static void _glDeleteVertexArrays(GLuint vao);
	static void _uniform1i(GLint location, int value);
	static void _uniform1f(GLint location, float value);
	static void _uniform2f(GLint location, float x, float y);
	static void _uniform3f(GLint location, float x, float y, float z);
	static void _uniform4f(GLint location, float x, float y, float z, float w);
	static void _uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
	static void _uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
	static void _lineWidth(float width);
	static void _drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
	static void _drawArrays(GLenum mode, GLint first, GLsizei count);
	static void _glBindVertexArray(GLuint vao);
	static void _enableBlend();
	static void _disableBlend();
	static void _blendFunc(GLenum sfactor, GLenum dfactor);
	static void _attachShader(GLuint program, GLuint shader);
	static void _linkProgram(GLuint program);
	static void _detachShader(GLuint program, GLuint shader);
	static void _deleteProgram(GLuint program);
	static void _useProgram(GLuint program);
	static void _shaderSource(GLuint shader, const std::string& source);
	static void _compileShader(GLuint shader);
	static void _deleteShader(GLuint shader);
	static void _depthFunc(GLenum func);
	static void _depthMask(bool flag);
	static void _clearDepth(double depth);
	static void _enableDepthTest();
	static void _disableDepthTest();
	static void _bindTexture(GLuint texture);
	static void _getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels);
	static void _activeTexture(GLenum texture);
	static void _deleteTextures(GLuint texture);
	static void _texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
	static void _texParameteri(GLenum target, GLenum pname, GLint param);
	static void _glBindBuffer(GLenum target, GLuint buffer);
	static void _glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
	static void _glDeleteBuffers(GLuint buffer);
	static void _enableVertexAttribArray(GLuint index);
	static void _disableVertexAttribArray(GLuint index);
	static void _vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
	static void _readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels);
	static void _pixelStore(GLenum pname, GLint param);
	static void _checkGLError(const std::string& operation);


	static std::string getGLErrorString(GLenum error);

	// 静态成员变量
	static std::thread::id s_RenderThreadId;
	static std::atomic<bool> s_IsInInit;
	static std::queue<std::function<void()>> s_RenderQueue;
	static std::mutex s_RenderQueueMutex;
	static std::atomic<bool> s_IsReplayingQueue;

	// ================== RenderState 结构体和实现 ==================
	struct RenderState {
		GLint currentProgram = 0;
		GLboolean blendEnabled = GL_FALSE;
		GLboolean depthTestEnabled = GL_FALSE;
		GLint blendSrcFactor = GL_SRC_ALPHA;
		GLint blendDstFactor = GL_ONE_MINUS_SRC_ALPHA;
		GLint activeTexture = GL_TEXTURE0;
		GLint boundTexture = 0;
		glm::vec4 clearColor{ 0.0f, 0.0f, 0.0f, 1.0f };
		glm::ivec4 viewport{ 0, 0, 800, 600 };


		RenderState() {
			glGetIntegerv(GL_CURRENT_PROGRAM, &currentProgram);
			blendEnabled = glIsEnabled(GL_BLEND);
			depthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
			glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcFactor);
			glGetIntegerv(GL_BLEND_DST_RGB, &blendDstFactor);
			glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
			glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
			glGetFloatv(GL_COLOR_CLEAR_VALUE, &clearColor[0]);
			glGetIntegerv(GL_VIEWPORT, &viewport[0]);
			while (glGetError() != GL_NO_ERROR) {}
		}

		void restore() {
			glViewport(viewport.x, viewport.y, viewport.z, viewport.w);
			glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
			glUseProgram(currentProgram);
			glActiveTexture(activeTexture);
			glBindTexture(GL_TEXTURE_2D, boundTexture);
			if (blendEnabled) glEnable(GL_BLEND); else glDisable(GL_BLEND);
			glBlendFunc(blendSrcFactor, blendDstFactor);
			if (depthTestEnabled) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
			while (glGetError() != GL_NO_ERROR) {}
		}

	};

	static std::stack<RenderState> s_StateStack;

	static int s_ViewportWidth;
	static int s_ViewportHeight;
	static glm::vec4 s_ClearColorValue;

	static std::shared_ptr<spdlog::logger> s_Logger;

};