#include "Entity.h"

#include <Globals.h>
#include <math.h>

#include "Game.h"
#include "Globals.h"

Entity::Entity(const EntityType* zType,
    Framework::Model3DData* model,
    Framework::Model3DTexture* texture,
    int id,
    Framework::Vec3<float> position,
    float size)
    : FactoryCraftModel(),
      id(id),
      zType(zType),
      playerControlled(0),
      frameLength(0.f)
{
    pos = position;
    setModelData(model);
    setModelTextur(texture);
    currentFrame.duration = 0;
    rend = 1;
    setSize(size);
}

Entity::~Entity() {}

void Entity::api(char* message)
{
    switch (message[0])
    {
    case 0:
        { // add movement frame
            MovementFrame frame;
            frame.position.x = *(float*)(message += 1);
            frame.position.y = *(float*)(message += 4);
            frame.position.z = *(float*)(message += 4);
            frame.rotation = *(float*)(message += 4);
            frame.duration = *(float*)(message += 4);
            framesLock.lockWrite();
            frameLength += frame.duration;
            frames.add(frame);
            framesLock.unlockWrite();
            break;
        }
    }
}

bool Entity::tick(double time)
{
    double totalTime = time;
    if (frameLength > 1.f)
    {
        totalTime *= 1.5f;
    }
    framesLock.lockWrite();
    frameLength -= totalTime;
    framesLock.unlockWrite();
    framesLock.lockRead();
    if (frameLength < 0.f)
    {
        frameLength = 0.f;
    }
    while (totalTime > 0)
    {
        if (currentFrame.duration <= 0)
        {
            if (frames.getEntryCount() > 0)
            {
                currentFrame = frames.get(0);
                framesLock.lockWrite();
                frames.remove(0);
                framesLock.unlockWrite();
            }
            else
            {
                break;
            }
        }
        float t = min(currentFrame.duration, (float)totalTime);
        pos += (currentFrame.position - pos) * (t / currentFrame.duration);
        if (getZRotation() - currentFrame.rotation > (float)PI)
        {
            setRotationZ(getZRotation() - 2.f * (float)PI);
        }
        else if (currentFrame.rotation - getZRotation() > (float)PI)
        {
            setRotationZ(getZRotation() + 2.f * (float)PI);
        }
        setRotationZ(getZRotation()
                     + (currentFrame.rotation - getZRotation())
                           * (t / currentFrame.duration));
        currentFrame.duration -= t;
        totalTime -= t;
        if (currentFrame.duration <= 0)
        {
            pos = currentFrame.position;
            setRotationZ(currentFrame.rotation);
        }
        rend = 1;
    }
    framesLock.unlockRead();
    if (playerControlled)
    {
        World::INSTANCE->zKamera()->setPosition(
            pos + Vec3<float>(0.f, 0.f, 1.5f));
        Model3D* target = World::INSTANCE->getCurrentTarget();
        Block* b = target ? dynamic_cast<Block*>(target) : 0;
        ((Game*)(Menu*)menuRegister->get("game"))
            ->updatePosition(
                pos, b != 0, b ? b->getLocation() : Vec3<int>(0, 0, 0));
        if (target) target->release();
    }
    return Model3D::tick(time);
}

int Entity::getId() const
{
    return id;
}

const EntityType* Entity::zEntityType() const
{
    return zType;
}

void Entity::setPlayerControlled()
{
    playerControlled = 1;
    World::INSTANCE->zKamera()->setPosition(pos + Vec3<float>(0.f, 0.f, 1.5f));
}