#include "pch.h"

#include "FrameInterpolator.h"
#include "ModelPart.h"
#include "Mesh2D.h"

#include "util/Vertex.h"
#include "spdlog/spdlog.h"
#include "glm/glm.hpp"
#include "glm/gtc/constants.hpp"

#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <optional>

namespace Vivid2D {
    namespace { // 使用匿名命名空间来隐藏这些辅助函数，等同于 private static

        // --- 辅助转换方法 ---

        float toFloat(const ParameterValue& pv) {
            if (const float* val = std::get_if<float>(&pv)) {
                return *val;
            }
            return 0.0f;
        }

        glm::vec2 readVec2(const ParameterValue& pv) {
            if (const auto* val = std::get_if<std::pair<float, float>>(&pv)) {
                return { val->first, val->second };
            }
            if (const float* val = std::get_if<float>(&pv)) {
                return { *val, *val };
            }
            return { 0.0f, 0.0f };
        }

        // --- 角度归一化 ---

        float normalizeAngle(float a) {
            while (a <= -glm::pi<float>()) a += 2 * glm::pi<float>();
            while (a > glm::pi<float>()) a -= 2 * glm::pi<float>();
            return a;
        }

        float normalizeAnimAngleUnits(float a) {
            if (std::abs(a) > glm::pi<float>() * 2.1f) {
                return glm::radians(a);
            }
            return a;
        }

        // --- 查找索引 ---

        std::vector<size_t> findIndicesForParam(const ParameterRecord& fullParam, const std::string& paramId, const AnimationParameter& animParam) {
            std::vector<size_t> indices;
            for (size_t i = 0; i < fullParam.paramIds.size(); ++i) {
                if (fullParam.paramIds[i] == paramId && fullParam.animationParameters[i] == animParam) {
                    indices.push_back(i);
                }
            }
            return indices;
        }

        std::vector<size_t> findIndicesForDeformationVertex(const ParameterRecord& fullParam, const std::string& vertexId, const AnimationParameter& animParam) {
            std::vector<size_t> indices;
            for (size_t i = 0; i < fullParam.paramIds.size(); ++i) {
                if (fullParam.paramIds[i] == "meshVertices" && fullParam.animationParameters[i] == animParam) {
                    if (const auto* mapVal = std::get_if<std::map<std::string, std::any>>(&fullParam.values[i])) {
                        auto it = mapVal->find("id");
                        if (it != mapVal->end()) {
                            if (const auto* id = std::any_cast<std::string>(&it->second)) {
                                if (*id == vertexId) {
                                    indices.push_back(i);
                                }
                            }
                        }
                    }
                }
            }
            return indices;
        }

        // --- 查找关键帧并计算插值系数 T ---

        std::pair<ptrdiff_t, ptrdiff_t> findSurroundingKeyframes(const ParameterRecord& param, const std::vector<size_t>& indices, float current) {
            ptrdiff_t prevIndex = -1, nextIndex = -1;
            float prevVal = -std::numeric_limits<float>::infinity();
            float nextVal = std::numeric_limits<float>::infinity();

            for (size_t idx : indices) {
                float val = param.keyframes[idx];
                if (val <= current && val >= prevVal) {
                    prevIndex = idx;
                    prevVal = val;
                }
                if (val >= current && val <= nextVal) {
                    nextIndex = idx;
                    nextVal = val;
                }
            }
            return { prevIndex, nextIndex };
        }

        float computeT(float prevVal, float nextVal, float current) {
            if (std::abs(nextVal - prevVal) < 1e-6) return 0.0f;
            return std::clamp((current - prevVal) / (nextVal - prevVal), 0.0f, 1.0f);
        }

        // --- 计算目标值 ---

        std::optional<glm::vec2> computeVec2Target(const ParameterRecord& fullParam, const std::string& paramId, float current, const AnimationParameter& animParam) {
            auto idxs = findIndicesForParam(fullParam, paramId, animParam);
            if (idxs.empty()) return std::nullopt;

            auto [prevIndex, nextIndex] = findSurroundingKeyframes(fullParam, idxs, current);

            if (prevIndex != -1 && nextIndex != -1) {
                if (prevIndex == nextIndex) return readVec2(fullParam.values[prevIndex]);

                glm::vec2 prev = readVec2(fullParam.values[prevIndex]);
                glm::vec2 next = readVec2(fullParam.values[nextIndex]);
                float t = computeT(fullParam.keyframes[prevIndex], fullParam.keyframes[nextIndex], current);
                return glm::mix(prev, next, t);
            }
            if (prevIndex != -1) return readVec2(fullParam.values[prevIndex]);
            if (nextIndex != -1) return readVec2(fullParam.values[nextIndex]);

            return std::nullopt;
        }

