#include <OgreSceneNode.h>

#include "core/ConfigVar.h"
#include "core/DebugDraw.h"
#include "core/Entity.h"
#include "core/Logger.h"


ConfigVar cv_debug_entity("debug_entity", "Draw entity debug info", "0");


Entity::Entity(const Ogre::String& name, Ogre::SceneNode* node):
    m_Name(name),
    m_SceneNode(node),

    m_Height(1.0f),

    m_SolidRadius(0.24f),
    m_Solid(false),

    m_TalkRadius(0.45f),
    m_Talkable(false),

    m_State(Entity::NONE),

    m_MoveAutoSpeed(0.7f),
    m_MoveWalkSpeed(0.7f),
    m_MoveRunSpeed(0.8f),
    m_MovePosition(Ogre::Vector3(0, 0, 0)),
    m_MoveEntity(nullptr),
    m_MoveStopDistance(0.0f),
    m_MoveTriangleId(-1),
    m_MoveAutoRotation(true),
    m_MoveAutoAnimation(true),
    m_MoveAnimationWalk("Walk"),
    m_MoveAnimationRun("Run"),

    m_LinearMovement(LM_UP_TO_DOWN),
    m_LinearStart(0.0f, 0.0f, 0.0f),
    m_LinearEnd(0.0f, 0.0f, 0.0f),

    m_JumpStart(0.0f, 0.0f, 0.0f),
    m_JumpEnd(0.0f, 0.0f, 0.0f),
    m_JumpSeconds(0.0f),
    m_JumpCurrentSeconds(0.0f),

    m_OffsetPositionStart(0.0f, 0.0f, 0.0f),
    m_OffsetPositionEnd(0.0f, 0.0f, 0.0f),
    m_OffsetType(AT_NONE),
    m_OffsetSeconds(0.0f),
    m_OffsetCurrentSeconds(0.0f),

    m_TurnDirection(TD_CLOSEST),
    m_TurnDirectionStart(0),
    m_TurnDirectionEnd(0),
    m_TurnEntity(nullptr),
    m_TurnType(AT_NONE),
    m_TurnSeconds(0.0f),
    m_TurnCurrentSeconds(0.0f),

    m_AnimationSpeed(1.0f),
    m_AnimationDefault("Idle"),
    m_AnimationCurrentName(""),
    m_AnimationAutoPlay(true)
{
    m_model_root_node = m_SceneNode->createChildSceneNode();
    m_ModelNode = m_model_root_node->createChildSceneNode();

    m_Direction = new EntityDirection();
    m_Direction->setMaterial("entity/direction");
    m_DirectionNode = m_SceneNode->createChildSceneNode();
    m_DirectionNode->attachObject(m_Direction);

    m_SolidCollision = new EntityCollision();
    m_SolidCollision->setMaterial("entity/solid_collision");
    m_SolidCollisionNode = m_SceneNode->createChildSceneNode();
    m_SolidCollisionNode->setScale(m_SolidRadius, m_SolidRadius, m_Height);
    m_SolidCollisionNode->attachObject(m_SolidCollision);

    m_TalkCollision = new EntityCollision();
    m_TalkCollision->setMaterial("entity/talk_collision");
    m_TalkCollisionNode = m_SceneNode->createChildSceneNode();
    m_TalkCollisionNode->setScale(m_TalkRadius, m_TalkRadius, m_Height);
    m_TalkCollisionNode->attachObject(m_TalkCollision);

    m_model_root_node->setPosition(Ogre::Vector3::ZERO);
    m_ModelNode->setPosition(Ogre::Vector3::ZERO);
    m_SceneNode->setPosition(Ogre::Vector3::ZERO);

    LOG_TRIVIAL("Entity \"" + m_Name + "\" created.");
}


Entity::~Entity()
{
    delete m_SolidCollision;
    delete m_TalkCollision;

    m_SceneNode->removeAndDestroyAllChildren();

    LOG_TRIVIAL("Entity \"" + m_Name + "\" destroyed.");
}


