#define _USE_MATH_DEFINES
#include <windows.h>
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <thread>
#include <chrono>
#include <TlHelp32.h>
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#define MAX_PLAYERS 100
#define MAX_DISTANCE 500.0f
#define FOV 90.0f

struct Vector3 {
    float x, y, z;
    
    Vector3() : x(0), y(0), z(0) {}
    Vector3(float x, float y, float z) : x(x), y(y), z(z) {}
    
    Vector3 operator+(const Vector3& other) const {
        return Vector3(x + other.x, y + other.y, z + other.z);
    }
    
    Vector3 operator-(const Vector3& other) const {
        return Vector3(x - other.x, y - other.y, z - other.z);
    }
    
    Vector3 operator*(float scalar) const {
        return Vector3(x * scalar, y * scalar, z * scalar);
    }
    
    float Distance(const Vector3& other) const {
        float dx = x - other.x;
        float dy = y - other.y;
        float dz = z - other.z;
        return sqrt(dx * dx + dy * dy + dz * dz);
    }
    
    float Length() const {
        return sqrt(x * x + y * y + z * z);
    }
    
    Vector3 Normalize() const {
        float len = Length();
        if (len > 0) {
            return Vector3(x / len, y / len, z / len);
        }
        return *this;
    }
    
    float Dot(const Vector3& other) const {
        return x * other.x + y * other.y + z * other.z;
    }
};

struct Matrix4x4 {
    float m[4][4];
    
    Vector3 Transform(const Vector3& point) const {
        Vector3 result;
        result.x = point.x * m[0][0] + point.y * m[1][0] + point.z * m[2][0] + m[3][0];
        result.y = point.x * m[0][1] + point.y * m[1][1] + point.z * m[2][1] + m[3][1];
        result.z = point.x * m[0][2] + point.y * m[1][2] + point.z * m[2][2] + m[3][2];
        float w = point.x * m[0][3] + point.y * m[1][3] + point.z * m[2][3] + m[3][3];
        
        if (w != 0.0f) {
            result.x /= w;
            result.y /= w;
            result.z /= w;
        }
        return result;
    }
};

struct PlayerInfo {
    DWORD address;
    Vector3 position;
    Vector3 headPosition;
    Vector3 velocity;
    Vector3 viewAngle;
    float health;
    float distance;
    int team;
    char name[32];
    bool isVisible;
    bool isValid;
    int boneCount;
    Vector3 bones[100];
};

struct GameData {
    Vector3 localPosition;
    Vector3 localViewAngle;
    Vector3 localHeadPosition;
    Matrix4x4 viewMatrix;
    int localTeam;
    bool isInGame;
    DWORD localPlayerAddress;
};

class MemoryManager {
private:
    HANDLE hProcess;
    DWORD processId;
    DWORD clientBase;
    DWORD engineBase;
    
public:
    MemoryManager() : hProcess(NULL), processId(0), clientBase(0), engineBase(0) {}
    
    bool Initialize(const char* processName) {
        processId = GetProcessIdByName(processName);
        if (processId == 0) {
            std::cout << "[ERROR] Process not found: " << processName << std::endl;
            return false;
        }
        
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
        if (hProcess == NULL) {
            std::cout << "[ERROR] Failed to open process" << std::endl;
            return false;
        }
        
        clientBase = GetModuleBase("client.dll");
        engineBase = GetModuleBase("engine.dll");
        
        if (clientBase == 0 || engineBase == 0) {
            std::cout << "[ERROR] Failed to get module bases" << std::endl;
            return false;
        }
        
        std::cout << "[INFO] Memory manager initialized successfully" << std::endl;
        std::cout << "[INFO] Client base: 0x" << std::hex << clientBase << std::dec << std::endl;
        std::cout << "[INFO] Engine base: 0x" << std::hex << engineBase << std::dec << std::endl;
        
        return true;
    }
    
    DWORD GetProcessIdByName(const char* processName) {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(PROCESSENTRY32);
        
        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnapshot == INVALID_HANDLE_VALUE) return 0;
        
        if (Process32First(hSnapshot, &pe32)) {
            do {
                if (_stricmp(pe32.szExeFile, processName) == 0) {
                    CloseHandle(hSnapshot);
                    return pe32.th32ProcessID;
                }
            } while (Process32Next(hSnapshot, &pe32));
        }
        
