// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <IAEngine/Engine.hpp>

namespace ia::iae
{
    SpriteComponent::SpriteComponent(IN Node2D *node) : TextureComponent(node)
    {
    }

    VOID SpriteComponent::Draw()
    {
        const auto &animFrame = m_currentAnimationState;
        PositionOffset() = animFrame.Position;
        ScaleOffset() = animFrame.Scale;
        RotationOffset() = animFrame.Rotation;
        ColorOverlay() = animFrame.ColorOverlay;
        SetTexture(animFrame.Texture);
        TextureComponent::Draw();
    }

    VOID SpriteComponent::DebugDraw()
    {
        TextureComponent::DebugDraw();
    }

    VOID SpriteComponent::Update()
    {
        TextureComponent::Update();

        UpdateAnimation();
    }

    VOID SpriteComponent::FixedUpdate()
    {
        TextureComponent::FixedUpdate();
    }

    Handle SpriteComponent::AddAnimation(IN CONST Animation &animation)
    {
        if (animation.Keys.empty())
            return INVALID_HANDLE;
        m_animations.pushBack(animation);
        if(m_animations.size() == 1)
            ForceSetActiveAnimation(0);
        return m_animations.size() - 1;
    }

    VOID SpriteComponent::SetActiveAnimation(IN Handle animation)
    {
        if (animation == m_activeAnimationHandle)
            return;
        ForceSetActiveAnimation(animation);
    }

    VOID SpriteComponent::ForceSetActiveAnimation(IN Handle animation)
    {
        IA_RELEASE_ASSERT((animation != INVALID_HANDLE) && (animation < m_animations.size()));
        m_prevAnimationKeyFrameIndex = 0;
        m_activeAnimation = m_animations[animation];

        m_prevAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 0);
        m_nextAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 1);

        m_currentAnimationState = m_prevAnimationKeyFrame;
        m_activeAnimationHandle = animation;

        SetTexture(m_currentAnimationState.Texture);
    }

    VOID SpriteComponent::SetAnimationFrame(IN INT32 animation, IN INT32 frame)
    {
        m_activeAnimation = {};
        m_currentAnimationState = m_animations[animation].Keys[frame];
    }

    VOID SpriteComponent::UpdateAnimation()
    {
        const auto keyCount = m_activeAnimation.Keys.size();
        if (keyCount <= 1)
            return;

        if (m_currentAnimationState.ShouldInterpolate)
        {
            const auto t = m_timelinePosition / m_prevAnimationKeyFrame.Duration;
#define INTERP_PROPERTY(name)                                                                                          \
    m_currentAnimationState.name =                                                                                     \
        m_prevAnimationKeyFrame.name + (m_nextAnimationKeyFrame.name - m_prevAnimationKeyFrame.name) * t;

            INTERP_PROPERTY(Position);
            INTERP_PROPERTY(Rotation);
            INTERP_PROPERTY(Scale);
            INTERP_PROPERTY(ColorOverlay);

#undef INTERP_PROPERTY
        }

        if (m_timelinePosition >= m_prevAnimationKeyFrame.Duration)
        {
            m_prevAnimationKeyFrameIndex = (m_prevAnimationKeyFrameIndex + 1) % keyCount;
            if (!m_prevAnimationKeyFrameIndex && !m_activeAnimation.ShouldLoop)
            {
                m_activeAnimation = {};
                return;
            }
            m_prevAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 0);
            m_nextAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 1);
            m_currentAnimationState = m_prevAnimationKeyFrame;
            m_timelinePosition = 0;
        }
        m_timelinePosition += Engine::GetFrameDeltaTime() * 1000;
    }

    SpriteComponent::AnimationKeyFrame SpriteComponent::GetKeyFrame(IN INT32 index)
    {
        return m_activeAnimation.Keys[index % m_activeAnimation.Keys.size()];
    }
} // namespace ia::iae