void
Entity::Update()
{
    if(m_MoveEntity != nullptr)
    {
        m_MovePosition = m_MoveEntity->GetPosition();
        m_MoveStopDistance = GetSolidRadius() + m_MoveEntity->GetSolidRadius();
    }

    if(m_TurnEntity != nullptr)
    {
        Ogre::Degree angle = GetDirectionToEntity(m_TurnEntity);
        angle = CalculateTurnAngle(m_TurnDirectionStart, angle);
        m_TurnDirectionEnd = angle;
    }
}


void
Entity::UpdateDebug()
{
    int debug = cv_debug_entity.GetI();

    m_DirectionNode->setVisible(debug > 0);
    m_SolidCollisionNode->setVisible(debug > 0 && m_Solid);
    m_TalkCollisionNode->setVisible(debug > 0 && m_Talkable);

    // debug output
    if(debug > 0)
    {
        DEBUG_DRAW.SetColour(Ogre::ColourValue::White);
        DEBUG_DRAW.SetScreenSpace(true);
        DEBUG_DRAW.SetTextAlignment(DEBUG_DRAW.CENTER);
        DEBUG_DRAW.SetFadeDistance(40, 50);

        Ogre::Vector3 entity_pos = GetPosition();

        DEBUG_DRAW.Text(entity_pos, 0, 0, m_Name);
        DEBUG_DRAW.Text(entity_pos, 0, 12, m_AnimationCurrentName);

        if(debug > 1)
        {
            static Ogre::String move_state_string[] = { "NONE", "WALKMESH", "LINEAR", "JUMP" };

            DEBUG_DRAW.Text(entity_pos, 0, 24, Ogre::StringConverter::toString(entity_pos));
            DEBUG_DRAW.Text(entity_pos, 0, 36, "Triangle: " + Ogre::StringConverter::toString(m_MoveTriangleId));
            DEBUG_DRAW.Text(entity_pos, 0, 48, "Move state: " + move_state_string[m_State]);
            switch(m_State)
            {
                case Entity::WALKMESH:
                {
                    DEBUG_DRAW.Line3d(entity_pos, m_MovePosition);
                }
                break;

                case Entity::LINEAR:
                {
                    DEBUG_DRAW.Text(m_LinearStart, 0, 0, "Start: " + Ogre::StringConverter::toString(m_LinearStart));
                    DEBUG_DRAW.Text(m_LinearEnd, 0, 0, "End: " + Ogre::StringConverter::toString(m_LinearEnd));
                    DEBUG_DRAW.Line3d(m_LinearStart, m_LinearEnd);
                }
                break;

                case Entity::JUMP:
                {
                    DEBUG_DRAW.Text(m_JumpStart, 0, 0, "Start: " + Ogre::StringConverter::toString(m_JumpStart));
                    DEBUG_DRAW.Text(m_JumpEnd, 0, 0, "End: " + Ogre::StringConverter::toString(m_JumpEnd));
                    Ogre::Vector3 distance = m_JumpEnd - m_JumpStart;
                    Ogre::Vector3 pos1, pos2, pos3;
                    pos1.x = m_JumpStart.x + distance.x * 0.25f;
                    pos1.y = m_JumpStart.y + distance.y * 0.25f;
                    float current1 = m_JumpSeconds / 4;
                    pos1.z = current1 * current1 * -13.08f + current1 * ((distance.z) / m_JumpSeconds + m_JumpSeconds * 13.08f) + m_JumpStart.z;
                    pos2.x = m_JumpStart.x + distance.x * 0.5f;
                    pos2.y = m_JumpStart.y + distance.y * 0.5f;
                    float current2 = current1 * 2;
                    pos2.z = current2 * current2 * -13.08f + current2 * ((distance.z) / m_JumpSeconds + m_JumpSeconds * 13.08f) + m_JumpStart.z;
                    pos3.x = m_JumpStart.x + distance.x * 0.75f;
                    pos3.y = m_JumpStart.y + distance.y * 0.75f;
                    float current3 = current1 * 3;
                    pos3.z = current3 * current3 * -13.08f + current3 * ((distance.z) / m_JumpSeconds + m_JumpSeconds * 13.08f) + m_JumpStart.z;
                    DEBUG_DRAW.Line3d(m_JumpStart, pos1);
                    DEBUG_DRAW.Line3d(pos1, pos2);
                    DEBUG_DRAW.Line3d(pos2, pos3);
                    DEBUG_DRAW.Line3d(pos3, m_JumpEnd);
                }
                break;
            }
        }
    }
}


