#include "entity.h"

#include <algorithm>

namespace tactical_shooter {

Entity::Entity() {
    reset(godot::Vector3(0, 0, 0));
}

void Entity::reset(const godot::Vector3 &spawn_position) {
    position_ = spawn_position;
    velocity_ = godot::Vector3(0, 0, 0);
    yaw_ = 0.0f;
    pitch_ = 0.0f;
    health_ = 100.0f;
    armor_ = 0.0f;
    flags_ = ENTITY_FLAG_ALIVE;
    weapon_id_ = 0;
    ammo_ = 30;
    last_input_ = EntityInput{};
}

void Entity::apply_input(const EntityInput &input) {
    last_input_ = input;

    // Update flags from input
    if (input.crouch) flags_ |= ENTITY_FLAG_CROUCHING;
    else flags_ &= ~ENTITY_FLAG_CROUCHING;

    if (input.sprint) flags_ |= ENTITY_FLAG_SPRINTING;
    else flags_ &= ~ENTITY_FLAG_SPRINTING;

    if (input.aim) flags_ |= ENTITY_FLAG_AIMING;
    else flags_ &= ~ENTITY_FLAG_AIMING;
}

// ---- Snapshot / Delta --------------------------------------------------------

EntitySnapshot Entity::capture_snapshot() const {
    EntitySnapshot snap;
    snap.entity_id = entity_id_;
    snap.flags = flags_;
    snap.position = position_;
    snap.velocity = velocity_;
    snap.yaw = yaw_;
    snap.pitch = pitch_;
    snap.health = health_;
    snap.armor = armor_;
    snap.weapon_id = weapon_id_;
    snap.ammo = ammo_;
    snap.last_processed_input = last_input_.input_sequence;
    return snap;
}

EntitySnapshot::ChangeMask Entity::compute_change_mask(const EntitySnapshot &base) const {
    uint32_t mask = EntitySnapshot::CHANGED_NONE;

    if (position_ != base.position)          mask |= EntitySnapshot::CHANGED_POSITION;
    if (velocity_ != base.velocity)          mask |= EntitySnapshot::CHANGED_VELOCITY;
    if (yaw_ != base.yaw || pitch_ != base.pitch) mask |= EntitySnapshot::CHANGED_ROTATION;
    if (health_ != base.health)              mask |= EntitySnapshot::CHANGED_HEALTH;
    if (armor_ != base.armor)                mask |= EntitySnapshot::CHANGED_ARMOR;
    if (weapon_id_ != base.weapon_id)        mask |= EntitySnapshot::CHANGED_WEAPON;
    if (ammo_ != base.ammo)                  mask |= EntitySnapshot::CHANGED_AMMO;
    if (flags_ != base.flags)                mask |= EntitySnapshot::CHANGED_FLAGS;
    if (last_input_.input_sequence != base.last_processed_input)
        mask |= EntitySnapshot::CHANGED_INPUT;

    return static_cast<EntitySnapshot::ChangeMask>(mask);
}

// ---- GDScript Bindings -------------------------------------------------------

void Entity::_bind_methods() {
    using namespace godot;

    ClassDB::bind_method(D_METHOD("set_position", "position"), &Entity::set_position);
    ClassDB::bind_method(D_METHOD("get_position"), &Entity::get_position);
    ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "set_position", "get_position");

    ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &Entity::set_velocity);
    ClassDB::bind_method(D_METHOD("get_velocity"), &Entity::get_velocity);
    ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity"), "set_velocity", "get_velocity");

    ClassDB::bind_method(D_METHOD("set_health", "health"), &Entity::set_health);
    ClassDB::bind_method(D_METHOD("get_health"), &Entity::get_health);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "health"), "set_health", "get_health");

    ClassDB::bind_method(D_METHOD("set_armor", "armor"), &Entity::set_armor);
    ClassDB::bind_method(D_METHOD("get_armor"), &Entity::get_armor);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "armor"), "set_armor", "get_armor");

    ClassDB::bind_method(D_METHOD("set_yaw", "yaw"), &Entity::set_yaw);
    ClassDB::bind_method(D_METHOD("get_yaw"), &Entity::get_yaw);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "yaw"), "set_yaw", "get_yaw");

    ClassDB::bind_method(D_METHOD("set_pitch", "pitch"), &Entity::set_pitch);
    ClassDB::bind_method(D_METHOD("get_pitch"), &Entity::get_pitch);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch"), "set_pitch", "get_pitch");

    ClassDB::bind_method(D_METHOD("is_alive"), &Entity::is_alive);
    ClassDB::bind_method(D_METHOD("kill"), &Entity::kill);

    ClassDB::bind_method(D_METHOD("get_entity_id"), &Entity::get_entity_id);
}

// ---- Property Accessors ------------------------------------------------------

void Entity::set_position(const godot::Vector3 &p_position) { position_ = p_position; }
godot::Vector3 Entity::get_position() const { return position_; }
void Entity::set_velocity(const godot::Vector3 &p_velocity) { velocity_ = p_velocity; }
godot::Vector3 Entity::get_velocity() const { return velocity_; }
void Entity::set_health(float p_health) { health_ = std::clamp(p_health, 0.0f, 100.0f); }
float Entity::get_health() const { return health_; }
void Entity::set_armor(float p_armor) { armor_ = std::clamp(p_armor, 0.0f, 100.0f); }
float Entity::get_armor() const { return armor_; }
void Entity::set_yaw(float p_yaw) { yaw_ = p_yaw; }
float Entity::get_yaw() const { return yaw_; }
void Entity::set_pitch(float p_pitch) { pitch_ = std::clamp(p_pitch, -90.0f, 90.0f); }
float Entity::get_pitch() const { return pitch_; }
bool Entity::is_alive() const { return flags_ & ENTITY_FLAG_ALIVE; }
void Entity::kill() { flags_ &= ~ENTITY_FLAG_ALIVE; }

} // namespace tactical_shooter
