#include "Camera2D.h"

#include "Globals.h"
#include "Image.h"
#include "World2D.h"

using namespace Framework;

Camera2D::Camera2D()
    : DrawableBackground()
{
    setStyle(Style::Visible);
    world = 0;
    rotation = 0;
    matrix = Mat3<float>::identity();
    zoom = 1;
    tickWorld = 0;
    name = new Text("");
}

Camera2D::~Camera2D()
{
    if (world) world->release();
    name->release();
}

void Camera2D::setName(const char* name)
{
    this->name->setText(name);
}

void Camera2D::setName(Text* name)
{
    this->name->setText(*name);
    name->release();
}

void Camera2D::lookAtWorldPos(float x, float y)
{
    rend |= wPos != Vertex(x, y);
    wPos.x = x;
    wPos.y = y;
    if (world && world->getWorldInfo().hasSize
        && world->getWorldInfo().circular)
    {
        if (wPos.x < 0) wPos.x += (float)world->getWorldInfo().size.x;
        if (wPos.y < 0) wPos.y += (float)world->getWorldInfo().size.y;
        if (wPos.x > (float)world->getWorldInfo().size.x)
            wPos.x -= (float)world->getWorldInfo().size.x;
        if (wPos.y > (float)world->getWorldInfo().size.y)
            wPos.y -= (float)world->getWorldInfo().size.y;
    }
}

void Camera2D::lookAtWorldPos(Vertex pos)
{
    lookAtWorldPos(pos.x, pos.y);
}

void Camera2D::lookAtWorldArea(float width, float height)
{
    zoom = (float)getWidth() / width;
    // TODO have two scaling factors
}

void Camera2D::setRotation(float rotation)
{
    rend |= this->rotation != rotation;
    this->rotation = rotation;
}

void Camera2D::setZoom(float zoom)
{
    rend |= this->zoom != zoom;
    this->zoom = zoom;
}

void Camera2D::setWorld(World2D* welt, bool tick)
{
    if (this->world) this->world->release();
    this->world = welt;
    tickWorld = tick;
    rend = 1;
}

bool Camera2D::tick(double time)
{
    if (world && tickWorld) rend |= world->tick(time);
    return DrawableBackground::tick(time);
}

void Camera2D::render(Image& zRObj)
{
    if (hasStyleNot(Style::Visible)) return;
    DrawableBackground::render(zRObj);
    if (!world) return;
    rwLock.lockRead();
    if (!zRObj.setDrawOptions(innenPosition, innenSize))
    {
        rwLock.unlockRead();
        return;
    }
    matrix = Mat3<float>::translation((Vertex)gr / 2)
           * Mat3<float>::rotation(rotation) * Mat3<float>::scaling(zoom)
           * Mat3<float>::translation(-wPos);
    world->render(matrix, gr, zRObj, *name);
    zRObj.releaseDrawOptions();
    rwLock.unlockRead();
}

Vertex Camera2D::getWorldCoordinates(Point screenPos)
{
    Vertex wKoord
        = (Mat3<float>::translation(wPos) * Mat3<float>::scaling(1 / zoom)
              * Mat3<float>::rotation(-rotation)
              * Mat3<float>::translation((Vertex)gr / -2))
        * (Vertex)screenPos;
    if (world->getWorldInfo().circular && world->getWorldInfo().hasSize
        && world->getWorldInfo().size.x && world->getWorldInfo().size.y)
    {
        while (wKoord.x < 0)
            wKoord.x += (float)world->getWorldInfo().size.x;
        while (wKoord.x > (float)world->getWorldInfo().size.x)
            wKoord.x -= (float)world->getWorldInfo().size.x;
        while (wKoord.y < 0)
            wKoord.y += (float)world->getWorldInfo().size.y;
        while (wKoord.y > (float)world->getWorldInfo().size.y)
            wKoord.y -= (float)world->getWorldInfo().size.y;
    }
    return wKoord;
}

Vertex Camera2D::getWorldDirection(Vertex dir)
{
    return Vertex(dir.x, dir.y).rotation(-rotation) * (1 / zoom);
}

Vertex Camera2D::getWorldPosition()
{
    return wPos;
}

float Camera2D::getRotation()
{
    return rotation;
}

float Camera2D::getZoom()
{
    return zoom;
}

const Mat3<float>& Camera2D::getMatrix()
{
    return matrix;
}

Text* Camera2D::getName()
{
    return dynamic_cast<Text*>(name->getThis());
}

Text* Camera2D::zName()
{
    return name;
}

bool TestCamera2D::tick(double time)
{
    bool ret = rend;
    rend = Camera2D::tick(time);
    if (getKeyState('q'))
    {
        rotation += (float)(time * 0.5);
        ret = 1;
    }
    if (getKeyState('e'))
    {
        rotation -= (float)(time * 0.5);
        ret = 1;
    }
    Vertex move;
    if (getKeyState('w')) move = Vertex(0, -100 * (float)time);
    if (getKeyState('a')) move = Vertex(-100 * (float)time, 0);
    if (getKeyState('d')) move = Vertex(100 * (float)time, 0);
    if (getKeyState('s')) move = Vertex(0, +100 * (float)time);
    if (move != Vertex(0, 0))
    {
        wPos += move.rotation(-rotation) * (1 / zoom);
        ret = 1;
    }
    if (getKeyState('+'))
    {
        zoom += (zoom) * (float)time;
        ret = 1;
    }
    if (getKeyState('-'))
    {
        zoom -= (zoom) * (float)time;
        if (zoom <= 0) zoom = 1;
        ret = 1;
    }
    return ret;
}