const Ogre::String&
Entity::GetName() const
{
    return m_Name;
}


void
Entity::SetPosition(const Ogre::Vector3& position)
{
    m_SceneNode->setPosition(position);
}


void
Entity::ScriptSetPosition(const float x, const float y, const float z)
{
    SetPosition(Ogre::Vector3(x, y, z));
    // if we set it from script - reset walkmesh triangle to reattach entity to walkmesh again if needed
    m_MoveTriangleId = -1;
}


const Ogre::Vector3
Entity::GetPosition() const
{
    return m_SceneNode->getPosition();
}


void
Entity::ScriptGetPosition() const
{
    Ogre::Vector3 position = m_SceneNode->getPosition();
    ScriptManager::getSingleton().AddValueToStack(position.x);
    ScriptManager::getSingleton().AddValueToStack(position.y);
    ScriptManager::getSingleton().AddValueToStack(position.z);
}


void
Entity::SetOffset(const Ogre::Vector3& position)
{
    assert(m_model_root_node);
    m_model_root_node->setPosition(position);
}


const Ogre::Vector3
Entity::GetOffset() const
{
    assert(m_model_root_node);
    return m_model_root_node->getPosition();
}


void
Entity::SetRotation(const Ogre::Degree& rotation)
{
    assert(m_model_root_node);
    float angle = rotation.valueDegrees() - Ogre::Math::Floor(rotation.valueDegrees() / 360.0f) * 360.0f;

    if(angle < 0)
    {
        angle = 360 + angle;
    }

    Ogre::Quaternion q;
    Ogre::Vector3 vec = Ogre::Vector3::UNIT_Z;
    q.FromAngleAxis(Ogre::Radian(Ogre::Degree(angle)), vec);
    m_model_root_node->setOrientation(q);
    m_DirectionNode->setOrientation(q);
}


void
Entity::ScriptSetRotation(const float rotation)
{
    SetRotation(Ogre::Degree(rotation));
}


Ogre::Degree
Entity::GetRotation() const
{
    assert(m_model_root_node);
    Ogre::Quaternion q = m_model_root_node->getOrientation();
    Ogre::Degree temp;
    Ogre::Vector3 vec = Ogre::Vector3::UNIT_Z;
    q.ToAngleAxis(temp, vec);

    return temp;
}


float
Entity::ScriptGetRotation() const
{
    return GetRotation().valueDegrees();
}


void Entity::setScale(const Ogre::Vector3 &scale)
{
    assert(m_model_root_node);
    m_model_root_node->setScale(scale);
}


void Entity::setRootOrientation(const Ogre::Quaternion &root_orientation)
{
    assert(m_ModelNode);
    m_ModelNode->setOrientation(root_orientation);
}


float
Entity::GetHeight() const
{
    return m_Height;
}


void
Entity::SetSolidRadius(const float radius)
{
    m_SolidRadius = radius;
    m_SolidCollisionNode->setScale(m_SolidRadius, m_SolidRadius, m_Height);
}


float
Entity::GetSolidRadius() const
{
    return m_SolidRadius;
}


void
Entity::SetSolid(const bool solid)
{
    m_Solid = solid;
}


bool
Entity::IsSolid() const
{
    return m_Solid;
}


