#pragma once

#include "ModelPart.h"
#include "util/BoundingBox.h"
#include "util/Vertex.h"
#include "util/VertexList.h"
#include "../systems/Texture.h"
#include "../systems/RenderSystem.h"
#include "../systems/MultiSelectionBoxRenderer.h"

#include <glm/glm.hpp>
#include <glm/mat3x3.hpp>
#include <string>
#include <vector>
#include <memory>
#include <atomic>
#include <map>
#include <functional>

/**
 * @namespace Vivid2D
 * @brief Vivid2D 引擎的核心命名空间，包含了所有与2D渲染和动画相关的类与功能。
 */
namespace Vivid2D {
    // 类型别名，用于简化代码
    using util::VertexList;
    using util::BoundingBox;
    using util::Vertex;
    using Vivid2D::ModelPart;

    /**
     * @class Mesh2D
     * @brief 表示一个二维网格，是场景中可渲染对象的基本构建块。
     *
     * Mesh2D 类管理着顶点数据、纹理坐标、索引、纹理以及渲染状态。
     * 它支持GPU数据上传、绘制、变换、边界框计算和顶点级的精细操作。
     * 同时，它也为变形和动画提供了基础支持。
     */
    class VIVID_2D_MYDLL_API Mesh2D {
    public:
        /**
         * @enum DrawMode
         * @brief 定义了渲染网格时顶点数据的解释方式。
         */
        enum class DrawMode {
            POINTS,        ///< 将每个顶点渲染成一个点。
            LINES,         ///< 每两个顶点构成一条独立的线段。
            LINE_STRIP,    ///< 顶点按顺序连接成一条连续的线。
            TRIANGLES,     ///< 每三个顶点构成一个独立的三角形。
            TRIANGLE_STRIP,///< 顶点按顺序构成一串相连的三角形。
            TRIANGLE_FAN   ///< 顶点以扇形方式构成一串共享中心的三角形。
        };

        // ==================== 构造/析构/拷贝/移动 ====================

        /**
         * @brief 默认构造函数。
         * @param name 网格的名称，默认为 "unnamed"。
         */
        explicit Mesh2D(std::string name = "unnamed");

        /**
         * @brief 使用顶点、UV和索引数据构造一个新的 Mesh2D 对象。
         * @param name 网格的名称。
         * @param vertices 顶点位置数据 (x, y, z, ...)。
         * @param uvs 纹理坐标数据 (u, v, ...)。
         * @param indices 定义顶点连接关系的索引数据。
         */
        Mesh2D(std::string name, const std::vector<float>& vertices, const std::vector<float>& uvs, const std::vector<int>& indices);

        /**
         * @brief 析构函数，负责清理GPU资源。
         */
        ~Mesh2D();

        /**
         * @brief 拷贝构造函数。
         * @param other 要拷贝的另一个 Mesh2D 对象。
         */
        Mesh2D(const Mesh2D& other);

        /**
         * @brief 拷贝赋值运算符。
         * @param other 要赋值的另一个 Mesh2D 对象。
         * @return 当前对象的引用。
         */
        Mesh2D& operator=(const Mesh2D& other);

        /**
         * @brief 移动构造函数。
         * @param other 用于移动的另一个 Mesh2D 对象。
         */
        Mesh2D(Mesh2D&& other) noexcept;

        /**
         * @brief 移动赋值运算符。
         * @param other 用于移动赋值的另一个 Mesh2D 对象。
         * @return 当前对象的引用。
         */
        Mesh2D& operator=(Mesh2D&& other) noexcept;

        // ==================== 变形引擎 ====================

        /**
         * @brief 在指定的世界坐标位置添加一个控制点（顶点）。
         * @param x 新控制点的 x 坐标。
         * @param y 新控制点的 y 坐标。
         * @return 指向新创建的顶点的指针。
         */
        Vertex* addControlPointAt(float x, float y);

