#pragma once

#ifndef MODEL2D_H
#define MODEL2D_H

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <set>
#include <stduuid/uuid.h>

#include "util/ModelPose.h"
#include "util/LightSource.h"
#include "ModelPart.h"
#include "Mesh2D.h"

// 前置声明必要的类
namespace Vivid2D {
    namespace Render {
        namespace Texture {
            class Texture;
        }
    }
    namespace util {
        class BoundingBox;
        class PhysicsSystem;
        class AnimationLayer;
        class AnimationParameter;
    }

    /**
     * @class Model2D
     * @brief 2D 模型核心数据结构，管理模型的部件、网格、纹理、姿态、动画和光源等所有资源。
     * @details
     * Model2D 是一个容器类，用于聚合构成一个完整 2D 角色或对象所需的所有数据。
     * 它负责层级结构的管理、动画参数的驱动、姿态的混合与应用，以及物理和渲染状态的更新。
     * 该类被设计为不可拷贝，但可移动，以确保资源所有权的唯一性。
     */
    class VIVID_2D_MYDLL_API Model2D {
    public:
        // 使用 stduuid 库的 UUID 类型
        using UUID = uuids::uuid;

        // ==================== 构造器 ====================
        /**
         * @brief 默认构造函数，创建一个未命名的 Model2D 实例。
         */
        Model2D();

        /**
         * @brief 构造函数，使用指定的名称创建一个 Model2D 实例。
         * @param name 模型的名称。
         */
        explicit Model2D(const std::string& name);

        /**
         * @brief 析构函数。
         */
        ~Model2D();

        // 禁止拷贝和赋值
        Model2D(const Model2D&) = delete;
        Model2D& operator=(const Model2D&) = delete;

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

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

        // ==================== 姿态管理 ====================

        /**
         * @brief 添加一个新的姿态到模型。
         * @param pose 要添加的 ModelPose 对象。如果已存在同名姿态，它将被覆盖。
         */
        void addPose(const util::ModelPose& pose);

        /**
         * @brief 根据名称获取一个姿态。
         * @param name 姿态的名称。
         * @return 如果找到，返回指向该姿态的常量指针；否则返回 nullptr。
         */
        const util::ModelPose* getPose(const std::string& name) const;

        /**
         * @brief 立即将模型的状态应用为指定的姿态。
         * @param poseName 要应用的姿态的名称。
         */
        void applyPose(const std::string& poseName);

        /**
         * @brief 在指定时间内平滑地混合到目标姿态。
         * @param targetPoseName 目标姿态的名称。
         * @param blendTime 完成混合所需的总时间（秒）。
         */
        void blendToPose(const std::string& targetPoseName, float blendTime);

        /**
         * @brief 将模型当前的部件变换状态保存为一个新的姿态。
         * @param poseName 要保存的新姿态的名称。
         */
        void saveCurrentPose(const std::string& poseName);

        /**
         * @brief 获取当前模型正在应用的姿态名称。
         * @return 当前姿态的名称。
         */
        const std::string& getCurrentPoseName() const { return m_currentPoseName; }

        /**
         * @brief 检查模型是否包含具有指定名称的姿态。
         * @param poseName 要检查的姿态名称。
         * @return 如果存在则返回 true，否则返回 false。
         */
        bool hasPose(const std::string& poseName) const;

        /**
         * @brief 从模型中移除一个姿态。
         * @param poseName 要移除的姿态的名称。
         */
        void removePose(const std::string& poseName);

        /**
         * @brief 获取所有已定义姿态的名称集合。
         * @return 一个包含所有姿态名称的 std::set。
         */
        std::set<std::string> getPoseNames() const;

        /**
         * @brief 设置特定姿态的混合权重。此功能通常用于更高级的姿态混合逻辑，例如加权混合多个姿态。
         * @param poseName 要设置混合权重的姿态名称。
         * @param blendFactor 混合权重因子，通常在 [0, 1] 范围内。
         */
        void setPoseBlend(const std::string& poseName, float blendFactor);

