#pragma once

#include <string>
#include <set>
#include <vector>
#include <optional>
#include <variant>
#include <map>
#include <any>
#include <sstream> // For toString implementations

namespace Vivid2D {

    // 向前声明 ModelPart 类，避免循环引用
    class ModelPart;

    /**
     * @class AnimationParameter
     * @brief 表示一个可动画的参数，封装了其当前值、范围、默认值以及关联的关键帧信息。
     * @details
     * 该类用于驱动模型部件的各种属性（如位置、旋转、网格变形等）的动画。
     * 它跟踪参数值的变化，并提供了一套管理关键帧的接口。
     */
    class VIVID_2D_MYDLL_API AnimationParameter {
    public:
        /**
         * @brief 默认构造函数。
         */
        AnimationParameter()
            : m_Id(""), m_Value(0.0f), m_DefaultValue(0.0f),
            m_MinValue(0.0f), m_MaxValue(0.0f), m_Changed(false) {}

        /**
         * @brief 构造一个具有完整定义的动画参数。
         * @param id 参数的唯一标识符。
         * @param min 参数允许的最小值。
         * @param max 参数允许的最大值。
         * @param defaultValue 参数的默认值。
         */
        AnimationParameter(std::string id, float min, float max, float defaultValue);

        /**
         * @brief 设置参数的当前值，并将其限制在[min, max]范围内。
         * @details 如果新值与旧值不同，则将参数标记为已更改（dirty）。
         * @param value 要设置的新值。
         */
        void setValue(float value);

        /**
         * @brief 创建并返回当前参数对象的一个副本。
         * @return 当前对象的深拷贝。
         */
        AnimationParameter copy() const;

        /**
         * @brief 检查自上次调用 markClean() 以来，参数值是否已更改。
         * @return 如果值已更改，则返回 true，否则返回 false。
         */
        bool hasChanged() const;

        /**
         * @brief 将参数标记为未更改（clean）。
         */
        void markClean();

        /**
         * @brief 获取参数的当前值。
         * @return 当前的浮点数值。
         */
        float getValue() const;

        /**
         * @brief 获取参数的唯一标识符。
         * @return 参数的 ID 字符串。
         */
        const std::string& getId() const;

        /**
         * @brief 获取参数的最小值。
         * @return 允许的最小值。
         */
        float getMinValue() const;

        /**
         * @brief 获取参数的最大值。
         * @return 允许的最大值。
         */
        float getMaxValue() const;

        /**
         * @brief 获取参数的默认值。
         * @return 默认值。
         */
        float getDefaultValue() const;

        /**
         * @brief 将参数的当前值重置为其默认值。
         */
        void reset();

        /**
         * @brief 获取归一化后的参数值，范围在 [0.0, 1.0] 之间。
         * @return 归一化后的值。
         */
        float getNormalizedValue() const;

        /**
         * @brief 根据一个归一化的值（0.0 到 1.0）来设置参数的实际值。
         * @param normalized 介于 0.0 和 1.0 之间的归一化值。
         */
        void setNormalizedValue(float normalized);

        // --- 关键帧管理 ---
        /**
         * @brief 在参数的时间轴上添加一个关键帧。
         * @param frameValue 要添加为关键帧的值。
         * @return 如果成功添加（即该值之前不是关键帧），则返回 true。
         */
        bool addKeyframe(float frameValue);

        /**
         * @brief 从参数的时间轴上移除一个关键帧。
         * @param frameValue 要移除的关键帧的值。
         * @return 如果成功移除（即该值之前是关键帧），则返回 true。
         */
        bool removeKeyframe(float frameValue);

        /**
         * @brief 检查给定的值是否是一个关键帧。
         * @param frameValue 要检查的值。
         * @return 如果是关键帧，则返回 true。
         */
        bool isKeyframe(float frameValue) const;

        /**
         * @brief 获取所有关键帧的集合。
         * @return 包含所有关键帧值的常量引用集合。
         */
        const std::set<float>& getKeyframes() const;

        /**
         * @brief 清除所有已设置的关键帧。
         */
        void clearKeyframes();

        /**
         * @brief 查找距离给定值最近的关键帧。
         * @param value 要在其附近查找的值。
         * @param snapThreshold 查找范围的阈值。
         * @return 如果在阈值范围内找到关键帧，则返回该关键帧的值；否则返回 std::nullopt。
         */
        std::optional<float> getNearestKeyframe(float value, float snapThreshold) const;

        // --- C++ 特性 ---
        /**
         * @brief 比较两个 AnimationParameter 对象是否相等。
         * @param other 要比较的另一个对象。
         * @return 如果所有成员（除 m_Changed 标志外）都相等，则返回 true。
         */
        bool operator==(const AnimationParameter& other) const;

        /**
         * @brief 将参数的状态转换为字符串表示形式，便于调试。
         * @return 描述参数状态的字符串。
         */
        std::string toString() const;

    private:
        //! 参数的唯一标识符
        std::string m_Id;
        //! 参数的当前值
        float m_Value;
        //! 参数的默认值
        float m_DefaultValue;
        //! 参数的最小值
        float m_MinValue;
        //! 参数的最大值
        float m_MaxValue;
        //! 标记参数值是否已更改
        bool m_Changed = false;
        //! 存储此参数所有关键帧值的集合
        std::set<float> m_Keyframes;
    };


