#pragma once

#include "AnimationParameter.h"
#include <glm/glm.hpp>
#include <glm/gtx/matrix_transform_2d.hpp> // For glm::translate, glm::rotate, glm::scale
#include <glm/mat3x3.hpp>
#include <string>
#include <vector>
#include <memory>
#include <map>

// 前置声明
namespace Vivid2D {
    class Mesh2D;
    namespace util {
        class BoundingBox;
    }
    /**
     * @class ModelPart
     * @brief 表示 2D 模型的一个可动部件，是构成模型层级结构的基本单元。
     * @details
     * 每个 ModelPart 都有自己的变换（位置、旋转、缩放）、渲染属性（可见性、混合模式、不透明度）
     * 以及可以附加的网格。它通过父子关系组织成树状结构，从而实现复杂的层级动画。
     * 变换是相对于父部件的，最终会计算出一个世界变换矩阵来确定其在场景中的最终位置。
     */
    class VIVID_2D_MYDLL_API ModelPart {
    public:
        /**
         * @enum BlendMode
         * @brief 定义了部件渲染时使用的混合模式。
         */
        enum class BlendMode {
            NORMAL,   //!< 正常混合，Alpha 混合
            ADDITIVE, //!< 加法混合，常用于发光效果
            MULTIPLY, //!< 正片叠底，常用于阴影或着色
            SCREEN    //!< 滤色混合，与正片叠底效果相反
        };

        // ==================== 构造函数 ====================
        /**
         * @brief 构造函数，创建一个具有指定名称的模型部件。
         * @param name 部件的名称，默认为 "unnamed"。
         */
        explicit ModelPart(std::string name = "unnamed");

        /**
         * @brief 拷贝构造函数，执行深拷贝。
         * @details
         * 创建一个与 `other` 完全相同的新 ModelPart，包括其所有子部件、网格和参数。
         * 父子关系链也会被完整地复制。
         * @param other 要拷贝的源 ModelPart 对象。
         */
        ModelPart(const ModelPart& other);

        /**
         * @brief 拷贝赋值运算符，执行深拷贝。
         * @param other 要拷贝的源 ModelPart 对象。
         * @return 当前对象的引用。
         */
        ModelPart& operator=(const ModelPart& other);

        /**
         * @brief 移动构造函数。
         * @param other 用于移动的右值引用 ModelPart 对象。
         */
        ModelPart(ModelPart&& other) noexcept;

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


        // ==================== 层级管理 ====================
        /**
         * @brief 添加一个子部件。
         * @details
         * 将 `child` 添加到当前部件的子节点列表中，并自动设置 `child` 的父节点为当前部件。
         * `child` 的所有权将转移给当前部件。
         * @param child 要添加的子部件的 unique_ptr。
         */
        void addChild(std::unique_ptr<ModelPart> child);

        /**
         * @brief 移除一个子部件。
         * @details
         * 从子节点列表中移除指定的 `child` 部件，并返回其所有权给调用者。
         * 如果 `child` 不是当前部件的直接子节点，则不执行任何操作并返回空的 unique_ptr。
         * @param child 要移除的子部件的裸指针。
         * @return 被移除的子部件的 unique_ptr，如果未找到则为空。
         */
        std::unique_ptr<ModelPart> removeChild(ModelPart* child);

        /**
         * @brief 获取所有直接子部件的列表。
         * @return 一个包含所有子部件 unique_ptr 的常量引用向量。
         */
        const std::vector<std::unique_ptr<ModelPart>>& getChildren() const;

        /**
         * @brief 在直接子部件中按名称查找一个部件。
         * @param name 要查找的部件名称。
         * @return 如果找到，返回指向该部件的指针；否则返回 nullptr。
         */
        ModelPart* findChild(const std::string& name);

        /**
         * @brief 递归地在所有后代部件中按名称查找一个部件。
         * @param name 要查找的部件名称。
         * @return 如果找到，返回指向该部件的指针；否则返回 nullptr。
         */
        ModelPart* findChildRecursive(const std::string& name);