        // ==================== 光源管理 ====================
        /**
         * @brief 获取模型关联的所有光源。
         * @return 一个包含所有光源的常量引用向量。
         */
        const std::vector<util::LightSource>& getLights() const;

        /**
         * @brief 向模型添加一个光源。
         * @param light 要添加的光源对象。
         */
        void addLight(const util::LightSource& light);

        /**
         * @brief 从模型中移除一个光源。
         * @param light 要移除的光源对象。注意：比较是基于光源对象的属性值。
         */
        void removeLight(const util::LightSource& light);

        /**
         * @brief 清除所有与模型关联的光源。
         */
        void clearLights();

        // ==================== 部件管理 ====================
        /**
         * @brief 创建一个新的模型部件，但不立即添加到模型中。
         * @param name 新部件的名称。
         * @return 返回一个包含新创建 ModelPart 的 std::unique_ptr。
         */
        std::unique_ptr<ModelPart> createPart(const std::string& name);

        /**
         * @brief 将一个已存在的部件添加到模型中。
         * @param part 要添加的部件的 std::unique_ptr，所有权将转移给 Model2D。
         */
        void addPart(std::unique_ptr<ModelPart> part);

        /**
         * @brief 根据名称获取一个模型部件。
         * @param name 要获取的部件的名称。
         * @return 如果找到，返回指向该部件的指针；否则返回 nullptr。
         */
        ModelPart* getPart(const std::string& name) const;

        /**
         * @brief 获取部件名称到部件指针的映射表。
         * @return 一个包含所有部件映射的常量引用。
         */
        const std::map<std::string, ModelPart*>& getPartMap() const;

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

        /**
         * @brief 获取模型的根部件。
         * @return 指向根部件的指针。如果没有设置根部件，则可能为 nullptr。
         */
        ModelPart* getRootPart() const { return m_rootPart; }

        // ==================== 参数管理 ====================
        /**
         * @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;

        /**
         * @brief 获取所有动画参数的映射表。
         * @return 一个包含所有参数的常量引用映射表。
         */
        const std::map<std::string, AnimationParameter>& getParameters() const;

        // ==================== 网格管理 ====================
        /**
         * @brief 创建一个新的 2D 网格。
         * @param name 网格的名称。
         * @param vertices 顶点坐标数组 (x, y, z, ...)。
         * @param uvs UV 纹理坐标数组 (u, v, ...)。
         * @param indices 顶点索引数组。
         * @return 返回一个包含新创建 Mesh2D 的 std::unique_ptr。
         */
        std::unique_ptr<Mesh2D> createMesh(const std::string& name, const std::vector<float>& vertices,
            const std::vector<float>& uvs, const std::vector<int>& indices);

        /**
         * @brief 将一个已存在的网格添加到模型中。
         * @param mesh 要添加的网格的 std::unique_ptr，所有权将转移给 Model2D。
         */
        void addMesh(std::unique_ptr<Mesh2D> mesh);

        /**
         * @brief 根据名称获取一个网格。
         * @param name 要获取的网格的名称。
         * @return 如果找到，返回指向该网格的指针；否则返回 nullptr。
         */
        Mesh2D* getMesh(const std::string& name) const;

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

        // ==================== 纹理管理 ====================
        /**
         * @brief 向模型添加一个纹理资源。
         * @param texture 要添加的纹理的共享指针。
         */
        void addTexture(std::shared_ptr<Render::Texture::Texture> texture);

        /**
         * @brief 根据名称获取一个纹理。
         * @param name 要获取的纹理的名称。
         * @return 如果找到，返回指向该纹理的共享指针；否则返回一个空的共享指针。
         */
        std::shared_ptr<Render::Texture::Texture> getTexture(const std::string& name) const;

        /**
         * @brief 获取所有纹理的映射表。
         * @return 一个包含所有纹理的常量引用映射表。
         */
        const std::map<std::string, std::shared_ptr<Render::Texture::Texture>>& getTextures() const;

