#include "RenderThread.h"

#include <iostream>

#include "Logging.h"
#include "Screen.h"
#include "Timer.h"

using namespace Framework;

// Contents of the RenderTh class from RenderThread.h
// Constructor
RenderTh::RenderTh()
    : Thread(),
      stop(0),
      screen(0),
      timer(new Timer()),
      renderTickTime(1 / 60),
      renderParameter(0),
      tickParameter(0),
      renderFunktion(0),
      tickFunktion(0),
      pause(0),
      maxFps(30),
      quiet(0)
{}

// Destructor
RenderTh::~RenderTh()
{
    terminate();
    if (screen) screen->release();
    timer->release();
}

// non-constant
Lock& RenderTh::readLock()
{
    return rwLock.getReadLock();
}

Lock& RenderTh::writeLock()
{
    return rwLock.getWriteLock();
}

void RenderTh::setScreen(Screen* bildschirm) // sets the screen
{
    rwLock.lockWrite();
    if (this->screen) this->screen->release();
    this->screen = bildschirm;
    rwLock.unlockWrite();
}

void RenderTh::thread() // Render loop
{
    int val = 0;
    double time = 0;
    timer->measureStart();
    double ausgleich = 0;
    while (!stop)
    {
        rwLock.lockRead();
        if (screen && !pause)
        {
            if (renderFunktion)
                renderFunktion(renderParameter, this, screen->zRenderImage());
            screen->render();
            val++;
            if (tickFunktion) tickFunktion(tickParameter, this, renderTickTime);
            screen->tick(renderTickTime);
            time += renderTickTime;
            rwLock.unlockRead();
            if (time > 1)
            {
                time -= 1;
                if (!quiet)
                {
                    Logging::info() << "FPS: " << val << "\n";
                }
                val = 0;
            }
        }
        else
        {
            rwLock.unlockRead();
            Sleep(100);
        }
        ausgleich += 1.0 / maxFps - renderTickTime;
        if (ausgleich > 0) Sleep((int)(ausgleich * 1000));
        timer->measureEnd();
        timer->measureStart();
        renderTickTime = timer->getSekunden();
    }
    timer->measureEnd();
}

void RenderTh::beginn() // starts rendering
{
    stop = 0;
    start();
}

void RenderTh::terminate() // terminates the thread
{
    stop = 1;
    waitForThread(2000);
    if (run) ende();
}

void RenderTh::setQuiet(
    bool quiet) // if true, then nothing will be printent to std::cout
{
    this->quiet = quiet;
}

void RenderTh::setMaxFps(int fps) // sets the number of frames per second
{
    maxFps = fps;
}

void RenderTh::setPause(bool p) // Render pause
{
    pause = p;
}

void RenderTh::setRenderFunktion(std::function<void(void*, void*, Image*)>
        rF) // sets the callback function for rendering
{
    renderFunktion = rF;
}

void RenderTh::setTickFunktion(std::function<void(void*, void*, double)>
        tF) // sets the callback function for tick
{
    tickFunktion = tF;
}

void RenderTh::setRenderFunktionParameter(
    void* p) // sets the parameter of the callback function for rendering
{
    renderParameter = p;
}

void RenderTh::setTickFunktionParameter(
    void* p) // sets the parameter of the callback function for tick
{
    tickParameter = p;
}

// constant
Screen* RenderTh::getScreen() const // returns the screen
{
    return screen ? dynamic_cast<Screen*>(screen->getThis()) : 0;
}

Screen* RenderTh::zScreen() const
{
    return screen;
}

double RenderTh::getRenderTickZeit()
    const // returns the time needed for rendering and tick
{
    return renderTickTime;
}