#include "AnimationComponent.h"

AnimationComponent::AnimationComponent(Object *owner)
    : Component(owner) {}

void AnimationComponent::SetTextureAtlasPath(const std::string &path, int texelWidth, int texelHeight, float duration, int start, int end)
{
    AssetManager::LoadAssetAsync(path, AssetType::Image);
    const auto *asset = AssetManager::GetAssetByPath(path);
    if (asset)
        SetTextureAtlas(asset->uaid, texelWidth, texelHeight, duration, start, end);
}

void AnimationComponent::SetTextureAtlas(uint64_t uaid, int texelWidth, int texelHeight, float duration, int start, int end)
{
    textureUAID = uaid;

    const auto* asset = AssetManager::GetAssetByID(uaid);
    if (!asset || !asset->loaded)
    {
        Logger::LogError("SetTextureAtlas: Asset not found or not loaded (UAID: %llu)", uaid);
        return;
    }

    const auto* imageAsset = dynamic_cast<const ImageAssetInfo*>(asset);
    if (!imageAsset)
    {
        Logger::LogError("SetTextureAtlas: Asset with UAID %llu is not an image", uaid);
        return;
    }

    int texWidth = static_cast<int>(imageAsset->size.x);
    int texHeight = static_cast<int>(imageAsset->size.y);

    if (texelWidth <= 0 || texelHeight <= 0 || texWidth < texelWidth || texHeight < texelHeight)
    {
        Logger::LogError("SetTextureAtlas: Invalid texel size %dx%d for image size %dx%d (UAID: %llu)",
                         texelWidth, texelHeight, texWidth, texHeight, uaid);
        return;
    }

    if (texWidth % texelWidth != 0 || texHeight % texelHeight != 0)
    {
        Logger::LogWarning("SetTextureAtlas: Texture size is not cleanly divisible by texel size (UAID: %llu)", uaid);
    }

    texture = std::make_shared<Texture>(imageAsset->uaid);

    atlas.texture = texture;
    atlas.SetTexelSize(texelWidth, texelHeight);

    columns = texWidth / texelWidth;
    rows = texHeight / texelHeight;
    totalFrames = columns * rows;

    startFrame = 0;
    endFrame = totalFrames > 0 ? totalFrames - 1 : 0;

    if (start != 0)
        startFrame = start;

    if (end != 0)
        endFrame = end;

    frameDuration = duration;
    currentFrame = startFrame;

    renderType = RenderType::Unlit;
}


void AnimationComponent::Play() { playing = true; }
void AnimationComponent::Stop() { playing = false; }
void AnimationComponent::SetLooping(bool loop_) { loop = loop_; }

bool AnimationComponent::IsPlaying() const { return playing; }

void AnimationComponent::SetPlaying(bool play) { playing = play; }

uint64_t AnimationComponent::GetTextureUAID() const { return textureUAID; }

void AnimationComponent::SetTextureUAID(uint64_t uaid) { textureUAID = uaid; }

bool AnimationComponent::IsLooping() const { return loop; }
void AnimationComponent::SetSpeed(float speed_) { speed = speed_; }
float AnimationComponent::GetSpeed() const { return speed; }

void AnimationComponent::SetFrame(int frame)
{
    currentFrame = std::clamp(frame, startFrame, endFrame);
    time = 0.0f;
}
int AnimationComponent::GetCurrentFrame() const { return currentFrame; }
int AnimationComponent::GetStartFrame() const { return startFrame; }
void AnimationComponent::SetStartFrame(int start) { startFrame = std::clamp(start, 0, totalFrames - 1); }
int AnimationComponent::GetEndFrame() const { return endFrame; }
void AnimationComponent::SetEndFrame(int end) { endFrame = std::clamp(end, 0, totalFrames - 1); }
int AnimationComponent::GetTotalFrames() const { return totalFrames; }

float AnimationComponent::GetFrameDuration() const { return frameDuration; }
void AnimationComponent::SetFrameDuration(float duration) { frameDuration = duration; }

TextureAtlas *AnimationComponent::GetAtlas() { return &atlas; }
const TextureAtlas *AnimationComponent::GetAtlas() const { return &atlas; }

void AnimationComponent::Update(float dt)
{
    if (!playing || totalFrames <= 1)
        return;

    time += dt * speed;
    if (time >= frameDuration)
    {
        time = 0;
        currentFrame++;

        if (currentFrame > endFrame)
        {
            if (loop)
                currentFrame = startFrame;
            else
            {
                currentFrame = endFrame;
                playing = false;
            }
        }
    }
}

std::string AnimationComponent::GetName() const { return "AnimationComponent"; }

void AnimationComponent::Save(YAML::Emitter &out) const
{
    out << YAML::BeginMap;
    out << YAML::Key << "type" << YAML::Value << GetName();
    out << YAML::Key << "TextureUAID" << YAML::Value << textureUAID;
    out << YAML::Key << "TexelWidth" << YAML::Value << atlas.frameWidth;
    out << YAML::Key << "TexelHeight" << YAML::Value << atlas.frameHeight;
    out << YAML::Key << "FrameDuration" << YAML::Value << frameDuration;
    out << YAML::Key << "StartFrame" << YAML::Value << startFrame;
    out << YAML::Key << "EndFrame" << YAML::Value << endFrame;
    out << YAML::EndMap;
}

void AnimationComponent::Load(const YAML::Node &node)
{

    uint64_t uaid = node["TextureUAID"].as<uint64_t>();
    int texelWidth = node["TexelWidth"].as<int>();
    int texelHeight = node["TexelHeight"].as<int>();
    float duration = node["FrameDuration"].as<float>();
    int start = node["StartFrame"] ? node["StartFrame"].as<int>() : 0;
    int end = node["EndFrame"] ? node["EndFrame"].as<int>() : 0;

    SetTextureAtlas(uaid, texelWidth, texelHeight, duration, start, end);
}