        // ==================== 变换系统 ====================
        /**
         * @brief 设置部件相对于其父部件的位置。
         * @param pos 新的局部位置坐标。
         */
        void setPosition(const glm::vec2& pos);

        /**
         * @brief 设置部件相对于其父部件的旋转。
         * @param radians 新的旋转角度（以弧度为单位）。
         */
        void setRotation(float radians);

        /**
         * @brief 设置部件的旋转和缩放中心点（轴心）。
         * @param pivot 新的轴心坐标（在部件的局部坐标系中定义）。
         */
        void setPivot(const glm::vec2& pivot);

        /**
         * @brief 设置部件相对于其父部件的缩放。
         * @param scale 新的缩放因子（x 和 y 方向）。
         */
        void setScale(const glm::vec2& scale);

        /**
         * @brief 在当前位置上应用一个位移。
         * @param delta 位移向量。
         */
        void translate(const glm::vec2& delta);

        /**
         * @brief 在当前旋转角度上增加一个旋转量。
         * @param deltaRadians 要增加的旋转角度（以弧度为单位）。
         */
        void rotate(float deltaRadians);

        /**
         * @brief 在当前缩放上应用一个缩放因子。
         * @param factor 缩放乘数。
         */
        void scale(const glm::vec2& factor);

        /**
         * @brief 递归地重新计算当前部件及其所有子部件的世界变换矩阵。
         * @details 当父部件的变换发生改变时，需要调用此方法来更新所有子孙部件的最终世界位置。
         */
        void recomputeWorldTransformRecursive();

        /**
         * @brief 使用当前的世界变换矩阵更新所有关联网格的顶点位置。
         * @details 此方法通常在渲染前调用，以确保网格在屏幕上显示在正确的位置。
         */
        void updateMeshVertices();

        // ==================== 网格管理 ====================
        /**
         * @brief 向此部件添加一个网格。
         * @param mesh 要添加的网格的 unique_ptr，所有权将转移给 ModelPart。
         */
        void addMesh(std::unique_ptr<Mesh2D> mesh);

        /**
         * @brief 从此部件移除一个网格。
         * @param mesh 要移除的网格的裸指针。
         * @return 如果成功移除返回 true，否则返回 false。
         */
        bool removeMesh(Mesh2D* mesh);

        /**
         * @brief 获取附加到此部件的所有网格的列表。
         * @return 一个包含所有网格 unique_ptr 的常量引用向量。
         */
        const std::vector<std::unique_ptr<Mesh2D>>& getMeshes() const;

        // ==================== 工具方法 ====================
        /**
         * @brief 将一个局部坐标点转换为世界坐标。
         * @param localPoint 在此部件局部坐标系中的点。
         * @return 对应的世界坐标系中的点。
         */
        glm::vec2 localToWorld(const glm::vec2& localPoint) const;

        /**
         * @brief 将一个世界坐标点转换为局部坐标。
         * @param worldPoint 在世界坐标系中的点。
         * @return 对应的在此部件局部坐标系中的点。
         */
        glm::vec2 worldToLocal(const glm::vec2& worldPoint) const;

        /**
         * @brief 计算并获取此部件在世界坐标系中的包围盒。
         * @details 包围盒会包含所有附加到此部件的网格。
         * @return 在世界坐标系中的包围盒。
         */
        util::BoundingBox getWorldBounds();

        /**
         * @brief 检查此部件及其所有父部件是否都可见。
         * @return 如果从根节点到此部件的整个路径都可见，则返回 true。
         */
        bool isEffectivelyVisible() const;

        // ==================== Getters & Setters ====================
        const std::string& getName() const;
        void setName(const std::string& name);
        ModelPart* getParent() const;
        const glm::vec2& getPosition() const;
        float getRotation() const;
        const glm::vec2& getScale() const;
        const glm::vec2& getPivot() const;
        const glm::mat3& getLocalTransform() const;
        const glm::mat3& getWorldTransform() const;
        bool isVisible() const;
        void setVisible(bool visible);
        BlendMode getBlendMode() const;
        void setBlendMode(BlendMode mode);
        float getOpacity() const;
        void setOpacity(float opacity);