        std::optional<float> computeRotationTarget(const ParameterRecord& fullParam, const std::string& paramId, float current, const AnimationParameter& animParam) {
            auto idxs = findIndicesForParam(fullParam, paramId, animParam);
            if (idxs.empty()) return std::nullopt;

            auto [prevIndex, nextIndex] = findSurroundingKeyframes(fullParam, idxs, current);

            if (prevIndex != -1 && nextIndex != -1) {
                float p = normalizeAnimAngleUnits(toFloat(fullParam.values[prevIndex]));
                if (prevIndex == nextIndex) return p;

                float q = normalizeAnimAngleUnits(toFloat(fullParam.values[nextIndex]));
                float t = computeT(fullParam.keyframes[prevIndex], fullParam.keyframes[nextIndex], current);
                return p + t * normalizeAngle(q - p);
            }
            if (prevIndex != -1) return normalizeAnimAngleUnits(toFloat(fullParam.values[prevIndex]));
            if (nextIndex != -1) return normalizeAnimAngleUnits(toFloat(fullParam.values[nextIndex]));

            return std::nullopt;
        }

        std::map<std::string, glm::vec2> computeMeshVerticesTarget(const ParameterRecord& fullParam, float current, const AnimationParameter& animParam) {
            std::map<std::string, glm::vec2> targetDeformations;
            std::set<std::string> uniqueVertexIds;

            // 1. 收集所有唯一的顶点ID
            for (size_t i = 0; i < fullParam.paramIds.size(); ++i) {
                if (fullParam.paramIds[i] == "meshVertices" && fullParam.animationParameters[i] == animParam) {
                    if (const auto* mapVal = std::get_if<std::map<std::string, std::any>>(&fullParam.values[i])) {
                        if (auto it = mapVal->find("id"); it != mapVal->end()) {
                            if (const auto* id = std::any_cast<std::string>(&it->second)) {
                                uniqueVertexIds.insert(*id);
                            }
                        }
                    }
                }
            }
            if (uniqueVertexIds.empty()) return targetDeformations;

            // 2. 为每个顶点ID计算插值后的位置
            for (const auto& vertexId : uniqueVertexIds) {
                auto idxs = findIndicesForDeformationVertex(fullParam, vertexId, animParam);
                if (idxs.empty()) continue;

                auto [prevIndex, nextIndex] = findSurroundingKeyframes(fullParam, idxs, current);
                std::optional<glm::vec2> finalPos;

                auto getVertexPos = [&](ptrdiff_t index) -> glm::vec2 {
                    const auto& mapVal = std::get<std::map<std::string, std::any>>(fullParam.values[index]);
                    const auto& vertexVal = mapVal.at("Vertex"); // .at() will throw if not found
                    return std::any_cast<glm::vec2>(vertexVal);
                    };

                if (prevIndex != -1 && nextIndex != -1) {
                    glm::vec2 prevPos = getVertexPos(prevIndex);
                    if (prevIndex == nextIndex) {
                        finalPos = prevPos;
                    }
                    else {
                        glm::vec2 nextPos = getVertexPos(nextIndex);
                        float t = computeT(fullParam.keyframes[prevIndex], fullParam.keyframes[nextIndex], current);
                        finalPos = glm::mix(prevPos, nextPos, t);
                    }
                }
                else if (prevIndex != -1) {
                    finalPos = getVertexPos(prevIndex);
                }
                else if (nextIndex != -1) {
                    finalPos = getVertexPos(nextIndex);
                }

                if (finalPos) {
                    targetDeformations[vertexId] = *finalPos;
                }
            }
            return targetDeformations;
        }
    } // 匿名 namespace 结束

    void FrameInterpolator::applyFrameInterpolations(
        const ParametersManagement& pm,
        const std::vector<ModelPart*>& parts,
        const AnimationParameter& currentAnimationParameter,
        std::shared_ptr<spdlog::logger> logger)
    {
        if (parts.empty()) return;

        float current = currentAnimationParameter.getValue();

        for (ModelPart* part : parts) {
            if (!part) continue;

            const ParameterRecord* fullParam = pm.getModelPartParameters(part);
            if (!fullParam) continue;

            try {
                auto targetPivot = computeVec2Target(*fullParam, "pivot", current, currentAnimationParameter);
                auto targetScale = computeVec2Target(*fullParam, "scale", current, currentAnimationParameter);
                auto targetPosition = computeVec2Target(*fullParam, "position", current, currentAnimationParameter);
                auto targetRotation = computeRotationTarget(*fullParam, "rotate", current, currentAnimationParameter);

                if (targetPivot) part->setPivot(*targetPivot);
                if (targetScale) part->setScale(*targetScale);
                if (targetPosition) part->setPosition(*targetPosition);
                if (targetRotation) part->setRotation(*targetRotation);

                auto targetDeformations = computeMeshVerticesTarget(*fullParam, current, currentAnimationParameter);

                if (targetDeformations.empty() || part->getMeshes().empty()) {
                    part->updateMeshVertices();
                }
                else {
	                if (Mesh2D* targetMesh = part->getMeshes().front().get()) {
                        for (auto& [vertexId, worldPos] : targetDeformations) {
                            Vertex* vertex = targetMesh->findVertexByName(vertexId);
                            if (vertex) {
                                vertex->position = worldPos;
                            }
                        }
                        targetMesh->saveAsOriginal();
                        part->updateMeshVertices();
                    }
                }
            }
            catch (const std::exception& e) {
                if (logger) logger->error("Exception while applying interpolation for part '{}': {}", part->getName(), e.what());
            }
        }
    }

} // namespace Vivid2D