/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu 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.

Hikou no mizu 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 Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Animation.hpp"

#include <cmath>

namespace
{
    /// Tolerated margin of error for animation timing checks
    constexpr float ANIMATION_EPSILON = .0001f;
}

Animation::Animation() : m_frameIx(0), m_duration(0.f), m_time(0.f),
m_xScale(1.f), m_yScale(1.f)
{

}

void Animation::addFrame(const Frame &frame)
{
    m_framesList.push_back(frame);
}

void Animation::reset()
{
    goToTime(0.f);
}

void Animation::goToTime(float time)
{
    if (m_duration > 0.f && time >= 0.f)
    {
        m_time = (time <= m_duration) ? time : m_duration;

        std::size_t frameIx = static_cast<std::size_t>(
            static_cast<float>(m_framesList.size()) * (m_time / m_duration)
        );

        if (m_frameIx != frameIx && frameIx < m_framesList.size())
            m_frameIx = frameIx;
    }
}

void Animation::goForward(float distance)
{
    goToTime(m_time + distance);
}

bool Animation::goInLoop(float distance)
{
    const float advancedTime = m_time + distance;
    goToTime(std::fmod(advancedTime, m_duration));

    return (advancedTime > m_duration);
}

std::size_t Animation::getFrameSize() const
{
    return m_framesList.size();
}

void Animation::setPath(const std::string &path)
{
    m_path = path;
}

const std::string &Animation::getPath() const
{
    return m_path;
}

void Animation::setDuration(float duration)
{
    if (duration > 0.f)
        m_duration = duration;
}

float Animation::getDuration() const
{
    return m_duration;
}

void Animation::setScale(float scale)
{
    m_xScale = scale;
    m_yScale = scale;
}

float Animation::getXScale() const
{
    return m_xScale;
}

float Animation::getYScale() const
{
    return m_yScale;
}

float Animation::getCurrentTime() const
{
    return m_time;
}

std::size_t Animation::getCurrentFrame() const
{
    return m_frameIx;
}

bool Animation::atInitialState() const
{
    return (m_time <= ANIMATION_EPSILON);
}

bool Animation::atEndedState() const
{
    return (m_time >= m_duration - ANIMATION_EPSILON);
}

bool Animation::getSourceBox(Box &target) const
{
    if (m_frameIx < m_framesList.size())
    {
        target = m_framesList[m_frameIx].srcBox;
        return true;
    }

    return false;
}

bool Animation::getBodyBox(Box &target) const
{
    if (m_frameIx < m_framesList.size())
    {
        target = m_framesList[m_frameIx].bodyBox;
        return true;
    }

    return false;
}

float Animation::getWidth() const
{
    if (m_frameIx < m_framesList.size())
        return m_framesList[m_frameIx].srcBox.width;

    return 0.f;
}

float Animation::getHeight() const
{
    if (m_frameIx < m_framesList.size())
        return m_framesList[m_frameIx].srcBox.height;

    return 0.f;
}