        // ==================== 参数管理 ====================
        /**
         * @brief 创建一个专属于此部件的动画参数。
         * @param id 参数的唯一标识符。
         * @param min 参数的最小值。
         * @param max 参数的最大值。
         * @param defaultValue 参数的默认值。
         * @return 指向新创建的 AnimationParameter 的指针。
         */
        AnimationParameter* createParameter(const std::string& id, float min, float max, float defaultValue);

        /**
         * @brief 根据 ID 获取一个专属于此部件的动画参数。
         * @param id 要获取的参数 ID。
         * @return 如果找到，返回指向参数的指针；否则返回 nullptr。
         */
        AnimationParameter* getParameter(const std::string& id);

        /**
         * @brief 添加一个已存在的动画参数到此部件。
         * @param param 要添加的 AnimationParameter 对象。
         */
        void addParameter(const AnimationParameter& param);

        /**
         * @brief 设置此部件特定动画参数的值。
         * @param paramId 参数的 ID。
         * @param value 要设置的新值。
         */
        void setParameterValue(const std::string& paramId, float value);

        /**
         * @brief 获取此部件特定动画参数的当前值。
         * @param paramId 参数的 ID。
         * @return 参数的当前浮点数值。
         */
        float getParameterValue(const std::string& paramId) const;

    private:
        // ==================== 私有成员变量 ====================
        //! 部件名称
        std::string m_Name;
        //! 指向父部件的裸指针，不拥有所有权
        ModelPart* m_Parent = nullptr;
        //! 子部件列表，拥有其所有权
        std::vector<std::unique_ptr<ModelPart>> m_Children;
        //! 附加到此部件的网格列表，拥有其所有权
        std::vector<std::unique_ptr<Mesh2D>> m_Meshes;

        //! 相对于父部件的位置
        glm::vec2 m_Position{ 0.0f };
        //! 相对于父部件的旋转（弧度）
        float m_Rotation = 0.0f;
        //! 相对于父部件的缩放
        glm::vec2 m_Scale{ 1.0f };
        //! 变换的轴心（局部坐标）
        glm::vec2 m_Pivot{ 0.0f };

        //! 局部变换矩阵，描述了从父部件坐标系到当前部件坐标系的变换
        glm::mat3 m_LocalTransform{ 1.0f };
        //! 世界变换矩阵，描述了从模型根坐标系到当前部件坐标系的变换
        glm::mat3 m_WorldTransform{ 1.0f };

        //! 当前部件的可见性标志
        bool m_IsVisible = true;
        //! 渲染混合模式
        BlendMode m_BlendMode = BlendMode::NORMAL;
        //! 不透明度，范围 [0.0, 1.0]
        float m_Opacity = 1.0f;

        //! 专属于此部件的动画参数
        std::map<std::string, AnimationParameter> m_Parameters;

        //! 标记局部变换是否需要重新计算
        bool m_TransformDirty = true;
        //! 标记包围盒是否需要重新计算。mutable 允许在 const 成员函数中修改它。
        mutable bool m_BoundsDirty = true;

        // ==================== 私有辅助方法 ====================
        /**
         * @brief 将变换标记为“脏”，表示局部变换矩阵需要在下次访问前重新计算。
         */
        void markTransformDirty();

        /**
         * @brief 根据 m_Position, m_Rotation, m_Scale, m_Pivot 更新局部变换矩阵 m_LocalTransform。
         */
        void updateLocalTransform();

        /**
         * @brief (保留) 更新包围盒。
         */
        void updateBounds() const;

        /**
         * @brief 深拷贝辅助函数，被拷贝构造函数和拷贝赋值运算符调用。
         * @param other 要从中拷贝数据的源对象。
         */
        void deepCopyFrom(const ModelPart& other);
    };

} // namespace Vivid2D