/*
 * Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "adaptativeframecounter.h"
#include "clock.h"

AdaptativeFrameCounter::AdaptativeFrameCounter()
{
    m_frames = 0;
    m_partialFrames = 0;
    m_partialFps = 0;
    m_maxPartialFps = 0;
    m_frameDelaySum = 0;
    m_mediumFrameDelay = 0;
    m_lastFps = 0;
    m_lastFrame = 0;
    m_maxFps = 0;
    m_sleepMicros = 0;
    m_mediumFrameDelay = 0;
    m_lastFpsUpdate = g_clock.micros();
    m_lastPartialFpsUpdate = g_clock.micros();
}

bool AdaptativeFrameCounter::shouldProcessNextFrame()
{
    if(m_maxFps == 0)
        return true;

    ticks_t now = g_clock.micros();
    if(now - m_lastFrame < m_bestFrameDelay)
        return false;
    return true;
}

void AdaptativeFrameCounter::processNextFrame()
{
    ticks_t now = g_clock.micros();
    m_frames++;
    m_partialFrames++;
    m_frameDelaySum += now - m_lastFrame;
    m_lastFrame = now ;
}

bool AdaptativeFrameCounter::update()
{
    ticks_t now = g_clock.micros();
    ticks_t delta = now - m_lastPartialFpsUpdate;
    if(delta > 41000 && m_partialFrames > 0) {
        m_partialFps = m_partialFrames / (delta / 1000000.0f);
        m_lastPartialFpsUpdate = now;
        m_partialFrames = 0;
    }

    delta =  now - m_lastFpsUpdate;
    if(delta >= 1000000) {
        m_lastFps = m_frames;
        if(m_frames > 0)
            m_mediumFrameDelay = m_frameDelaySum / m_frames;
        else
            m_mediumFrameDelay = 0;
        m_lastFpsUpdate = now;
        m_frames = 0;
        m_frameDelaySum = 0;

        //dump << stdext::format("FPS => %d  Partial FPS => %d  Frame Delay Hit => %.2f%%", m_lastFps, (int)m_partialFps, getFrameDelayHit());
        return true;
    }
    return false;
}

void AdaptativeFrameCounter::setMaxFps(int maxFps)
{
    maxFps = std::max(std::min(maxFps, 1000), 0);

    if(maxFps != 0) {
        m_bestFrameDelay = 1000000 / maxFps;
    } else {
        m_maxPartialFps = 0;
        m_bestFrameDelay = 0;
    }
    m_maxFps = maxFps;
}

int AdaptativeFrameCounter::getMaximumSleepMicros()
{
    if(m_maxFps == 0)
        return 0;
    return m_lastFrame + m_bestFrameDelay - g_clock.micros();
}

float AdaptativeFrameCounter::getFrameDelayHit()
{
    if(m_bestFrameDelay > 0)
        return ((m_bestFrameDelay - std::abs(m_bestFrameDelay - m_mediumFrameDelay)) * 100.0f) / (float)m_bestFrameDelay;
    else
        return 100.0f;
}