void
Entity::SetTalkRadius(const float radius)
{
    m_TalkRadius = radius;
    m_TalkCollisionNode->setScale(m_TalkRadius, m_TalkRadius, m_Height);
}


float
Entity::GetTalkRadius() const
{
    return m_TalkRadius;
}


void
Entity::SetTalkable(const bool talkable)
{
    m_Talkable = talkable;
}


bool
Entity::IsTalkable() const
{
    return m_Talkable;
}


void
Entity::SetState(const Entity::State state)
{
    m_State = state;
}


Entity::State
Entity::GetState() const
{
    return m_State;
}


void
Entity::SetMoveAutoSpeed(const float speed)
{
    m_MoveAutoSpeed = speed;
}


float
Entity::GetMoveAutoSpeed() const
{
    return m_MoveAutoSpeed;
}


void
Entity::SetMoveWalkSpeed(const float speed)
{
    m_MoveWalkSpeed = speed;
}


float
Entity::GetMoveWalkSpeed() const
{
    return m_MoveWalkSpeed;
}


void
Entity::SetMoveRunSpeed(const float speed)
{
    m_MoveRunSpeed = speed;
}


float
Entity::GetMoveRunSpeed() const
{
    return m_MoveRunSpeed;
}


void
Entity::SetMovePosition(const Ogre::Vector3& target)
{
    m_MovePosition = target;
}


const Ogre::Vector3&
Entity::GetMovePosition() const
{
    return m_MovePosition;
}


float
Entity::GetMoveStopDistance() const
{
    return m_MoveStopDistance;
}


void
Entity::SetMoveTriangleId(const int triangle)
{
    m_MoveTriangleId = triangle;
}


int
Entity::GetMoveTriangleId() const
{
    return m_MoveTriangleId;
}


void
Entity::SetMoveAutoRotation(const bool rotate)
{
    m_MoveAutoRotation = rotate;
}


bool
Entity::GetMoveAutoRotation() const
{
    return m_MoveAutoRotation;
}


void
Entity::SetMoveAutoAnimation(const bool animate)
{
    m_MoveAutoAnimation = animate;
}


bool
Entity::GetMoveAutoAnimation() const
{
    return m_MoveAutoAnimation;
}


const Ogre::String&
Entity::GetMoveAnimationWalkName() const
{
    return m_MoveAnimationWalk;
}


const Ogre::String&
Entity::GetMoveAnimationRunName() const
{
    return m_MoveAnimationRun;
}


void
Entity::ScriptMoveToPosition(const float x, const float y)
{
    m_State = Entity::WALKMESH;

    m_MovePosition = Ogre::Vector3(x, y, 0);
    m_MoveEntity = nullptr;
    m_MoveStopDistance = 0;
    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" set move to walkmesh position \"" + Ogre::StringConverter::toString(m_MovePosition) + "\".");
}


void
Entity::ScriptMoveToEntity(Entity* entity)
{
    m_State = Entity::WALKMESH;

    m_MovePosition = entity->GetPosition();
    m_MoveEntity = entity;
    m_MoveStopDistance = GetSolidRadius() + entity->GetSolidRadius();
    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" set move to entity \"" + entity->GetName() + "\".");
}


int
Entity::ScriptMoveSync()
{
    ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();
    m_Sync.push_back(script);

    LOG_TRIVIAL("[SCRIPT] Wait entity \"" + m_Name + "\" move for function \"" + script.function + "\" in script entity \"" + script.entity + "\".");

    return -1;
}


void
Entity::UnsetMove()
{
    m_State = Entity::NONE;
    m_MoveEntity = nullptr;
    m_MoveStopDistance = 0;

    for(const auto &script : m_Sync)
    {
        ScriptManager::getSingleton().ContinueScriptExecution(script);
    }
    m_Sync.clear();
}


void
Entity::ScriptLinearToPosition(const float x, const float y, const float z, const LinearMovement movement, const char* animation)
{
    Ogre::Vector3 pos = Ogre::Vector3(x, y, z);
    SetLinear(pos, movement, animation);

    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" set linear move to position \"" + Ogre::StringConverter::toString(pos) + "\" with animation \"" + animation + "\".");
}


