#pragma once
#include "BoneAnimatedModel.h"
#include "render/Renderer.h"
#include "render/TextureManager.h"
#include "items/Item.h"
#include <functional>
#include <memory>
#include <map>
#include <string>
#include <cstdint>
#include <vector>

namespace ZL {

enum class AnimationState {
    STAND = 0,
    WALK = 1,
    STAND_TO_ACTION = 2,
    ACTION_ATTACK = 3,
    ACTION_IDLE = 4,
    ACTION_TO_STAND = 5
};

class Character {
public:
    using PathPlanner = std::function<std::vector<Eigen::Vector3f>(
        const Eigen::Vector3f& start,
        const Eigen::Vector3f& end)>;

    void loadAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = "");
    void loadBinaryAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = "");
    // void setTexture(std::shared_ptr<Texture> texture);
    void setTexture(std::shared_ptr<Texture> texture) {
        this->texture = texture;
    }
    void update(int64_t deltaMs);
    // onArrived is called once when the character reaches this target.
    // From Python you can chain further commands (walk, wait, trigger others).
    void setTarget(const Eigen::Vector3f& target,
                   std::function<void()> onArrived = nullptr);
    void setPathPlanner(PathPlanner planner);
    void draw(Renderer& renderer);
    void drawShadowDepth(Renderer& renderer);
    void drawWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);

    // Public: read by Game for camera tracking and ray-cast origin
    Eigen::Vector3f position = Eigen::Vector3f(0.f, 0.f, 0.f);
    float facingAngle = 0.0f;

    // Per-character tuning — set after construction, before first update
    float walkSpeed = 3.0f;
    float rotationSpeed = 4.0f;
    float modelScale = 0.12f;
    // Applied after scale, fixes model-space orientation (e.g. Blender Z-up exports)
    Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();

    // NPC Gift
    Item giftItem;
    std::string npcId;
    std::string npcName;
    bool giftReceived = false;


    int battle_state = 0;
    bool resetAnim = false;
    int attack = 0;
    AnimationState currentState = AnimationState::STAND;
    float attack_cooldown = 0.f;
    bool canAttack = false;
    Character* attackTarget = nullptr;
    bool isPlayer = false;
    bool useGpuSkinning = true;

    float interactionRadius = 0.0f;


private:
    struct AnimationData {
        BoneSystem model;
        VertexRenderStruct modelMutable;
        float currentFrame = 0.f;
        int lastFrame = -1;
        int totalFrames = 1;

        // GPU skinning data (lazily initialized)
        VertexRenderStruct bindPoseMutable;
        std::shared_ptr<VBOHolder> boneIndices0VBO;
        std::shared_ptr<VBOHolder> boneIndices1VBO;
        std::shared_ptr<VBOHolder> boneWeights0VBO;
        std::shared_ptr<VBOHolder> boneWeights1VBO;
        bool gpuSkinningPrepared = false;
        std::vector<Eigen::Matrix4f> skinningMatrices;
    };

    std::map<AnimationState, AnimationData> animations;
    std::shared_ptr<Texture> texture;

    Eigen::Vector3f walkTarget = Eigen::Vector3f(0.f, 0.f, 0.f);
    Eigen::Vector3f requestedWalkTarget = Eigen::Vector3f(0.f, 0.f, 0.f);
    std::vector<Eigen::Vector3f> pathWaypoints;
    size_t currentWaypointIndex = 0;
    PathPlanner pathPlanner;
    float targetFacingAngle = 0.0f;
    std::function<void()> onArrivedCallback;

    static constexpr float WALK_THRESHOLD = 0.05f;
    static constexpr float TARGET_REPLAN_THRESHOLD = 0.25f;

    // Returns the animation state to actually play/draw, falling back to IDLE
    // if the requested state has no loaded animation.
    AnimationState resolveActiveState() const;

    // GPU skinning: prepare per-animation VBOs (called lazily on first draw)
    void prepareGpuSkinningVBOs(AnimationData& anim);
    // GPU skinning: draw using shader-based skinning
    void drawGpuSkinning(Renderer& renderer);
    // Shadow: draw into depth map (no texture, no projection push)
    void drawShadowDepthGpuSkinning(Renderer& renderer);
    void drawShadowDepthCpu(Renderer& renderer);
    // Shadow: draw with shadow-aware shaders
    void drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
    void drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lightFromCamera, GLuint shadowMapTex, const Eigen::Vector3f& lightDirCamera);
};

} // namespace ZL