        CloseHandle(hSnapshot);
        return 0;
    }
    
    DWORD GetModuleBase(const char* moduleName) {
        MODULEENTRY32 me32;
        me32.dwSize = sizeof(MODULEENTRY32);
        
        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
        if (hSnapshot == INVALID_HANDLE_VALUE) return 0;
        
        if (Module32First(hSnapshot, &me32)) {
            do {
                if (_stricmp(me32.szModule, moduleName) == 0) {
                    CloseHandle(hSnapshot);
                    return (DWORD)me32.modBaseAddr;
                }
            } while (Module32Next(hSnapshot, &me32));
        }
        
        CloseHandle(hSnapshot);
        return 0;
    }
    
    template<typename T>
    T Read(DWORD address) {
        T buffer;
        ReadProcessMemory(hProcess, (LPCVOID)address, &buffer, sizeof(T), NULL);
        return buffer;
    }
    
    template<typename T>
    bool Write(DWORD address, T value) {
        return WriteProcessMemory(hProcess, (LPVOID)address, &value, sizeof(T), NULL) != 0;
    }
    
    bool ReadBuffer(DWORD address, LPVOID buffer, SIZE_T size) {
        return ReadProcessMemory(hProcess, (LPCVOID)address, buffer, size, NULL) != 0;
    }
    
    Vector3 ReadVector3(DWORD address) {
        Vector3 vec;
        ReadProcessMemory(hProcess, (LPCVOID)address, &vec, sizeof(Vector3), NULL);
        return vec;
    }
    
    Matrix4x4 ReadMatrix(DWORD address) {
        Matrix4x4 mat;
        ReadProcessMemory(hProcess, (LPCVOID)address, &mat, sizeof(Matrix4x4), NULL);
        return mat;
    }
    
    void Cleanup() {
        if (hProcess) {
            CloseHandle(hProcess);
            hProcess = NULL;
        }
    }
    
    DWORD GetClientBase() const { return clientBase; }
    DWORD GetEngineBase() const { return engineBase; }
    HANDLE GetProcessHandle() const { return hProcess; }
};

class ThreeDTracker {
private:
    MemoryManager memory;
    GameData gameData;
    std::vector<PlayerInfo> players;
    bool isRunning;
    
public:
    ThreeDTracker() : isRunning(false) {}
    
    bool Initialize() {
        if (!memory.Initialize("TslGame.exe")) {
            return false;
        }
        
        gameData.isInGame = false;
        return true;
    }
    
    void UpdateGameData() {
        DWORD clientState = memory.Read<DWORD>(memory.GetEngineBase() + 0x59F19C);
        if (clientState) {
            gameData.isInGame = memory.Read<int>(clientState + 0x108) == 6;
        }
        
        if (!gameData.isInGame) return;
        
        DWORD localPlayer = memory.Read<DWORD>(memory.GetClientBase() + 0xDE6964);
        if (localPlayer) {
            gameData.localPlayerAddress = localPlayer;
            gameData.localPosition = memory.ReadVector3(localPlayer + 0x138);
            gameData.localViewAngle = memory.ReadVector3(localPlayer + 0x158);
            gameData.localTeam = memory.Read<int>(localPlayer + 0xF4);
            
            DWORD boneMatrix = memory.Read<DWORD>(localPlayer + 0x26A8);
            if (boneMatrix) {
                gameData.localHeadPosition = ReadBonePosition(boneMatrix, 8);
            }
            
            DWORD viewMatrixAddr = memory.GetClientBase() + 0x4DF0E44;
            gameData.viewMatrix = memory.ReadMatrix(viewMatrixAddr);
        }
    }
    
    Vector3 ReadBonePosition(DWORD boneMatrix, int boneIndex) {
        Vector3 position;
        DWORD boneAddr = boneMatrix + boneIndex * 48;
        position.x = memory.Read<float>(boneAddr + 0x0C);
        position.y = memory.Read<float>(boneAddr + 0x1C);
        position.z = memory.Read<float>(boneAddr + 0x2C);
        return position;
    }
    
    void UpdatePlayers() {
        players.clear();
        
        DWORD entityList = memory.GetClientBase() + 0x4E0102C;
        for (int i = 0; i < MAX_PLAYERS; i++) {
            DWORD entity = memory.Read<DWORD>(entityList + i * 0x10);
            if (!entity) continue;
            
            PlayerInfo player;
            player.address = entity;
            player.health = memory.Read<float>(entity + 0x100);
            player.team = memory.Read<int>(entity + 0xF4);
            player.position = memory.ReadVector3(entity + 0x138);
            player.viewAngle = memory.ReadVector3(entity + 0x158);
            player.distance = gameData.localPosition.Distance(player.position);
            
            if (player.health <= 0 || player.distance > MAX_DISTANCE) continue;
            
            player.isValid = true;
            player.isVisible = CheckVisibility(entity);
            player.team = memory.Read<int>(entity + 0xF4);
            
            memory.ReadBuffer(entity + 0x225, player.name, sizeof(player.name));
            
            DWORD boneMatrix = memory.Read<DWORD>(entity + 0x26A8);
            if (boneMatrix) {
                player.headPosition = ReadBonePosition(boneMatrix, 8);
                player.boneCount = 100;
                for (int j = 0; j < 100; j++) {
                    player.bones[j] = ReadBonePosition(boneMatrix, j);
                }
            }
            
            players.push_back(player);
        }
    }
    
