#include "../../../cs2/entity/C_CSPlayerPawn/C_CSPlayerPawn.h"
#include "../../../templeware/interfaces/CGameEntitySystem/CGameEntitySystem.h"
#include "../../../templeware/interfaces/interfaces.h"
#include "../../../templeware/hooks/hooks.h"
#include "../../../templeware/config/config.h"
#include "../../utils/memory/patternscan/patternscan.h"
#include "../visuals/visuals.h"
#include "CUserCmd.h"
#include "../../../templeware/utils/schema/schema.h"

#include <chrono>
#include <Windows.h>
#include <array>
#include <cmath>
#include <Psapi.h>
#include "../../menu/hud.h"
#include "../../menu/menu.h"

// Адреса из дампа (актуально для 2024-07-18)
#define DW_SENSITIVITY 0x1A6A9D8
#define DW_SENSITIVITY_SENS 0x40

float GetGameSensitivity() {
    static float fallback = 2.0f;
    HMODULE hClient = GetModuleHandleA("client.dll");
    if (!hClient) return fallback;
    uintptr_t sensBase = reinterpret_cast<uintptr_t>(hClient) + DW_SENSITIVITY;
    uintptr_t sensPtr = *reinterpret_cast<uintptr_t*>(sensBase);
    if (!sensPtr) return fallback;
    float sens = *reinterpret_cast<float*>(sensPtr + DW_SENSITIVITY_SENS);
    if (sens < 0.01f || sens > 20.0f) return fallback;
    return sens;
}

// Индексы костей (актуально для CS2)
constexpr int BONE_INDEX_HEAD = 6;   // Head
constexpr int BONE_INDEX_NECK = 5;   // Neck
constexpr int BONE_INDEX_BODY = 4;//0;   // Pelvis/Root

// --- lastBoneIdx теперь глобальный static ---
static int lastBoneIdx = -1;

inline QAngle_t CalcAngles(Vector_t viewPos, Vector_t aimPos)
{
    QAngle_t angle = { 0, 0, 0 };

    Vector_t delta = aimPos - viewPos;

    angle.x = -asin(delta.z / delta.Length()) * (180.0f / 3.141592654f);
    angle.y = atan2(delta.y, delta.x) * (180.0f / 3.141592654f);

    return angle;
}

inline float GetFov(const QAngle_t& viewAngle, const QAngle_t& aimAngle)
{
    QAngle_t delta = (aimAngle - viewAngle).Normalize();

    return sqrtf(powf(delta.x, 2.0f) + powf(delta.y, 2.0f));
}

// // Сигнатура функции GetBonePosition (актуально для client.dll):
// // 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 4D 8B F1
// using GetBonePositionFn = int64_t(__fastcall*)(void*, uint32_t, Vector_t*, Vector_t*);
// static GetBonePositionFn oGetBonePosition = nullptr;

// static void InitGetBonePositionSig() {
//     if (!oGetBonePosition) {
//         uintptr_t addr = M::patternScan("client.dll", "48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 4D 8B F1");
//         oGetBonePosition = reinterpret_cast<GetBonePositionFn>(addr);
//     }
// }

Vector_t GetBonePosition(const C_CSPlayerPawn* Entity, int boneIdx) {
    if (!Entity) return {};
    // Внешний способ: читаем Vector3 по адресу boneMatrix + boneIdx * 0x20
    uintptr_t pGameSceneNode = *reinterpret_cast<uintptr_t*>((uintptr_t)Entity + 0x328);
    if (!pGameSceneNode) return {};
    uintptr_t pBoneMatrix = *reinterpret_cast<uintptr_t*>(pGameSceneNode + 0x1F0);
    if (!pBoneMatrix) return {};
    return *reinterpret_cast<Vector_t*>(pBoneMatrix + boneIdx * 0x20);
}

