#include <vector>
#include <cmath>
#include <limits>
#include <iostream>
#include <array>

enum class TargetPart { HEAD, BODY, LIMB };

struct Vec3 {
    float x{}, y{}, z{};
    Vec3() = default;
    Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
    Vec3 operator+(const Vec3& r) const { return {x + r.x, y + r.y, z + r.z}; }
    Vec3& operator+=(const Vec3& r) { x += r.x; y += r.y; z += r.z; return *this; }
    Vec3 operator*(float s) const { return {x * s, y * s, z * s}; }
    Vec3 operator/(float s) const { return {x / s, y / s, z / s}; }
    float length() const { return std::sqrt(x * x + y * y + z * z); }
};

struct TargetData {
    int   targetID{};
    Vec3  pos{};
    Vec3  headPos{};
    float health{};
    bool  isEnemy{};
    bool  isKnocked{};
    float moveSpeed{};
    Vec3  moveDir{};
    std::array<Vec3, 8> bones{};

    // 动态强度：由外部每帧更新，0 静止，1 正常跑，>1 剧烈机动
    float moveIntensity = 1.0f;
};

struct PlayerData {
    Vec3  pos{};
    float aimInput{};
    bool  isIndoor{};
    bool  isMoving{};
    bool  isFiring{};
    bool  isADS{};
};

struct AimBotConfig {
    bool  enableAutoAim = true;
    float maxLockDistance = 2000.0f;
    float targetLostDelay = 5.0f;
    float baseSmoothness = 0.0f;
    float lowHpThreshold = 100.0f;
    TargetPart priorityPart = TargetPart::HEAD;
    float bulletSpeed = 5000.0f;
    float aimStrengthMax = 1.5f;
    bool onlyWhileFiring = true;
    bool onlyWhileADS = true;
    std::vector<int> headBoneIdx = {1, 3, 5};
    float assistRadiusMax = 10.0f;
};

float Calc3DDist(const Vec3& a, const Vec3& b) {
    float dx = b.x - a.x, dy = b.y - a.y, dz = b.z - a.z;
    return std::sqrt(dx * dx + dy * dy + dz * dz);
}

// 主自瞄逻辑
bool RunAimBot(const PlayerData& player,
               const std::vector<TargetData>& allTargets,
               const AimBotConfig& config,
               float& outAimAngleX,
               float& outAimAngleY,
               TargetPart& outLockedPart)
{
    if (config.onlyWhileFiring && !player.isFiring) return false;
    if (config.onlyWhileADS   && !player.isADS)    return false;

    const float MAX_RAD      = config.assistRadiusMax * float(M_PI) / 180.0f;
    const float ANGLE_WEIGHT = 1000.0f;
    const float ANGLE_8DEG   = 8.0f;

    TargetData lockedTarget;
    float bestScore = -std::numeric_limits<float>::max();
    bool  found     = false;

    for (const auto& tar : allTargets)
    {
        if (!tar.isEnemy || tar.isKnocked) continue;

        Vec3 headWorld{};
        for (int idx : config.headBoneIdx) headWorld += tar.bones[idx];
        headWorld /= static_cast<float>(config.headBoneIdx.size());

        float dist3D = Calc3DDist(player.pos, headWorld);
        if (dist3D > config.maxLockDistance) continue;

        Vec3  dir    = headWorld - player.pos;
        float yaw   = atan2(dir.y, dir.x);
        float pitch = asin(dir.z / dist3D);
        float dy = fabs(yaw   - outAimAngleX * float(M_PI) / 180.0f);
        float dp = fabs(pitch - outAimAngleY * float(M_PI) / 180.0f);
        if (dy > MAX_RAD || dp > MAX_RAD) continue;

        float angleDiff = (dy + dp) * 180.0f / float(M_PI);

        float score = 0.0f;
        if (config.priorityPart == TargetPart::HEAD) score += 1000.0f;
        else if (config.priorityPart == TargetPart::BODY) score += 500.0f;
        else score += 100.0f;
        if (tar.health <= config.lowHpThreshold) score += 500.0f;

        float angleScore = ANGLE_WEIGHT / (angleDiff + 1.0f);
        if (angleDiff <= ANGLE_8DEG) angleScore *= 2.0f;
        score += angleScore;

        if (score > bestScore)
        {
            bestScore   = score;
            lockedTarget = tar;
            found        = true;
            outLockedPart = config.priorityPart;
        }
    }

    if (!found) return false;

    Vec3 headWorld{};
    for (int idx : config.headBoneIdx) headWorld += lockedTarget.bones[idx];
    headWorld /= static_cast<float>(config.headBoneIdx.size());

    float bulletTime = Calc3DDist(player.pos, headWorld) / config.bulletSpeed;
    Vec3 predicted = headWorld + Vec3{
        lockedTarget.moveDir.x * lockedTarget.moveSpeed * bulletTime,
        lockedTarget.moveDir.y * lockedTarget.moveSpeed * bulletTime,
        0.f
    };

    if (player.isIndoor){
        predicted.x = headWorld.x + (predicted.x - headWorld.x) * 1.0f;
        predicted.y = headWorld.y + (predicted.y - headWorld.y) * 1.0f;
    }

    // 核心：强度恒定，但随敌人动态增强
    float aimStr = config.aimStrengthMax * lockedTarget.moveIntensity;

    Vec3 dir = predicted - player.pos;
    outAimAngleX = atan2(dir.y, dir.x)   * 180.0f / float(M_PI);
    outAimAngleY = asin(dir.z / dir.length()) * 180.0f / float(M_PI);

    return true;
}

/* ---------------- 测试 ---------------- */
int main()
{
    PlayerData player{};
    player.pos = {0, 0, 0};
    player.aimInput = 0.1f;
    player.isIndoor = false;
    player.isMoving = true;
    player.isFiring = true;
    player.isADS    = true;

    std::vector<TargetData> targets(3);
    for (int i = 0; i < 3; ++i)
    {
        TargetData& t = targets[i];
        t.targetID = 101 + i;
        t.pos      = Vec3(1800 + i * 200, 1200 + i * 400, 5);
        t.health   = 10 - i * 5;
        t.isEnemy  = true;
        t.isKnocked= false;
        t.moveSpeed= 15 + i * 10;        // 越往后速度越快
        t.moveDir  = Vec3(0.8f - i * 0.3f, 0.6f - i * 0.4f, 0);
        for (auto& b : t.bones) b = Vec3(t.pos.x + i, t.pos.y + i, t.pos.z + 8);
        t.bones[1] = Vec3(t.pos.x, t.pos.y, t.pos.z + 10);
        t.bones[3] = Vec3(t.pos.x, t.pos.y, t.pos.z + 12);
        t.bones[5] = Vec3(t.pos.x, t.pos.y, t.pos.z + 13);

        t.moveIntensity = 1.0f + i * 0.5f; // 动态强度示例
    }

    AimBotConfig aimConfig{};
    float aimX = 0.0f, aimY = 0.0f;
    TargetPart part{};
    bool locked = RunAimBot(player, targets, aimConfig, aimX, aimY, part);

    if (locked)
        std::cout << "Locked HEAD  X=" << aimX << "  Y=" << aimY << '\n';
    else
        std::cout << "No lock\n";
    return 0;
}