    /**
     * @using ParameterValue
     * @brief 定义一个可以存储不同类型参数值的变体类型。
     * @details
     * 这是一个灵活的数据容器，可以持有不同类型的动画数据：
     * - `std::monostate`: 表示空或无效值。
     * - `float`: 用于简单的单值参数，如旋转、不透明度。
     * - `std::pair<float, float>`: 用于二维向量参数，如位置、缩放。
     * - `std::map<std::string, std::any>`: 用于复杂的结构化数据，如网格顶点变形。
     */
    using ParameterValue = std::variant<
        std::monostate,
        float,
        std::pair<float, float>,
        std::map<std::string, std::any>
    >;

    /**
     * @struct ParameterRecord
     * @brief 存储单个 ModelPart 在动画时间轴上的所有参数数据记录。
     * @details
     * 这个结构体聚合了与一个模型部件相关的所有动画信息，
     * 包括它所关联的部件、控制参数、关键帧位置以及在这些关键帧上的具体参数值。
     */
    struct VIVID_2D_MYDLL_API ParameterRecord {
        //! 指向此记录所属的 ModelPart
        ModelPart* modelPart = nullptr;
        //! 描述动画状态的参数列表（例如，时间轴本身）
        std::vector<AnimationParameter> animationParameters;
        //! 与每个`values`条目对应的参数ID列表 (e.g., "rotate", "translateX")
        std::vector<std::string> paramIds;
        //! 在每个关键帧上，每个参数的具体值
        std::vector<ParameterValue> values;
        //! 所有关键帧在时间轴上的位置
        std::vector<float> keyframes;
        //! 标记每个时间点是否为关键帧
        std::vector<bool> isKeyframes;

        /**
         * @brief 将记录内容转换为字符串，用于调试。
         * @return 描述记录内容的字符串。
         */
        std::string toString() const;
    };

    /**
     * @class ParametersManagement
     * @brief 管理场景中所有 ModelPart 的动画参数记录。
     * @details
     * 这是一个中央管理器，负责存储、更新和查询每个模型部件在动画时间轴上的所有关键帧数据。
     * 它通过一个映射表将 ModelPart 指针与它们的 ParameterRecord 关联起来，以实现高效访问。
     */
    class VIVID_2D_MYDLL_API ParametersManagement {
    public:
        ParametersManagement() = default;

        /**
         * @brief 更新或创建一个参数记录，通常由UI或动画系统调用以记录一次参数变化。
         * @param modelPart 发生变化的模型部件。
         * @param currentAnimParam UI上当前选中的主控制参数（通常是时间轴）。
         * @param currentKeyframe 当前在时间轴上的位置/帧。
         * @param paramId 被修改的参数的唯一标识符 (例如, "rotate", "meshVertices")。
         * @param value 该参数在当前关键帧上的新值。
         */
        void broadcast(ModelPart* modelPart, const AnimationParameter& currentAnimParam, float currentKeyframe, const std::string& paramId, const ParameterValue& value);

        /**
         * @brief 精确地从指定 ModelPart 的记录中，删除特定索引位置的参数条目。
         * @param targetModelPart 目标 ModelPart。
         * @param indexToRemove 要删除的条目在其 `paramIds`, `values` 等向量中的索引。
         */
        void removeParameterAt(ModelPart* targetModelPart, size_t indexToRemove);

        /**
         * @brief 从指定 ModelPart 的记录中移除与特定 paramId 相关的所有关键帧数据。
         * @param modelPart 目标 ModelPart。
         * @param paramId 要移除的参数ID。如果提供 "all"，则会移除该部件的所有参数记录。
         */
        void removeParameter(ModelPart* modelPart, const std::string& paramId);

        /**
         * @brief 获取与给定 ModelPart 和 paramId 匹配的参数记录。
         * @details
         * 此函数会筛选出 `modelPart` 的记录中所有 `paramId` 匹配的条目，
         * 并返回一个只包含这些匹配项的新 ParameterRecord。
         * @param modelPart 目标 ModelPart。
         * @param paramId 要查询的参数 ID。
         * @return 如果找到任何匹配项，则返回一个包含这些匹配项的 ParameterRecord optional；否则返回 std::nullopt。
         */
        std::optional<ParameterRecord> getValue(ModelPart* modelPart, const std::string& paramId) const;

        /**
         * @brief 获取一个 ModelPart 的完整参数记录。
         * @param modelPart 要查询的 ModelPart。
         * @return 如果该部件有记录，则返回指向该记录的常量指针；否则返回 nullptr。
         */
        const ParameterRecord* getModelPartParameters(ModelPart* modelPart) const;

        /**
         * @brief 获取所有参数记录的只读引用。
         * @return 一个从 ModelPart 指针到其 ParameterRecord 的常量引用映射。
         */
        const std::map<ModelPart*, ParameterRecord>& getAllRecords() const;

        /**
         * @brief 将所有记录转换为字符串形式，用于调试。
         * @return 包含所有参数管理信息的字符串。
         */
        std::string toString() const;

    private:
        //! 使用 map 存储每个 ModelPart 的参数记录，以实现高效查找。
        //! 键是 ModelPart 的裸指针，值是其完整的动画数据记录。
        std::map<ModelPart*, ParameterRecord> m_records;
    };

} // namespace Vivid2D