// Получить позицию для аимбота в зависимости от выбора пользователя
Vector_t GetAimbotTargetPos(const C_CSPlayerPawn* Entity, const Vector_t& from, const QAngle_t& viewAngles) {
    switch (Config::aimbot_bone) {
        case Config::BONE_HEAD:
            return GetBonePosition(Entity, BONE_INDEX_HEAD);
        case Config::BONE_NECK:
            return GetBonePosition(Entity, BONE_INDEX_NECK);
        case Config::BONE_BODY:
            return GetBonePosition(Entity, BONE_INDEX_BODY);
        case Config::BONE_NEAREST:
        default: {
            // Каждый раз ищем ближайшую кость по FOV
            std::array<int, 2> bones = {BONE_INDEX_HEAD, BONE_INDEX_BODY};
            float bestFov = FLT_MAX;
            int bestIdx = BONE_INDEX_HEAD;
            for (int idx : bones) {
                Vector_t pos = GetBonePosition(Entity, idx);
                QAngle_t ang = CalcAngles(from, pos); // <-- Исправлено!
                float fov = GetFov(viewAngles, ang);
                if (fov < bestFov && std::isfinite(fov)) {
                    bestFov = fov;
                    bestIdx = idx;
                }
            }
            return GetBonePosition(Entity, bestIdx);
        }
    }
}


Vector_t GetEntityEyePos(const C_CSPlayerPawn* Entity) {
	if (!Entity)
		return {};

	uintptr_t game_scene_node = *reinterpret_cast<uintptr_t*>((uintptr_t)Entity + SchemaFinder::Get(hash_32_fnv1a_const("C_BaseEntity->m_pGameSceneNode")));

	auto Origin = *reinterpret_cast<Vector_t*>(game_scene_node + SchemaFinder::Get(hash_32_fnv1a_const("CGameSceneNode->m_vecAbsOrigin")));
	auto ViewOffset = *reinterpret_cast<Vector_t*>((uintptr_t)Entity + SchemaFinder::Get(hash_32_fnv1a_const("C_BaseModelEntity->m_vecViewOffset")));

	Vector_t Result = Origin + ViewOffset;
	if (!std::isfinite(Result.x) || !std::isfinite(Result.y) || !std::isfinite(Result.z))
		return {};

	return Result;
}

// Удаляем все вспомогательные функции инкапсуляции