    bool CheckVisibility(DWORD entity) {
        DWORD spotted = memory.Read<DWORD>(entity + 0x93D);
        return (spotted & (1 << 6)) != 0;
    }
    
    Vector3 WorldToScreen(const Vector3& worldPos) {
        Vector3 screenPos = gameData.viewMatrix.Transform(worldPos);
        
        int width = GetSystemMetrics(SM_CXSCREEN);
        int height = GetSystemMetrics(SM_CYSCREEN);
        
        screenPos.x = (width / 2.0f) + (screenPos.x * width) / (2.0f * screenPos.z);
        screenPos.y = (height / 2.0f) - (screenPos.y * height) / (2.0f * screenPos.z);
        
        return screenPos;
    }
    
    Vector3 CalculateAimAngle(const Vector3& targetPos) {
        Vector3 delta = targetPos - gameData.localHeadPosition;
        
        Vector3 angles;
        angles.x = -atan2(delta.z, sqrt(delta.x * delta.x + delta.y * delta.y)) * (180.0f / M_PI);
        angles.y = atan2(delta.y, delta.x) * (180.0f / M_PI);
        angles.z = 0;
        
        angles.x = NormalizeAngle(angles.x);
        angles.y = NormalizeAngle(angles.y);
        
        return angles;
    }
    
    float NormalizeAngle(float angle) {
        while (angle > 180.0f) angle -= 360.0f;
        while (angle < -180.0f) angle += 360.0f;
        return angle;
    }
    
    float GetFOV(const Vector3& aimAngle) {
        Vector3 delta = aimAngle - gameData.localViewAngle;
        delta.x = NormalizeAngle(delta.x);
        delta.y = NormalizeAngle(delta.y);
        return sqrt(delta.x * delta.x + delta.y * delta.y);
    }
    
    PlayerInfo FindBestTarget() {
        PlayerInfo bestTarget;
        float bestScore = -1.0f;
        
        for (const auto& player : players) {
            if (!player.isValid || player.team == gameData.localTeam) continue;
            
            Vector3 aimAngle = CalculateAimAngle(player.headPosition);
            float fov = GetFOV(aimAngle);
            float distance = player.distance;
            
            if (fov > FOV || distance > MAX_DISTANCE) continue;
            
            float visibilityScore = player.isVisible ? 1.5f : 1.0f;
            float distanceScore = 1.0f - (distance / MAX_DISTANCE);
            float fovScore = 1.0f - (fov / FOV);
            
            float score = (distanceScore * 0.4f + fovScore * 0.4f + visibilityScore * 0.2f);
            
            if (score > bestScore) {
                bestScore = score;
                bestTarget = player;
            }
        }
        
        return bestTarget;
    }
    
    void SmoothAim(Vector3& currentAngle, const Vector3& targetAngle, float smoothFactor) {
        Vector3 delta = targetAngle - currentAngle;
        delta.x = NormalizeAngle(delta.x);
        delta.y = NormalizeAngle(delta.y);
        
        delta.x *= smoothFactor;
        delta.y *= smoothFactor;
        
        currentAngle = currentAngle + delta;
        currentAngle.x = NormalizeAngle(currentAngle.x);
        currentAngle.y = NormalizeAngle(currentAngle.y);
    }
    
    void WriteViewAngles(const Vector3& angles) {
        DWORD clientState = memory.Read<DWORD>(memory.GetEngineBase() + 0x59F19C);
        if (clientState) {
            memory.Write<Vector3>(clientState + 0x4D90, angles);
        }
    }
    
