#include <Item/IEquipment.hpp>
#include <Item/StatefulItem/Jetpack/Jetpack.hpp>
#include <MovementPhysics/AirMovementPhysics/AirMovementPhysics.hpp>

AirMovementPhysics AirMovementPhysics::instance;

void AirMovementPhysics::updatePlayer(Player &player)
{
    // Calculate motion direction from controls.
    glm::vec3 motionDirection(0.0f, 0.0f, 0.0f);

    if (player.controlFlags.contains(Player::ControlFlag::Forward))
    {
        motionDirection += glm::vec3(sin(player.theta), cos(player.theta), 0.0f);
    }

    if (player.controlFlags.contains(Player::ControlFlag::Back))
    {
        motionDirection -= glm::vec3(sin(player.theta), cos(player.theta), 0.0f);
    }

    if (player.controlFlags.contains(Player::ControlFlag::Left))
    {
        motionDirection -= glm::vec3(sin(player.theta + 3.1415f * 0.5f), cos(player.theta + 3.1415f * 0.5f), 0.0f);
    }

    if (player.controlFlags.contains(Player::ControlFlag::Right))
    {
        motionDirection += glm::vec3(sin(player.theta + 3.1415f * 0.5f), cos(player.theta + 3.1415f * 0.5f), 0.0f);
    }

    if (glm::length(motionDirection) > 0.01f)
    {
        float linearAcceleration = player.isCrouching() ?
            Player::accelerationCrouch : Player::acceleration;
        
        if (player.controlFlags.contains(Player::ControlFlag::Sprint))
        {
            linearAcceleration *= Player::accelerationSprintFactor;

            float generatedAcceleration = 0.0f;

            for (auto &stack : player.items.store("equipment"))
            {
                IEquipment *equipment = stack.getItemAs<IEquipment>();

                if (equipment != nullptr)
                {
                    generatedAcceleration = equipment->generateAcceleration(stack);
                    if (generatedAcceleration > 0.0001f)
                    {
                        break;
                    }
                }
            }

            linearAcceleration += generatedAcceleration;
        }

        player.vel += linearAcceleration * glm::normalize(motionDirection);
        player.playFootstepSounds();
    }
    else
    {
        player.resetFootstepTimer();
    }

    if (player.controlFlags.contains(Player::ControlFlag::Jump))
    {
        player.onJumping();
    }

    if (player.controlFlags.contains(Player::ControlFlag::Jetpack))
    {
        float thrust = 0.0;

        for (auto &stack : player.items.store("equipment"))
        {
            IEquipment *equipment = stack.getItemAs<IEquipment>();

            if (equipment != nullptr)
            {
                thrust = equipment->generateThrust(stack);
                if (thrust > 0.0001f)
                {
                    break;
                }
            }
        }

        player.vel.z += wfixed(thrust);
    }
}

float AirMovementPhysics::getFriction()
{
    return friction;
}

float AirMovementPhysics::getGravity()
{
    return gravity;
}