#include <vector>
#include <cmath>
#include <string>
#include <memory>
#include <functional>
#include <algorithm>

#define PUBGMHD_NAMESPACE inline namespace com { namespace tencent { namespace tmgp { namespace pubgmhd {
#define END_PUBGMHD_NAMESPACE }}}

PUBGMHD_NAMESPACE

using f32 = float;
using i32 = int;

constexpr f32 PI = 3.141592653589793238f;

struct Vec2 {
    f32 x{}, y{};

    Vec2 operator-(const Vec2& o) const { return {x - o.x, y - o.y}; }
    Vec2 operator+(const Vec2& o) const { return {x + o.x, y + o.y}; }
    Vec2 operator*(f32 s) const { return {x * s, y * s}; }
    f32 len() const { return std::sqrt(x * x + y * y); }
    Vec2 nor() const { f32 l = len(); return l == 0 ? Vec2{} : Vec2{x / l, y / l}; }
    f32 ang() const { return std::atan2(y, x) * 180.f / PI; }
};

enum class WeaponType {
    M416,
    AKM,
    AWM,
    UMP45,
    DP28,
    GROZA,
    P1911,
    Vector,
    Mini14,
    SLR,
    MK14,
    S686,
    SKS,
    QBZ,
    AUG,
    M762,
    BerylM762,
    PP19,
    Tompson,
    MP5K,
    SawedOff,
    M249,
    MG3,
    M24,
    Kar98k,
    Win94,
    QBU,
    SKS,
    M16A4,
    RPG7,
    Groza,
    R45,
    FlareGun,
    Crossbow,
    Pan,
    Fist,
};

struct Enemy {
    Vec2 pos;
    Vec2 velocity;
    f32 move_speed = 2000.f;

    void update(f32 dt) {
        static i32 seed = 0;
        f32 dx = (std::rand_r((unsigned int*)&seed) % 20000 - 10000) * dt;
        f32 dy = (std::rand_r((unsigned int*)&seed) % 20000 - 10000) * dt;
        pos = pos + Vec2{dx, dy};
        velocity = Vec2{dx, dy}.nor();
    }

    Vec2 predict_position(f32 time_ahead) const {
        return pos + velocity * time_ahead;
    }
};

struct Bullet {
    Vec2 pos;
    Vec2 vel;
    f32 rotation{};
    Enemy* target{};
    bool active = true;

    bool update(f32 dt) {
        if (!target || !active) return false;

        Vec2 dir = target->pos - pos;
        if (dir.len() < 0.01f) return false;

        f32 target_angle = dir.ang();
        f32 current_angle = rotation;
        f32 delta = target_angle - current_angle;

        while (delta < -180.f) delta += 360.f;
        while (delta > 180.f) delta -= 360.f;

        f32 turn_rate = 900000.f * dt;
        if (std::abs(delta) > 0.1f) {
            rotation += std::clamp(delta, -turn_rate, turn_rate);
        }

        vel = Vec2{std::cos(rotation * PI / 180), std::sin(rotation * PI / 180)} * 500000.f * dt;
        pos = pos + vel;

        return (pos - target->pos).len() < 10.f;
    }
};

class WeaponSystem {
public:
    struct FireMode {
        i32 burst_count = 1;
        f32 spread_deg = 0.f;
        f32 fire_rate = 0.1f;
        i32 bullet_split = 1;
        bool auto_fire = false;
    };

    std::vector<Enemy*> enemies;
    std::vector<Bullet> bullets;
    Vec2 crosshair{400.f, 300.f};
    f32 aim_smooth = 9999999.f;
    f32 detect_range = 9999999.f;
    WeaponType current_weapon = WeaponType::M416;

    using FireFunc = std::function<void(WeaponSystem&, const Vec2&)>;
    std::map<WeaponType, FireFunc> weapon_fire_funcs;
    std::map<WeaponType, FireMode> weapon_modes;

    WeaponSystem() {
        register_weapons();
    }