        /**
         * @brief 设置用于网格变形的控制顶点列表。
         * @param controlVertices 一组指向用作控制点的顶点的指针。
         */
        void setDeformationControlVertices(const std::vector<Vertex*>& controlVertices);

        /**
         * @brief 应用一个局部的推力效果来变形网格。
         * @param baseVertexState 变形前的顶点状态快照。
         * @param pushCenterWorld 推力中心的世界坐标。
         * @param delta 推力的位移向量。
         * @param radius 推力影响的半径范围。
         */
        void applyLocalizedPush(const std::vector<Vertex>& baseVertexState, const glm::vec2& pushCenterWorld, const glm::vec2& delta, float radius);

        // ==================== 网格数据设置 ====================

        /**
         * @brief 一次性设置网格的所有核心数据。
         * @param vertices 顶点位置数据。
         * @param uvs 纹理坐标数据。
         * @param indices 索引数据。
         */
        void setMeshData(const std::vector<float>& vertices, const std::vector<float>& uvs, const std::vector<int>& indices);

        /**
         * @brief 设置网格的顶点数据。
         * @param vertices 新的顶点位置数据。
         * @param updateOriginal 如果为 true，则同时更新原始顶点状态（用于 `resetToOriginal`）。
         */
        void setVertices(const std::vector<float>& vertices, bool updateOriginal = false);

        /**
         * @brief 设置网格要使用的纹理。
         * @param texture 指向纹理对象的共享指针。
         */
        void setTexture(std::shared_ptr<Render::Texture::Texture> texture);

        /**
         * @brief 将此网格关联到一个 ModelPart。
         * @param modelPart 指向所属 ModelPart 的指针。
         */
        void setModelPart(ModelPart* modelPart);

        /**
         * @brief 设置网格的轴心点（变换中心）。
         * @param pivot 新的轴心点坐标。
         */
        void setPivot(const glm::vec2& pivot);

        /**
         * @brief 移动网格的轴心点。
         * @param dx 在 x 轴上的移动量。
         * @param dy 在 y 轴上的移动量。
         */
        void movePivot(float dx, float dy);

        // ==================== 顶点操作 ====================

        /**
         * @brief 在顶点列表末尾添加一个新顶点。
         * @param vertex 要添加的顶点。
         */
        void addVertex(const Vertex& vertex);

        /**
         * @brief 在指定索引位置插入一个顶点。
         * @param index 插入位置的索引。
         * @param vertex 要插入的顶点。
         */
        void insertVertex(int index, const Vertex& vertex);

        /**
         * @brief 移除指定索引位置的顶点。
         * @param index 要移除的顶点的索引。
         */
        void removeVertex(int index);

        /**
         * @brief 移除一个指定的顶点并重新映射索引以维持网格的完整性。
         * @param vertexToRemove 指向要移除的顶点的指针。
         */
        void removeVertexAndRemapIndices(Vertex* vertexToRemove);

        /**
         * @brief 通过名称查找顶点。
         * @param name 要查找的顶点的名称。
         * @return 如果找到，返回指向该顶点的指针；否则返回 nullptr。
         */
        Vertex* findVertexByName(const std::string& name);

        /**
         * @brief 通过名称查找顶点 (const 版本)。
         * @param name 要查找的顶点的名称。
         * @return 如果找到，返回指向该顶点的常量指针；否则返回 nullptr。
         */
        const Vertex* findVertexByName(const std::string& name) const;

        // ==================== 变换与状态 ====================

        /**
         * @brief 将所有顶点的位置重置到其原始保存的状态。
         */
        void resetToOriginal();

        /**
         * @brief 将当前的顶点状态保存为原始状态。
         */
        void saveAsOriginal();

        /**
         * @brief 对所有顶点应用一个变换函数。
         * @param transformer 一个函数，接收顶点引用和索引作为参数，并直接修改顶点位置。
         */
        void transformVertices(const std::function<void(glm::vec2&, int)>& transformer);