int
Entity::ScriptLinearSync()
{
    ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();

    LOG_TRIVIAL("[SCRIPT] Wait entity \"" + m_Name + "\" linear move for function \"" + script.function + "\" in script entity \"" + script.entity + "\".");

    m_Sync.push_back(script);
    return -1;
}


void
Entity::SetLinear(const Ogre::Vector3& end, const LinearMovement movement, const Ogre::String& animation)
{
    m_State = Entity::LINEAR;
    m_LinearMovement = movement;
    m_LinearStart = GetPosition();
    m_LinearEnd = end;

    // linear animation
    m_AnimationAutoPlay = false;
    PlayAnimation(animation, Entity::AUTO_ANIMATION, Entity::PLAY_LOOPED, 0, -1);

    // after move we need reattach entity to walkmesh
    m_MoveTriangleId = -1;
}


void
Entity::UnsetLinear()
{
    m_State = Entity::NONE;

    m_AnimationAutoPlay = true;
    PlayAnimation(m_AnimationDefault, Entity::AUTO_ANIMATION, Entity::PLAY_LOOPED, 0, -1);

    for(size_t i = 0; i < m_Sync.size(); ++i)
    {
        ScriptManager::getSingleton().ContinueScriptExecution(m_Sync[i]);
    }
    m_Sync.clear();
}


LinearMovement
Entity::GetLinearMovement() const
{
    return m_LinearMovement;
}


const Ogre::Vector3&
Entity::GetLinearStart() const
{
    return m_LinearStart;
}


const Ogre::Vector3&
Entity::GetLinearEnd() const
{
    return m_LinearEnd;
}


void
Entity::ScriptJumpToPosition(const float x, const float y, const float z, const float seconds)
{
    Ogre::Vector3 jump_to(x, y, z);
    SetJump(jump_to, seconds);

    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" set jump to position \"" + Ogre::StringConverter::toString(jump_to) + "\" in " + Ogre::StringConverter::toString(seconds) + " seconds.");
}


int
Entity::ScriptJumpSync()
{
    ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();

    LOG_TRIVIAL("[SCRIPT] Wait entity \"" + m_Name + "\" jump for function \"" + script.function + "\" in script entity \"" + script.entity + "\".");

    m_Sync.push_back(script);
    return -1;
}


void
Entity::SetJump(const Ogre::Vector3& jump_to, const float seconds)
{
    m_State = Entity::JUMP;
    m_JumpStart = GetPosition();
    m_JumpEnd = jump_to;
    m_JumpSeconds = seconds;
    m_JumpCurrentSeconds = 0;

    // after move we need reattach entity to walkmesh
    m_MoveTriangleId = -1;
}


void
Entity::UnsetJump()
{
    m_State = Entity::NONE;

    for(size_t i = 0; i < m_Sync.size(); ++i)
    {
        ScriptManager::getSingleton().ContinueScriptExecution(m_Sync[i]);
    }
    m_Sync.clear();
}


const Ogre::Vector3&
Entity::GetJumpStart() const
{
    return m_JumpStart;
}


const Ogre::Vector3&
Entity::GetJumpEnd() const
{
    return m_JumpEnd;
}


float
Entity::GetJumpSeconds() const
{
    return m_JumpSeconds;
}


void
Entity::SetJumpCurrentSeconds(const float seconds)
{
    m_JumpCurrentSeconds = seconds;
}


float
Entity::GetJumpCurrentSeconds() const
{
    return m_JumpCurrentSeconds;
}