    void DrawESP() {
        for (const auto& player : players) {
            if (!player.isValid || player.distance > MAX_DISTANCE) continue;
            
            Vector3 screenPos = WorldToScreen(player.position);
            Vector3 headScreenPos = WorldToScreen(player.headPosition);
            
            if (screenPos.z > 0 && headScreenPos.z > 0) {
                float height = abs(headScreenPos.y - screenPos.y);
                float width = height * 0.6f;
                
                DrawBox(screenPos.x - width/2, headScreenPos.y, width, height, 
                       player.isVisible ? D3DCOLOR_ARGB(255, 0, 255, 0) : D3DCOLOR_ARGB(255, 255, 0, 0));
                
                DrawHealthBar(screenPos.x - width/2 - 5, headScreenPos.y, 3, height, player.health);
                
                char info[128];
                sprintf_s(info, "[%.1fm] %s", player.distance, player.name);
                DrawText(info, screenPos.x, headScreenPos.y - 20, D3DCOLOR_ARGB(255, 255, 255, 255));
            }
        }
    }
    
    void DrawBox(float x, float y, float width, float height, D3DCOLOR color) {
        // Box drawing implementation
    }
    
    void DrawHealthBar(float x, float y, float width, float height, float health) {
        // Health bar implementation
    }
    
    void DrawText(const char* text, float x, float y, D3DCOLOR color) {
        // Text drawing implementation
    }
    
    void PrintDebugInfo() {
        system("cls");
        std::cout << "=== 3D Coordinate Tracker ===" << std::endl;
        std::cout << "Local Position: " << gameData.localPosition.x << ", " 
                  << gameData.localPosition.y << ", " << gameData.localPosition.z << std::endl;
        std::cout << "Local View Angle: " << gameData.localViewAngle.x << ", "
                  << gameData.localViewAngle.y << ", " << gameData.localViewAngle.z << std::endl;
        std::cout << "Players Found: " << players.size() << std::endl;
        std::cout << "In Game: " << (gameData.isInGame ? "Yes" : "No") << std::endl;
        std::cout << "Local Team: " << gameData.localTeam << std::endl;
        
        for (int i = 0; i < std::min(5, (int)players.size()); i++) {
            const auto& player = players[i];
            std::cout << "\nPlayer " << i+1 << ": " << player.name << std::endl;
            std::cout << "  Position: " << player.position.x << ", " 
                      << player.position.y << ", " << player.position.z << std::endl;
            std::cout << "  Distance: " << player.distance << "m" << std::endl;
            std::cout << "  Health: " << player.health << std::endl;
            std::cout << "  Team: " << player.team << std::endl;
            std::cout << "  Visible: " << (player.isVisible ? "Yes" : "No") << std::endl;
        }
        
        if (!players.empty()) {
            PlayerInfo target = FindBestTarget();
            if (target.isValid) {
                std::cout << "\n=== Best Target ===" << std::endl;
                std::cout << "Name: " << target.name << std::endl;
                std::cout << "Distance: " << target.distance << "m" << std::endl;
                std::cout << "Position: " << target.position.x << ", "
                         << target.position.y << ", " << target.position.z << std::endl;
                
                Vector3 aimAngle = CalculateAimAngle(target.headPosition);
                std::cout << "Aim Angle: " << aimAngle.x << ", " << aimAngle.y << std::endl;
                std::cout << "FOV: " << GetFOV(aimAngle) << std::endl;
            }
        }
    }
    
    void Run() {
        isRunning = true;
        
        while (isRunning) {
            UpdateGameData();
            
            if (gameData.isInGame) {
                UpdatePlayers();
                
                if (GetAsyncKeyState(VK_F6) & 1) {
                    PrintDebugInfo();
                }
                
                if (GetAsyncKeyState(VK_F7) & 1) {
                    PlayerInfo target = FindBestTarget();
                    if (target.isValid) {
                        Vector3 aimAngle = CalculateAimAngle(target.headPosition);
                        SmoothAim(gameData.localViewAngle, aimAngle, 0.3f);
                        WriteViewAngles(gameData.localViewAngle);
                    }
                }
                
                if (GetAsyncKeyState(VK_F8) & 1) {
                    // Draw ESP
                }
            }
            
            if (GetAsyncKeyState(VK_END) & 1) {
                isRunning = false;
            }
            
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        
        Cleanup();
    }
    
    void Cleanup() {
        memory.Cleanup();
        std::cout << "[INFO] Tracker cleaned up" << std::endl;
    }
};

int main() {
    ThreeDTracker tracker;
    
    std::cout << "3D Coordinate Tracker Initializing..." << std::endl;
    std::cout << "Controls:" << std::endl;
    std::cout << "F6 - Show debug info" << std::endl;
    std::cout << "F7 - Aim at best target" << std::endl;
    std::cout << "F8 - Toggle ESP" << std::endl;
    std::cout << "END - Exit program" << std::endl;
    
    if (tracker.Initialize()) {
        tracker.Run();
    } else {
        std::cout << "[ERROR] Failed to initialize tracker" << std::endl;
        system("pause");
    }
    
    return 0;
}