        /**
         * @brief 标记网格数据已更改，需要在下一帧重新上传到GPU。
         */
        void markDirty();

        /**
         * @brief 检查网格数据是否已被标记为“脏”。
         * @return 如果网格数据已更改且需要更新GPU，则为 true。
         */
        bool isDirty() const;

        // ==================== 边界框与碰撞检测 ====================

        /**
         * @brief 更新网格的包围盒。如果边界框数据是旧的，则会重新计算。
         */
        void updateBounds() const;

        /**
         * @brief 获取网格的轴对齐包围盒 (AABB)。
         * @return 对包围盒的常量引用。
         */
        const util::BoundingBox& getBounds() const;

        /**
         * @brief 检查一个点是否包含在网格内。
         * @param x 点的 x 坐标。
         * @param y 点的 y 坐标。
         * @param precise 如果为 true，则执行精确的基于几何的检测；否则使用快速的边界框检测。
         * @return 如果点在网格内，则为 true。
         */
        bool containsPoint(float x, float y, bool precise = false) const;

        /**
         * @brief 检查一个点是否包含在网格内。
         * @param point 要检查的点。
         * @param precise 如果为 true，则执行精确的基于几何的检测；否则使用快速的边界框检测。
         * @return 如果点在网格内，则为 true。
         */
        bool containsPoint(const glm::vec2& point, bool precise = false) const;

        // ==================== 渲染 ====================

        /**
         * @brief 将网格数据（顶点、UV、索引）上传到GPU的VBO和EBO。
         */
        void uploadToGPU();

        /**
         * @brief 从GPU删除与此网格相关的缓冲对象 (VAO, VBO, EBO)。
         */
        void deleteGPU();

        /**
         * @brief 使用指定的着色器和模型矩阵来渲染网格。
         * @param shaderProgram 要使用的着色器程序的ID。
         * @param modelMatrix 应用于网格的模型变换矩阵。
         */
        void draw(int shaderProgram, const glm::mat3& modelMatrix);

        // ==================== Getters & Setters ====================

        /** @brief 获取网格的名称。 */
        const std::string& getName() const;
        /** @brief 设置网格的名称。 */
        void setName(const std::string& name);
        /** @brief 获取当前顶点位置数据。 */
        std::vector<float> getVertices() const;
        /** @brief 获取当前纹理坐标数据。 */
        std::vector<float> getUVs() const;
        /** @brief 获取原始（未变形）的顶点位置数据。 */
        std::vector<float> getOriginalVertices() const;
        /** @brief 获取索引数据。 */
        std::vector<int> getIndices() const;
        /** @brief 获取顶点总数。 */
        int getVertexCount() const;
        /** @brief 通过索引获取一个可修改的顶点。 */
        Vertex* getVertex(int index);
        /** @brief 通过索引获取一个不可修改的顶点。 */
        const Vertex* getVertex(int index) const;
        /** @brief 获取当前轴心点。 */
        const glm::vec2& getPivot() const;
        /** @brief 获取原始轴心点。 */
        const glm::vec2& getOriginalPivot() const;
        /** @brief 检查网格是否可见。 */
        bool isVisible() const;
        /** @brief 设置网格的可见性。 */
        void setVisible(bool visible);
        /** @brief 获取当前的绘制模式。 */
        DrawMode getDrawMode() const;
        /** @brief 设置绘制模式。 */
        void setDrawMode(DrawMode mode);
        /** @brief 检查网格是否被选中。 */
        bool isSelected() const;
        /** @brief 设置网格的选中状态。 */
        void setSelected(bool selected);
        /** @brief 获取此网格所属的 ModelPart。 */
        ModelPart* getModelPart() const;
        /** @brief 获取当前活动的顶点列表。 */
        VertexList* getActiveVertexList() const;
        /** @brief 获取用于变形的控制顶点列表。 */
        const std::vector<Vertex*>& getDeformationControlVertices() const;