void
Entity::ScriptOffsetToPosition(const float x, const float y, const float z, const ActionType type, const float seconds)
{
    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" set offset to position \"" + Ogre::StringConverter::toString(Ogre::Vector3(x, y, z)) + "'.");

    Ogre::Vector3 position = Ogre::Vector3(x, y, z);

    if(type == AT_NONE)
    {
        this->SetOffset(position);
        return;
    }

    m_OffsetPositionStart = GetOffset();
    m_OffsetPositionEnd = position;
    m_OffsetType = type;
    m_OffsetSeconds = seconds;
    m_OffsetCurrentSeconds = 0;
}


int
Entity::ScriptOffsetSync()
{
    ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();

    LOG_TRIVIAL("[SCRIPT] Wait entity \"" + m_Name + "\" offset for function \"" + script.function + "\" in script entity \"" + script.entity + "\".");

    m_OffsetSync.push_back(script);
    return -1;
}


void
Entity::UnsetOffset()
{
    m_OffsetType = AT_NONE;

    for(unsigned int i = 0; i < m_OffsetSync.size(); ++i)
    {
        ScriptManager::getSingleton().ContinueScriptExecution(m_OffsetSync[i]);
    }
    m_OffsetSync.clear();
}


const Ogre::Vector3&
Entity::GetOffsetPositionStart() const
{
    return m_OffsetPositionStart;
}


const Ogre::Vector3&
Entity::GetOffsetPositionEnd() const
{
    return m_OffsetPositionEnd;
}


ActionType
Entity::GetOffsetType() const
{
    return m_OffsetType;
}


float
Entity::GetOffsetSeconds() const
{
    return m_OffsetSeconds;
}


void
Entity::SetOffsetCurrentSeconds(const float seconds)
{
    m_OffsetCurrentSeconds = seconds;
}


float
Entity::GetOffsetCurrentSeconds() const
{
    return m_OffsetCurrentSeconds;
}


void
Entity::ScriptTurnToDirection(const float direction, const TurnDirection turn_direction, const ActionType turn_type, const float seconds)
{
    SetTurn(Ogre::Degree(direction), nullptr, turn_direction, turn_type, seconds);
    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" turn to angle \"" + Ogre::StringConverter::toString(direction) + "\".");
}


void
Entity::ScriptTurnToEntity(Entity* entity, const TurnDirection turn_direction, const float seconds)
{
    if(entity == nullptr || entity == this)
    {
        LOG_ERROR("[SCRIPT] Turn to entity: Invalid entity pointer (NUUL or this).");
        return;
    }

    Ogre::Degree angle = GetDirectionToEntity(entity);
    SetTurn(angle, entity, turn_direction, AT_SMOOTH, seconds);
    LOG_TRIVIAL("[SCRIPT] Entity \"" + m_Name + "\" turn to entity \"" + entity->GetName() + ".");
}


int
Entity::ScriptTurnSync()
{
    ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();

    LOG_TRIVIAL("[SCRIPT] Wait entity \"" + m_Name + "\" turn for function \"" + script.function + "\" in script entity \"" + script.entity + "\".");

    m_TurnSync.push_back(script);
    return -1;
}


void
Entity::SetTurn(const Ogre::Degree& direction_to, Entity* entity, const TurnDirection turn_direction, const ActionType turn_type, const float seconds)
{
    if(turn_type == AT_NONE)
    {
        SetRotation(direction_to);
        return;
    }

    m_TurnDirection = turn_direction;

    Ogre::Degree angle_start = GetRotation();
    Ogre::Degree angle_end = CalculateTurnAngle(angle_start, direction_to);

    m_TurnEntity = entity;
    m_TurnDirectionStart = angle_start;
    m_TurnDirectionEnd = angle_end;
    m_TurnType = turn_type;
    m_TurnSeconds = seconds;
    m_TurnCurrentSeconds = 0;
}


void
Entity::UnsetTurn()
{
    m_TurnType = AT_NONE;

    for(unsigned int i = 0; i < m_TurnSync.size(); ++i)
    {
        ScriptManager::getSingleton().ContinueScriptExecution(m_TurnSync[i]);
    }
    m_TurnSync.clear();
}


