#include "movement_component.h"

#include <algorithm>
#include <cmath>

namespace tactical_shooter {

MovementComponent::MovementComponent() {}

MovementComponent::MovementComponent(const Parameters &params)
    : params_(params) {}

void MovementComponent::set_parameters(const Parameters &params) {
    params_ = params;
}

void MovementComponent::update(Entity &entity, float delta) {
    if (!entity.is_alive()) return;

    const EntityInput &input = entity.last_input();

    // Set flags based on input
    uint16_t flags = entity.get_flags();
    if (input.crouch)   flags |= ENTITY_FLAG_CROUCHING;
    else                flags &= ~ENTITY_FLAG_CROUCHING;
    if (input.sprint)   flags |= ENTITY_FLAG_SPRINTING;
    else                flags &= ~ENTITY_FLAG_SPRINTING;
    entity.set_flags(flags);

    // Apply movement on ground vs in air
    if (flags & ENTITY_FLAG_GROUNDED) {
        apply_ground_movement(entity, delta);
    } else {
        apply_air_movement(entity, delta);
    }

    // Handle jump
    if (input.jump && (flags & ENTITY_FLAG_GROUNDED)) {
        godot::Vector3 vel = entity.get_velocity();
        vel.y = params_.jump_velocity;
        entity.set_velocity(vel);
        entity.set_flags(flags & ~ENTITY_FLAG_GROUNDED);
    }

    // Apply gravity
    godot::Vector3 vel = entity.get_velocity();
    vel.y += params_.gravity * delta;
    entity.set_velocity(vel);

    // Integrate
    integrate_position(entity, delta);

    // Simple ground detection: if y velocity resolved, flag grounded
    // (Full ground detection requires scene query — this is the stub)
    vel = entity.get_velocity();
    if (entity.get_position().y <= 0.0f && vel.y <= 0.0f) {
        godot::Vector3 pos = entity.get_position();
        pos.y = 0.0f;
        entity.set_position(pos);
        vel.y = 0.0f;
        entity.set_velocity(vel);
        uint16_t f = entity.get_flags();
        f |= ENTITY_FLAG_GROUNDED;
        entity.set_flags(f);
    }
}

void MovementComponent::apply_ground_movement(Entity &entity, float delta) {
    const EntityInput &input = entity.last_input();
    godot::Vector3 vel = entity.get_velocity();

    // Determine target speed
    float max_speed = params_.walk_speed;
    if (entity.get_flags() & ENTITY_FLAG_SPRINTING) max_speed = params_.sprint_speed;
    if (entity.get_flags() & ENTITY_FLAG_CROUCHING) max_speed = params_.crouch_speed;

    // Apply friction
    float speed = vel.length();
    if (speed > 0.0f) {
        float drop = speed * params_.friction * delta;
        float new_speed = std::max(0.0f, speed - drop);
        vel *= (new_speed / speed);
    }

    // Acceleration from input
    godot::Vector3 wish_dir = input.move_direction;
    float wish_len = wish_dir.length();
    if (wish_len > 1.0f) wish_dir /= wish_len;
    if (wish_len > 0.0f) {
        vel += wish_dir * params_.acceleration * delta;
        // Clamp to max speed in the horizontal plane
        godot::Vector3 horiz(vel.x, 0.0f, vel.z);
        float horiz_len = horiz.length();
        if (horiz_len > max_speed) {
            horiz *= max_speed / horiz_len;
            vel.x = horiz.x;
            vel.z = horiz.z;
        }
    }

    // Clamp overall velocity
    if (vel.length() > params_.max_velocity) {
        vel = vel.normalized() * params_.max_velocity;
    }

    entity.set_velocity(vel);
}

void MovementComponent::apply_air_movement(Entity &entity, float delta) {
    const EntityInput &input = entity.last_input();
    godot::Vector3 vel = entity.get_velocity();

    // Apply air friction
    float speed = vel.length();
    if (speed > 0.0f) {
        float drop = speed * params_.air_friction * delta;
        float new_speed = std::max(0.0f, speed - drop);
        vel *= (new_speed / speed);
    }

    // Reduced acceleration in air
    godot::Vector3 wish_dir = input.move_direction;
    float wish_len = wish_dir.length();
    if (wish_len > 1.0f) wish_dir /= wish_len;
    if (wish_len > 0.0f) {
        // Only accelerate in horizontal plane while airborne
        godot::Vector3 horiz_accel(
            wish_dir.x * params_.air_acceleration * delta,
            0.0f,
            wish_dir.z * params_.air_acceleration * delta
        );
        vel.x += horiz_accel.x;
        vel.z += horiz_accel.z;
    }

    // Clamp horizontal speed
    godot::Vector3 horiz(vel.x, 0.0f, vel.z);
    float max_horiz = std::max(params_.walk_speed, params_.sprint_speed);
    if (horiz.length() > max_horiz) {
        horiz *= max_horiz / horiz.length();
        vel.x = horiz.x;
        vel.z = horiz.z;
    }

    entity.set_velocity(vel);
}

void MovementComponent::integrate_position(Entity &entity, float delta) {
    godot::Vector3 pos = entity.get_position();
    godot::Vector3 vel = entity.get_velocity();
    pos += vel * delta;
    entity.set_position(pos);
}

} // namespace tactical_shooter