        // ==================== 动画层管理 ====================
        /**
         * @brief 创建一个新的动画层。
         * @param name 动画层的名称。
         * @return 返回一个包含新创建 AnimationLayer 的 std::unique_ptr。
         */
        std::unique_ptr<util::AnimationLayer> createAnimationLayer(const std::string& name);

        /**
         * @brief 获取模型中所有动画层的列表。
         * @return 一个包含所有动画层的 std::unique_ptr 的常量引用向量。
         */
        const std::vector<std::unique_ptr<util::AnimationLayer>>& getAnimationLayers() const;

        /**
         * @brief 设置模型的动画层列表。
         * @param animationLayers 一个包含动画层的 std::unique_ptr 向量，所有权将转移给 Model2D。
         */
        void setAnimationLayers(std::vector<std::unique_ptr<util::AnimationLayer>> animationLayers);

        // ==================== 更新系统 ====================
        /**
         * @brief 更新模型的内部状态。
         * @details
         * 此方法应每帧调用一次。它负责处理姿态混合、参数变形、层级变换更新和包围盒计算。
         * @param deltaTime 从上一帧到当前帧的时间差（秒）。
         */
        void update(float deltaTime);

        // ==================== 工具方法 ====================
        /**
         * @brief 标记模型需要更新。在修改了任何可能影响渲染或物理状态的属性后调用。
         */
        void markNeedsUpdate() { m_needsUpdate = true; }

        /**
         * @brief 检查模型当前是否可见。
         * @return 如果可见则返回 true，否则返回 false。
         */
        bool isVisible() const;

        /**
         * @brief 设置模型的可见性。
         * @param visible true 表示可见，false 表示隐藏。
         */
        void setVisible(bool visible);

        /**
         * @brief 检查模型当前是否正在进行姿态混合。
         * @return 如果正在混合（混合进度小于 1.0），则返回 true。
         */
        bool isBlending() const { return m_blendProgress < 1.0f; }

        // ==================== Getter/Setter ====================
        /**
         * @brief 获取模型的名称。
         * @return 模型的名称。
         */
        const std::string& getName() const { return m_name; }

        /**
         * @brief 设置模型的名称。
         * @param name 新的名称。
         */
        void setName(const std::string& name) { m_name = name; }

        /**
         * @brief 获取模型的唯一标识符 (UUID)。
         * @return 模型的 UUID。
         */
        const UUID& getUuid() const { return m_uuid; }

        /**
         * @brief 设置模型的唯一标识符 (UUID)。
         * @param uuid 新的 UUID。
         */
        void setUuid(const UUID& uuid) { m_uuid = uuid; }

        /**
         * @brief 获取模型的物理系统。
         * @return 指向物理系统的常量指针。
         */
        util::PhysicsSystem* getPhysics() { return m_physics.get(); }

        /**
         * @brief 获取模型当前应用的姿态。
         * @return 当前姿态的拷贝。
         */
        util::ModelPose getCurrentPose() const { return m_currentPose; }

        /**
         * @brief 获取当前姿态混合的进度。
         * @return 混合进度，范围从 0.0 (起始姿态) 到 1.0 (目标姿态)。
         */
        float getBlendProgress() const { return m_blendProgress; }

        /**
         * @brief 获取所有已定义姿态的映射表。
         * @return 一个从姿态名称到 ModelPose 对象的常量引用映射表。
         */
        const std::map<std::string, util::ModelPose>& getPoses() const { return m_poses; }

        /**
         * @brief 获取模型的包围盒。
         * @return 指向包围盒的常量指针。
         */
        const util::BoundingBox* getBounds() const { return m_bounds.get(); }

        /**
         * @brief 获取模型的版本号。
         * @return 模型的版本字符串。
         */
        const std::string& getVersion() const { return m_version; }