        // ==================== 静态工厂方法 ====================

        /**
         * @brief 创建一个标准的矩形网格。
         * @param name 网格的名称。
         * @param width 矩形的宽度。
         * @param height 矩形的高度。
         * @return 一个指向新创建的 Mesh2D 的唯一指针。
         */
        static std::unique_ptr<Mesh2D> createQuad(const std::string& name, float width, float height);

        /**
         * @brief 创建一个圆形的网格。
         * @param name 网格的名称。
         * @param radius 圆的半径。
         * @param segments 用于近似圆的线段（三角形）数量。
         * @return 一个指向新创建的 Mesh2D 的唯一指针。
         */
        static std::unique_ptr<Mesh2D> createCircle(const std::string& name, float radius, int segments);

    private:
        // ==================== 私有成员变量 ====================
        std::string m_Name;                                      ///< 网格的名称。
        std::unique_ptr<VertexList> m_ActiveVertexList;          ///< 管理活动顶点数据的列表。
        ModelPart* m_ModelPart = nullptr;                        ///< 指向所属 ModelPart 的指针。
        std::shared_ptr<Render::Texture::Texture> m_Texture;     ///< 网格使用的纹理。
        bool m_IsVisible = true;                                 ///< 网格是否可见。
        DrawMode m_DrawMode = DrawMode::TRIANGLES;               ///< 当前的渲染模式。
        GLuint m_VaoID = 0, m_VboID = 0, m_EboID = 0;             ///< OpenGL 缓冲对象 ID。
        int m_IndexCount = 0;                                    ///< 索引的数量。
        bool m_IsUploaded = false;                               ///< 数据是否已上传到GPU。
        bool m_IsDirty = true;                                   ///< 数据是否需要重新上传到GPU。

        mutable util::BoundingBox m_Bounds;                      ///< 网格的包围盒 (mutable 以便在 const 方法中更新)。
        mutable bool m_BoundsDirty = true;                       ///< 包围盒是否需要重新计算 (mutable)。

        std::atomic<bool> m_IsSelected{ false };                 ///< 网格是否被选中 (原子变量，保证线程安全)。
        glm::vec2 m_Pivot{ 0.0f, 0.0f };                         ///< 当前的轴心点。
        glm::vec2 m_OriginalPivot{ 0.0f, 0.0f };                 ///< 用于重置的原始轴心点。
        std::vector<Vertex*> m_DeformationControlVertices;       ///< 用于变形的控制顶点列表。

        // ==================== 私有辅助方法 ====================

        /**
         * @brief 执行精确的几何测试，判断点是否在网格的三角形内。
         * @param x 点的 x 坐标。
         * @param y 点的 y 坐标。
         * @return 如果点在网格的几何形状内，则为 true。
         */
        bool isPointInMeshGeometry(float x, float y) const;

        /**
         * @brief 绘制网格被选中时的高亮效果（如顶点和边框）。
         * @param modelMatrix 网格的模型矩阵。
         */
        void drawSelectionHighlights(const glm::mat3& modelMatrix);

        /**
         * @brief 从另一个 Mesh2D 对象拷贝数据。
         * @param other 源对象。
         */
        void copyFrom(const Mesh2D& other);

        /**
         * @brief 在插入一个顶点后，更新索引数据以维持拓扑结构。
         * @param insertedIndex 新插入顶点的索引。
         */
        void updateIndicesAfterVertexInsertion(int insertedIndex);

        /**
         * @brief 在移除一个顶点后，更新索引数据。
         * @param removedIndex 被移除顶点的索引。
         */
        void updateIndicesAfterVertexRemoval(int removedIndex);

        /**
         * @brief 当顶点数量改变时，重新生成索引数据。
         * @param newVertexCount 新的顶点总数。
         */
        void regenerateIndicesForNewVertexCount(int newVertexCount);
    };

} // namespace Vivid2D