Ogre::Degree
Entity::CalculateTurnAngle(const Ogre::Degree& start, const Ogre::Degree& end) const
{
    Ogre::Degree ret = end;

    switch(m_TurnDirection)
    {
        case TD_CLOCKWISE:
        {
            if(end <= start)
            {
                ret = end + Ogre::Degree(360);
            }
        }
        break;

        case TD_ANTICLOCKWISE:
        {
            if(end >= start)
            {
                ret = end - Ogre::Degree(360);
            }
        }
        break;

        case TD_CLOSEST:
        {
            Ogre::Degree delta = end - start;

            delta = (delta < Ogre::Degree(0)) ? -delta : delta;

            if(delta > Ogre::Degree(180))
            {
                if(start < end)
                {
                    ret = end - Ogre::Degree(360);
                }
                else
                {
                    ret = end + Ogre::Degree(360);
                }
            }
        }
        break;
    }

    return ret;
}


Ogre::Degree
Entity::GetTurnDirectionStart() const
{
    return m_TurnDirectionStart;
}


Ogre::Degree
Entity::GetTurnDirectionEnd() const
{
    return m_TurnDirectionEnd;
}


ActionType
Entity::GetTurnType() const
{
    return m_TurnType;
}


float
Entity::GetTurnSeconds() const
{
    return m_TurnSeconds;
}


void
Entity::SetTurnCurrentSeconds(const float seconds)
{
    m_TurnCurrentSeconds = seconds;
}


float
Entity::GetTurnCurrentSeconds() const
{
    return m_TurnCurrentSeconds;
}


void
Entity::ScriptSetAnimationSpeed(const float speed)
{
    m_AnimationSpeed = speed;
}


const Ogre::String&
Entity::GetDefaultAnimationName() const
{
    return m_AnimationDefault;
}


const Ogre::String&
Entity::GetCurrentAnimationName() const
{
    return m_AnimationCurrentName;
}


Entity::AnimationState
Entity::GetAnimationState() const
{
    return m_AnimationState;
}


void
Entity::ScriptPlayAnimation(const char* name)
{
    PlayAnimation(Ogre::String(name), Entity::REQUESTED_ANIMATION, Entity::PLAY_DEFAULT, 0, -1);
}


void
Entity::ScriptPlayAnimationStop(const char* name)
{
    PlayAnimation(Ogre::String(name), Entity::REQUESTED_ANIMATION, Entity::PLAY_ONCE, 0, -1);
}


void
Entity::ScriptPlayAnimation(const char* name, const float start, const float end)
{
    PlayAnimation(Ogre::String(name), Entity::REQUESTED_ANIMATION, Entity::PLAY_DEFAULT, start, end);
}


void
Entity::ScriptPlayAnimationStop(const char* name, const float start, const float end)
{
    PlayAnimation(Ogre::String(name), Entity::REQUESTED_ANIMATION, Entity::PLAY_ONCE, start, end);
}


void
Entity::ScriptSetDefaultAnimation(const char* animation)
{
    m_AnimationDefault = Ogre::String(animation);
}


int
Entity::ScriptAnimationSync()
{
    ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();

    LOG_TRIVIAL("[SCRIPT] Wait entity \"" + m_Name + "\" animation for function \"" + script.function + "\" in script entity \"" + script.entity + "\".");

    m_AnimationSync.push_back(script);
    return -1;
}


Ogre::Degree
Entity::GetDirectionToEntity(Entity* entity) const
{
    Ogre::Vector3 current_point = GetPosition();
    Ogre::Vector3 direction_point = entity->GetPosition();

    Ogre::Vector2 up(0.0f, -1.0f);
    Ogre::Vector2 dir(direction_point.x - current_point.x, direction_point.y - current_point.y);
    // angle between vectors
    Ogre::Degree angle(Ogre::Radian(acosf(dir.dotProduct(up) / (dir.length() * up.length()))));
    return (dir.x < 0) ? Ogre::Degree(360) - angle : angle;
}