        /**
         * @brief 设置模型的版本号。
         * @param version 新的版本字符串。
         */
        void setVersion(const std::string& version) { m_version = version; }

    private:
        // ==================== 成员变量 ====================
        //! 模型的名称
        std::string m_name;
        //! 模型格式的版本号
        std::string m_version = "1.0.0";
        //! 模型的唯一标识符 (UUID)
        UUID m_uuid;

        //! 存储所有模型部件的列表，拥有其所有权
        std::vector<std::unique_ptr<ModelPart>> m_parts;
        //! 从部件名称到部件指针的映射，用于快速查找
        std::map<std::string, ModelPart*> m_partMap;
        //! 指向层级结构根部的指针
        ModelPart* m_rootPart = nullptr;

        //! 存储所有网格的列表，拥有其所有权
        std::vector<std::unique_ptr<Mesh2D>> m_meshes;
        //! 从纹理名称到纹理共享指针的映射
        std::map<std::string, std::shared_ptr<Render::Texture::Texture>> m_textures;

        //! 从参数 ID 到 AnimationParameter 对象的映射
        std::map<std::string, AnimationParameter> m_parameters;
        //! 存储所有动画层的列表，拥有其所有权
        std::vector<std::unique_ptr<util::AnimationLayer>> m_animationLayers;
        //! 模型的物理系统
        std::unique_ptr<util::PhysicsSystem> m_physics;

        //! 标记模型是否需要在下一帧更新其变换或顶点数据
        bool m_needsUpdate = true;
        //! 模型的轴对齐包围盒 (AABB)
        std::unique_ptr<util::BoundingBox> m_bounds;

        //! 从姿态名称到 ModelPose 对象的映射
        std::map<std::string, util::ModelPose> m_poses;
        //! 当前应用的姿态名称
        std::string m_currentPoseName = "default";
        //! 当前姿态的完整数据
        util::ModelPose m_currentPose;
        //! 姿态混合的目标姿态
        util::ModelPose m_blendTargetPose;
        //! 姿态混合的当前进度 [0.0, 1.0]
        float m_blendProgress = 1.0f;
        //! 姿态混合的速度
        float m_blendSpeed = 1.0f;

        //! 影响模型的光源列表
        std::vector<util::LightSource> m_lights;

        // ==================== 私有辅助方法 ====================
        /**
         * @brief 初始化模型的默认姿态。
         * @details
         * 当模型被创建时调用，捕获所有部件的初始变换作为“default”姿态。
         */
        void initializeDefaultPose();

        /**
         * @brief 在每帧更新中处理姿态混合的逻辑。
         * @param deltaTime 帧间时间差。
         */
        void updatePoseBlending(float deltaTime);

        /**
         * @brief 将 m_currentPose 中的变换数据应用到模型的各个部件上。
         */
        void applyCurrentPoseToModel();

        /**
         * @brief 将给定的姿态内部应用到模型部件上，这是一个底层的应用函数。
         * @param pose 要应用的姿态。
         */
        void applyPoseInternal(const util::ModelPose& pose) const;

        /**
         * @brief 捕获模型当前所有部件的变换，并存储到一个 ModelPose 对象中。
         * @param pose 用于接收当前姿态数据的 ModelPose 对象。
         */
        void captureCurrentPose(util::ModelPose& pose);

        /**
         * @brief 根据所有动画参数的当前值，更新受其影响的部件变形。
         */
        void updateParameterDeformations();

        /**
         * @brief 将单个动画参数的变形效果应用到相关的模型部件上。
         * @param param 要应用的动画参数。
         */
        void applyParameterDeformations(AnimationParameter* param);

        /**
         * @brief 更新整个模型部件层级的全局变换矩阵。
         */
        void updateHierarchyTransforms();

        /**
         * @brief 根据所有部件的当前位置和大小，重新计算模型的总包围盒。
         */
        void updateBoundingBox();
    };

} // namespace Vivid2D

#endif // MODEL2D_H