void Aimbot() {
    static C_CSPlayerPawn* lockedTarget = nullptr;
    static bool prevAimbotState = false;
    static bool targetWasLost = false;
    static QAngle_t last_punch_angle = {0,0,0};
    static int shotCount = 0;
    float sensitivity = GetGameSensitivity();
    bool aimbotActive = Config::always_on_aimbot ? true : Config::aimbot;
    // --- shooterAfterAim + triggerbot_key ---
    if (Config::shooterAfterAim && (GetAsyncKeyState(Config::triggerbot_key) & 0x8000)) {
        aimbotActive = true;
    }
    C_CSPlayerPawn* lp = H::oGetLocalPlayer(0);
    if (cached_players.empty() || !lp || !lp->handle().valid() || lp->getHealth() <= 0)
        return;
    Vector_t lep = GetEntityEyePos(lp);
    QAngle_t* viewangles = (QAngle_t*)(modules.getModule("client") + 0x1A78650);
	bool isShooting = (GetAsyncKeyState(Config::triggerbot_key) & 0x8000) || (GetAsyncKeyState(VK_LBUTTON) & 0x8000);

    int shotsFired = lp ? lp->getShotsFired() : 0;
    shotCount = shotsFired;
    //g_DebugString = "debug: " + std::to_string(shotsFired) + "Sens: " + std::to_string(sensitivity);
  
    if (Config::aimbot_on_lmb && !Config::always_on_aimbot) {
        aimbotActive = isShooting;
    }

    // Получаем локального игрока и viewangles всегда, чтобы не дублировать код
    // C_CSPlayerPawn* lp = H::oGetLocalPlayer(0);
    // Vector_t lep = GetEntityEyePos(lp);
    // QAngle_t* viewangles = (QAngle_t*)(modules.getModule("client") + 0x1A78650);

    // Проверка: стреляет ли игрок (LMB)
    // bool isShooting = (GetAsyncKeyState(VK_LBUTTON) & 0x8000);

    // --- Новый режим: always_on_aimbot ---
    if (Config::always_on_aimbot) {
        float bestFov = Config::aimbot_fov;
        PlayerCache* bestTarget = nullptr;
        QAngle_t bestAngle = { 0, 0, 0 };
        for (auto& Player : cached_players) {
            if (!Player.handle.valid() || Player.health <= 0 || Player.handle.index() == INVALID_EHANDLE_INDEX)
                continue;
            if (Config::teamCheck && (Player.team_num == cached_local.team))
                continue;
            Vector_t target_pos = Player.position + Player.viewOffset;
            QAngle_t angle = CalcAngles(target_pos, lep);
            angle.x *= -1.f;
            angle.y += 180.f;
            const float fov = GetFov(*viewangles, angle);
            if (!std::isfinite(fov) || fov > bestFov)
                continue;
            bestFov = fov;
            bestTarget = &Player;
            bestAngle = angle;
        }
        if (bestTarget) {
            Vector_t target_pos = bestTarget->position + bestTarget->viewOffset;
            QAngle_t angle = CalcAngles(target_pos, lep);
            angle.x *= -1.f;
            angle.y += 180.f;
            QAngle_t cur_punch_angle = *(QAngle_t*)((uintptr_t)lp + SchemaFinder::Get(hash_32_fnv1a_const("C_CSPlayerPawn->m_aimPunchAngle")));
            // --- RCS ---
            bool rcsActive = false;
            if (Config::shooterAfterAim && (GetAsyncKeyState(Config::triggerbot_key) & 0x8000)) {
                rcsActive = (Config::rcs || Config::shooterAfterAim) && (shotCount > Config::rcsActivationShots);
            } else {
                rcsActive = (Config::rcs || Config::shooterAfterAim) && (shotCount > Config::rcsActivationShots);
            }
            if (rcsActive && shotsFired > 1) {
                // Для прямого изменения viewangles: делить НЕ нужно
                //QAngle_t punchDelta = cur_punch_angle - last_punch_angle;
                //g_DebugString = "debug: " + std::to_string(punchDelta.x);
                angle -= cur_punch_angle * 2.f;
                last_punch_angle = cur_punch_angle;
            } else {
                last_punch_angle = {0,0,0};
            }
            angle.z = 0.f;
            angle = angle.Normalize();

            // --- Smooth ---
            QAngle_t delta = (angle - *viewangles).Normalize();
            if (Config::aimbot_smooth > 0.f) {
                QAngle_t cur = *viewangles;
                float smooth = Config::aimbot_smooth;
                if (Config::aimbot_dynamic_smooth) {
                    float fov = sqrtf(delta.x * delta.x + delta.y * delta.y);
                    float maxFov = Config::aimbot_fov;
                    float factor = (maxFov > 0.01f) ? (fov / maxFov) : 0.f;
                    smooth = smooth + (smooth * factor * Config::aimbot_dynamic_smooth_factor);
                }
                angle = cur + delta * (1.f / smooth);
                angle = angle.Normalize();
                *viewangles = angle;
            } else {
                *viewangles = angle;
            }
        }
        // В этом режиме не используем lockedTarget и не трогаем targetWasLost/prevAimbotState
        return;
    }

    // Если кнопка только что нажата (переход с false на true) — ищем новую цель
    if (aimbotActive && !prevAimbotState) {
        lastBoneIdx = -1; // Сброс при новой активации
        int nMaxHighestEntity = I::GameEntity->Instance->GetHighestEntityIndex();
        float bestFov = Config::aimbot_fov;
        C_CSPlayerPawn* bestTarget = nullptr;
        QAngle_t bestAngle = { 0, 0, 0 };

        for (int i = 1; i <= nMaxHighestEntity; i++) {
            auto Entity = I::GameEntity->Instance->Get(i);
            if (!Entity)
                continue;
            if (!Entity->handle().valid())
                continue;
            SchemaClassInfoData_t* _class = nullptr;
            Entity->dump_class_info(&_class);
            if (!_class)
                continue;
            const uint32_t hash = HASH(_class->szName);
            if (hash == HASH("C_CSPlayerPawn")) {
                C_CSPlayerPawn* pawn = (C_CSPlayerPawn*)Entity;
                if (!pawn) continue;
                if (pawn->get_entity_by_handle() == lp->get_entity_by_handle()) continue;
                if (pawn->getHealth() <= 0) continue;
                if (Config::team_check && pawn->getTeam() == lp->getTeam()) continue;
                Vector_t target_pos = GetEntityEyePos(pawn);//GetAimbotTargetPos(pawn, lep);
                QAngle_t angle = CalcAngles(target_pos, lep);
                angle.x *= -1.f;
                angle.y += 180.f;
                const float fov = GetFov(*viewangles, angle);
                if (!std::isfinite(fov) || fov > bestFov)
                    continue;
                bestFov = fov;
                bestTarget = pawn;
                bestAngle = angle;
            }
        }
        lockedTarget = bestTarget;
        targetWasLost = false;
        // Сброс punch_angle при смене цели
        last_punch_angle = {0,0,0};
    }

    // Если кнопка отпущена — сбрасываем захват
    if (!aimbotActive) {
        lockedTarget = nullptr;
        targetWasLost = false;
        last_punch_angle = {0,0,0}; // сброс rcs
        // Сброс lastBoneIdx при отпускании кнопки аимбота (например, в Aimbot()):
        // if (!aimbotActive) lastBoneIdx = -1;
    }

    // Если есть захваченная цель и кнопка удерживается
    if (aimbotActive && lockedTarget) {
        // Проверяем, что цель всё ещё валидна
        if (!lp || !lp->handle().valid() || lp->getHealth() <= 0) {
            lockedTarget = nullptr;
            targetWasLost = false;
            last_punch_angle = {0,0,0};
            return;
        }
        if (
            lockedTarget->get_entity_by_handle() == lp->get_entity_by_handle() ||
            lockedTarget->getHealth() <= 0 ||
            !lockedTarget->handle().valid()
        ) {
            lockedTarget = nullptr;
            targetWasLost = true;
            last_punch_angle = {0,0,0}; // сброс rcs
        }
        else {
            Vector_t target_pos = GetAimbotTargetPos(lockedTarget, lep, *viewangles);
            QAngle_t angle = CalcAngles(target_pos, lep);
            angle.x *= -1.f;
            angle.y += 180.f;
            QAngle_t cur_punch_angle = *(QAngle_t*)((uintptr_t)lp + SchemaFinder::Get(hash_32_fnv1a_const("C_CSPlayerPawn->m_aimPunchAngle")));
            // --- RCS ---
            bool rcsActive = false;
            if (Config::shooterAfterAim && (GetAsyncKeyState(Config::triggerbot_key) & 0x8000)) {
                rcsActive = (Config::rcs || Config::shooterAfterAim) && (shotCount > Config::rcsActivationShots);
            } else {
                rcsActive = (Config::rcs || Config::shooterAfterAim) && (shotCount > Config::rcsActivationShots);
            }
            if (rcsActive && shotCount > 1) {
                // Для прямого изменения viewangles: делить НЕ нужно
                //QAngle_t punchDelta = cur_punch_angle - last_punch_angle;
                //g_DebugString = "debug: " + std::to_string(punchDelta.x);
                angle -= cur_punch_angle * 2.f;
                last_punch_angle = cur_punch_angle;
            } else {
                last_punch_angle = {0,0,0};
            }
            angle.z = 0.f;
            angle = angle.Normalize();
            // --- Smooth ---
            if (Config::aimbot_smooth > 0.f) {
                QAngle_t cur = *viewangles;
                QAngle_t delta = (angle - cur).Normalize();
                float smooth = Config::aimbot_smooth;
                if (Config::aimbot_dynamic_smooth) {
                    float fov = sqrtf(delta.x * delta.x + delta.y * delta.y);
                    float maxFov = Config::aimbot_fov;
                    float factor = (maxFov > 0.01f) ? (fov / maxFov) : 0.f;
                    smooth = smooth + (smooth * factor * Config::aimbot_dynamic_smooth_factor);
                }
                angle = cur + delta * (1.f / smooth);
                angle = angle.Normalize();
                *viewangles = angle;
            } else {
                *viewangles = angle;
            }
        }
    }

    //Если кнопка зажата, цели нет, и мы не теряли цель (т.е. только в начале или если цели не было вообще)
    if (aimbotActive && !lockedTarget && !targetWasLost) {
       int nMaxHighestEntity = I::GameEntity->Instance->GetHighestEntityIndex();
       float bestFov = Config::aimbot_fov;
       C_CSPlayerPawn* bestTarget = nullptr;
       QAngle_t bestAngle = { 0, 0, 0 };

       for (int i = 1; i <= nMaxHighestEntity; i++) {
           auto Entity = I::GameEntity->Instance->Get(i);
           if (!Entity)
               continue;
           if (!Entity->handle().valid())
               continue;
           SchemaClassInfoData_t* _class = nullptr;
           Entity->dump_class_info(&_class);
           if (!_class)
               continue;
           const uint32_t hash = HASH(_class->szName);
           if (hash == HASH("C_CSPlayerPawn")) {
               C_CSPlayerPawn* pawn = (C_CSPlayerPawn*)Entity;
               if (!pawn) continue;
               if (pawn->get_entity_by_handle() == lp->get_entity_by_handle()) continue;
               if (pawn->getHealth() <= 0) continue;
               if (Config::team_check && pawn->getTeam() == lp->getTeam()) continue;
               Vector_t target_pos = GetEntityEyePos(pawn);//GetAimbotTargetPos(pawn, lep);
               QAngle_t angle = CalcAngles(target_pos, lep);
               angle.x *= -1.f;
               angle.y += 180.f;
               const float fov = GetFov(*viewangles, angle);
               if (!std::isfinite(fov) || fov > bestFov)
                   continue;
               bestFov = fov;
               bestTarget = pawn;
               bestAngle = angle;
           }
       }
       lockedTarget = bestTarget;
       // Сброс punch_angle при смене цели
       last_punch_angle = {0,0,0};
    }

    prevAimbotState = aimbotActive;
}