    void register_weapons() {
        weapon_modes[WeaponType::M416] = {3, 2.f, 0.08f, 1, true};
        weapon_modes[WeaponType::AKM] = {1, 3.f, 0.12f, 1, true};
        weapon_modes[WeaponType::AWM] = {1, 0.1f, 1.5f, 1, false};
        weapon_modes[WeaponType::UMP45] = {1, 1.5f, 0.06f, 1, true};
        weapon_modes[WeaponType::DP28] = {1, 2.5f, 0.1f, 1, true};
        weapon_modes[WeaponType::GROZA] = {1, 1.2f, 0.07f, 1, true};
        weapon_modes[WeaponType::P1911] = {1, 1.8f, 0.3f, 1, false};
        weapon_modes[WeaponType::Vector] = {1, 1.f, 0.03f, 1, true};
        weapon_modes[WeaponType::Mini14] = {1, 0.8f, 0.2f, 1, true};
        weapon_modes[WeaponType::SLR] = {1, 0.5f, 0.3f, 1, false};
        weapon_modes[WeaponType::MK14] = {1, 1.5f, 0.1f, 1, true};
        weapon_modes[WeaponType::S686] = {1, 5.f, 0.5f, 1, false};
        weapon_modes[WeaponType::SKS] = {1, 1.2f, 0.2f, 1, true};
        weapon_modes[WeaponType::QBZ] = {1, 1.5f, 0.1f, 1, true};
        weapon_modes[WeaponType::AUG] = {1, 1.f, 0.08f, 1, true};
        weapon_modes[WeaponType::M762] = {1, 2.f, 0.1f, 1, true};
        weapon_modes[WeaponType::BerylM762] = {1, 2.f, 0.1f, 1, true};
        weapon_modes[WeaponType::PP19] = {1, 1.2f, 0.05f, 1, true};
        weapon_modes[WeaponType::Tompson] = {1, 2.5f, 0.03f, 1, true};
        weapon_modes[WeaponType::MP5K] = {1, 1.2f, 0.04f, 1, true};
        weapon_modes[WeaponType::SawedOff] = {1, 5.f, 0.5f, 1, false};
        weapon_modes[WeaponType::M249] = {1, 3.f, 0.05f, 1, true};
        weapon_modes[WeaponType::MG3] = {1, 3.f, 0.05f, 1, true};
        weapon_modes[WeaponType::M24] = {1, 0.1f, 1.0f, 1, false};
        weapon_modes[WeaponType::Kar98k] = {1, 0.1f, 1.0f, 1, false};
        weapon_modes[WeaponType::Win94] = {1, 0.1f, 1.0f, 1, false};
        weapon_modes[WeaponType::QBU] = {1, 0.5f, 0.2f, 1, true};
        weapon_modes[WeaponType::SKS] = {1, 1.2f, 0.2f, 1, true};
        weapon_modes[WeaponType::M16A4] = {3, 1.5f, 0.1f, 1, false};
        weapon_modes[WeaponType::RPG7] = {1, 0.0f, 1.0f, 1, false};
        weapon_modes[WeaponType::Groza] = {1, 1.2f, 0.07f, 1, true};
        weapon_modes[WeaponType::R45] = {1, 2.0f, 0.3f, 1, false};
        weapon_modes[WeaponType::FlareGun] = {1, 0.0f, 1.0f, 1, false};
        weapon_modes[WeaponType::Crossbow] = {1, 0.0f, 1.0f, 1, false};
        weapon_modes[WeaponType::Pan] = {1, 0.0f, 0.5f, 1, false};
        weapon_modes[WeaponType::Fist] = {1, 0.0f, 0.5f, 1, false};

        for (auto& [type, mode] : weapon_modes) {
            weapon_fire_funcs[type] = [mode](WeaponSystem& sys, const Vec2& from) {
                Enemy* closest = sys.find_closest_enemy();
                if (!closest) return;

                for (i32 i = 0; i < mode.bullet_split; ++i) {
                    Bullet b;
                    Vec2 dir = (closest->predict_position(0.5f) - from).nor();

                    f32 angle_offset = (i - mode.bullet_split / 2) * mode.spread_deg;
                    dir = rotate_vector(dir, angle_offset);

                    b.pos = from;
                    b.target = closest;
                    b.rotation = dir.ang();
                    sys.bullets.push_back(b);
                }
            };
        }
    }

    Vec2 rotate_vector(const Vec2& v, f32 degrees) const {
        f32 rad = degrees * PI / 180.f;
        f32 cosd = std::cos(rad), sind = std::sin(rad);
        return {v.x * cosd - v.y * sind, v.x * sind + v.y * cosd};
    }

    Enemy* find_closest_enemy() const {
        Enemy* closest = nullptr;
        f32 min_dist = 9999999.f;
        for (auto e : enemies) {
            f32 d = (e->pos - crosshair).len();
            if (d < min_dist && d < detect_range) {
                min_dist = d;
                closest = e;
            }
        }
        return closest;
    }

    void update(f32 dt) {
        for (auto& e : enemies) e->update(dt);

        Enemy* clo = find_closest_enemy();
        if (clo) crosshair = crosshair + ((clo->pos - crosshair) * aim_smooth * dt);

        for (i32 i = 0; i < bullets.size();) {
            if (bullets[i].update(dt)) {
                bullets.erase(bullets.begin() + i);
            } else ++i;
        }
    }

    void shoot(const Vec2& from) {
        if (weapon_fire_funcs.count(current_weapon)) {
            weapon_fire_funcs[current_weapon](*this, from);
        }
    }

    void set_current_weapon(WeaponType type) {
        current_weapon = type;
    }
};

END_PUBGMHD_NAMESPACE