void SilentAimbot(CUserCmd* pCmd) {
    if (!Config::silent_aim || !pCmd) return;
    static C_CSPlayerPawn* lockedTarget = nullptr;
    static bool prevSilentState = false;
    static QAngle_t last_punch_angle = {0,0,0};
    static int shotCount = 0;
    C_CSPlayerPawn* lp = H::oGetLocalPlayer(0);
    if (!lp || !lp->handle().valid() || lp->getHealth() <= 0) return;
    Vector_t lep = GetEntityEyePos(lp);
    QAngle_t* cur = (QAngle_t*)(modules.getModule("client") + 0x1A78650);
    if (!cur) return;
    bool silentActive = Config::always_on_silent_aim ? true : (GetAsyncKeyState(Config::silent_aim_key) & 0x8000);
    // --- shooterAfterAim + triggerbot_key для сайлент аимбота ---
    if (Config::silent_shooterAfterAim && (GetAsyncKeyState(Config::triggerbot_key) & 0x8000)) {
        silentActive = true;
    }
    int shotsFired = lp ? lp->getShotsFired() : 0;
    shotCount = shotsFired;

    // Если кнопка только что нажата — ищем новую цель
    if (silentActive && !prevSilentState) {
        int nMaxHighestEntity = I::GameEntity->Instance->GetHighestEntityIndex();
        float bestFov = Config::aimbot_fov;
        C_CSPlayerPawn* bestTarget = nullptr;
        QAngle_t bestAngle = { 0, 0, 0 };
        for (int i = 1; i <= nMaxHighestEntity; i++) {
            auto Entity = I::GameEntity->Instance->Get(i);
            if (!Entity) continue;
            if (!Entity->handle().valid()) continue;
            SchemaClassInfoData_t* _class = nullptr;
            Entity->dump_class_info(&_class);
            if (!_class) continue;
            const uint32_t hash = HASH(_class->szName);
            if (hash == HASH("C_CSPlayerPawn")) {
                C_CSPlayerPawn* pawn = (C_CSPlayerPawn*)Entity;
                if (!pawn) continue;
                if (pawn->get_entity_by_handle() == lp->get_entity_by_handle()) continue;
                if (pawn->getHealth() <= 0) continue;
                if (Config::team_check && pawn->getTeam() == lp->getTeam()) continue;
                Vector_t target_pos = GetAimbotTargetPos(pawn, lep, *cur);
                QAngle_t angle = CalcAngles(target_pos, lep);
                angle.x *= -1.f;
                angle.y += 180.f;
                float fov = GetFov(*cur, angle);
                if (!std::isfinite(fov) || fov > bestFov) continue;
                bestFov = fov;
                bestTarget = pawn;
                bestAngle = angle;
            }
        }
        lockedTarget = bestTarget;
        last_punch_angle = {0,0,0};
    }

    // Если кнопка зажата, цели нет — постоянный поиск цели
    if (silentActive && !lockedTarget) {
        int nMaxHighestEntity = I::GameEntity->Instance->GetHighestEntityIndex();
        float bestFov = Config::aimbot_fov;
        C_CSPlayerPawn* bestTarget = nullptr;
        QAngle_t bestAngle = { 0, 0, 0 };
        for (int i = 1; i <= nMaxHighestEntity; i++) {
            auto Entity = I::GameEntity->Instance->Get(i);
            if (!Entity) continue;
            if (!Entity->handle().valid()) continue;
            SchemaClassInfoData_t* _class = nullptr;
            Entity->dump_class_info(&_class);
            if (!_class) continue;
            const uint32_t hash = HASH(_class->szName);
            if (hash == HASH("C_CSPlayerPawn")) {
                C_CSPlayerPawn* pawn = (C_CSPlayerPawn*)Entity;
                if (!pawn) continue;
                if (pawn->get_entity_by_handle() == lp->get_entity_by_handle()) continue;
                if (pawn->getHealth() <= 0) continue;
                if (Config::team_check && pawn->getTeam() == lp->getTeam()) continue;
                Vector_t target_pos = GetAimbotTargetPos(pawn, lep, *cur);
                QAngle_t angle = CalcAngles(target_pos, lep);
                angle.x *= -1.f;
                angle.y += 180.f;
                float fov = GetFov(*cur, angle);
                if (!std::isfinite(fov) || fov > bestFov) continue;
                bestFov = fov;
                bestTarget = pawn;
                bestAngle = angle;
            }
        }
        lockedTarget = bestTarget;
        last_punch_angle = {0,0,0};
    }

    // Если кнопка отпущена — сбрасываем захват
    if (!silentActive) {
        lockedTarget = nullptr;
        last_punch_angle = {0,0,0};
    }

    // Если есть захваченная цель и кнопка удерживается
    if (silentActive && lockedTarget) {
        // Проверяем, что цель всё ещё валидна
        if (!lp || !lp->handle().valid() || lp->getHealth() <= 0 ||
            lockedTarget->get_entity_by_handle() == lp->get_entity_by_handle() ||
            lockedTarget->getHealth() <= 0 ||
            !lockedTarget->handle().valid() ||
            (Config::team_check && lockedTarget->getTeam() == lp->getTeam())) {
            lockedTarget = nullptr;
            last_punch_angle = {0,0,0};
            prevSilentState = silentActive;
            return;
        }
        Vector_t target_pos = GetAimbotTargetPos(lockedTarget, lep, *cur);
        QAngle_t angle = CalcAngles(target_pos, lep);
        angle.x *= -1.f;
        angle.y += 180.f;
        //QAngle_t cur_punch_angle = *(QAngle_t*)((uintptr_t)lp + SchemaFinder::Get(hash_32_fnv1a_const("C_CSPlayerPawn->m_aimPunchAngle")));
        //// --- RCS ---
        //bool rcsActive = (Config::rcs || Config::silent_shooterAfterAim) && (shotCount > Config::rcsActivationShots);
        //if (rcsActive && shotCount > 1) {
        //    angle -= cur_punch_angle * 2.f;
        //    last_punch_angle = cur_punch_angle;
        //} else {
        //    last_punch_angle = {0,0,0};
        //}
        //angle.z = 0.f;
        //angle = angle.Normalize();
        // --- Smooth ---
        if (Config::silent_aim_smooth > 0.f) {
            QAngle_t delta = (angle - *cur).Normalize();
            float smooth = Config::silent_aim_smooth;
            angle = *cur + delta * (1.f / smooth);
            angle = angle.Normalize();
        }
        *cur = angle;
    }
    prevSilentState = silentActive;
}
