#include <iostream>
#include "visuals.h"
#include <Engine/Engine.h>
#include <cfloat>
#include <imgui/imgui.h>
#include <Windows.h>
#include <algorithm>
#include <mutex>
#include <ImGui/imgui_internal.h>
#include "../../../Graphics/Graphics.h"

#define IMGUI_DEFINE_MATH_OPERATORS
#define FlotationDevice(c) ImGui::ColorConvertFloat4ToU32(ImVec4(c[0], c[1], c[2], c[3]))

extern ImFont* Tahoma_BoldXP;

// ==========================================
// УТИЛИТЫ
// ==========================================

static void Outline(
    const ImVec2& Pos,
    const char* Text,
    const float Col[4])
{
    if (!Text || !*Text) return;
    ImDrawList* Draw = ImGui::GetBackgroundDrawList();
    if (Tahoma_BoldXP) ImGui::PushFont(Tahoma_BoldXP);
    const ImVec2 Position(std::roundf(Pos.x), std::roundf(Pos.y));
    Draw->AddText(Position + ImVec2(1, 1), IM_COL32(0, 0, 0, 255), Text);
    Draw->AddText(Position, FlotationDevice(Col), Text);
    if (Tahoma_BoldXP) ImGui::PopFont();
}

// Получить смещение клиентской области Roblox
static POINT GetRobloxOffset()
{
    POINT offset = { 0, 0 };
    HWND hwnd = FindWindowA(NULL, "Roblox");
    if (hwnd)
    {
        RECT rect;
        if (GetWindowRect(hwnd, &rect))
        {
            offset.x = rect.left;
            offset.y = rect.top;
        }
    }
    return offset;
}

namespace Visuals {

    void RunService() {
        if (Globals::VisualEngine.Address == 0) return;

        ImDrawList* Draw = ImGui::GetBackgroundDrawList();

        // Получаем смещение окна Roblox (важно для корректного ESP)
        POINT RobloxOffset = GetRobloxOffset();

        // Получаем позицию курсора относительно клиента Roblox
        POINT Cursor;
        GetCursorPos(&Cursor);
        HWND Roblox = FindWindowA(NULL, "Roblox");
        if (Roblox) ScreenToClient(Roblox, &Cursor);

        // FIX: Конвертируем в абсолютные экранные координаты для ImGui
        ImVec2 MousePos(
            (float)Cursor.x + RobloxOffset.x,
            (float)Cursor.y + RobloxOffset.y
        );

        // Лямбда для FOV
        auto DrawDecagon = [&](
            float Radius,
            float* Col,
            bool Spin,
            float SpinSpeed)
            {
                if (Radius <= 1.0f) return;
                static float Rotation = 0.f;
                if (Spin) Rotation += SpinSpeed / 1000.f;
                const int Sides = 10;
                ImVec2 Points[Sides];
                float Step = 2.f * IM_PI / Sides;
                for (int i = 0; i < Sides; i++) {
                    float Angle = i * Step + Rotation;
                    Points[i] = ImVec2(
                        MousePos.x + cosf(Angle) * Radius,
                        MousePos.y + sinf(Angle) * Radius
                    );
                }
                Draw->AddPolyline(Points, Sides, FlotationDevice(Col), true, 2.0f);
            };

        // FOV
        if (Globals::Aimbot::DrawFov)
            DrawDecagon(
                Globals::Aimbot::FovSize,
                Globals::Aimbot::FovColor,
                Globals::Aimbot::FovSpin,
                Globals::Aimbot::FovSpinSpeed
            );

        if (Globals::Silent::DrawFov)
            DrawDecagon(
                Globals::Silent::Fov,
                Globals::Silent::FovColor,
                Globals::Silent::FovSpin,
                Globals::Silent::FovSpinSpeed
            );

        if (!Globals::Visuals::Enabled) return;

        // Копируем кэш
        std::vector<Globals::CachedPlayer> players;
        {
            std::lock_guard<std::mutex> lock(Globals::Cache_Mutex);
            players = Globals::Player_Cache;
        }

        SDK::VisualEngine Ve(Globals::VisualEngine.Address);

        for (const auto& Player : players) {
            if (!Player.Character.Address || !Player.Head.Address) continue;
            if (Globals::Visuals::Render_Distance > 0.f &&
                Player.Distance > Globals::Visuals::Render_Distance) continue;

            // FIX: Используем Get_PartPosition() вместо Get_Primitive().Get_Position()
            // Get_Primitive() может давать смещённые координаты центра колизии
            SDK::Part HeadPart(Player.Head.Address);
            SDK::Vector3 HeadPos = HeadPart.Get_PartPosition();
            if (std::isnan(HeadPos.x) || std::isnan(HeadPos.y) || std::isnan(HeadPos.z))
                continue;

            // FIX: Определяем высоту персонажа по реальным частям тела
            // Для R15 берём от головы до ног
            // Для R6 тоже считаем реально
            float topOffset = 1.5f;   // над головой
            float bottomOffset = 3.2f; // под HumanoidRootPart

            // Если есть ноги — вычисляем bottomOffset точнее
            if (Player.LeftFoot.Address) {
                SDK::Part FootPart(Player.LeftFoot.Address);
                SDK::Vector3 FootPos = FootPart.Get_PartPosition();
                if (!std::isnan(FootPos.y)) {
                    bottomOffset = HeadPos.y - FootPos.y + 0.5f;
                }
            }
            else if (Player.LeftLeg.Address) {
                SDK::Part LegPart(Player.LeftLeg.Address);
                SDK::Vector3 LegPos = LegPart.Get_PartPosition();
                if (!std::isnan(LegPos.y)) {
                    bottomOffset = HeadPos.y - LegPos.y + 0.5f;
                }
            }

            SDK::Vector2 TopRaw = Ve.World_To_Screen(HeadPos + SDK::Vector3(0, topOffset, 0));
            SDK::Vector2 BottomRaw = Ve.World_To_Screen(HeadPos - SDK::Vector3(0, bottomOffset, 0));

            // FIX: Добавляем смещение клиентской области
            SDK::Vector2 Top = { TopRaw.x + RobloxOffset.x, TopRaw.y + RobloxOffset.y };
            SDK::Vector2 Bottom = { BottomRaw.x + RobloxOffset.x, BottomRaw.y + RobloxOffset.y };

            if (TopRaw.x <= 0 || BottomRaw.x <= 0) continue;

            float Height = std::abs(Bottom.y - Top.y);
            if (Height < 5.f) continue;

            float Width = Height * 0.55f;
            ImVec2 BPos(Top.x - Width / 2.f, Top.y);
            ImVec2 BSize(Width, Height);

            float right_text_y = 0.f;

            // ==========================================
            // BOX
            // ==========================================
            if (Globals::Visuals::Box) {
                float BoxThick = static_cast<float>(Globals::Visuals::Thickness);
                switch (Globals::Visuals::Box_Type) {
                case 0:
                    Draw->AddRect(
                        BPos,
                        BPos + BSize,
                        FlotationDevice(Globals::Visuals::Colors::Box),
                        0.f, 0,
                        BoxThick
                    );
                    break;
                case 1:
                {
                    float CW = BSize.x * 0.25f;
                    float CH = BSize.y * 0.25f;
                    ImU32 col = FlotationDevice(Globals::Visuals::Colors::Box);

                    Draw->AddLine(BPos, BPos + ImVec2(CW, 0), col, BoxThick);
                    Draw->AddLine(BPos, BPos + ImVec2(0, CH), col, BoxThick);
                    Draw->AddLine(BPos + ImVec2(BSize.x, 0), BPos + ImVec2(BSize.x - CW, 0), col, BoxThick);
                    Draw->AddLine(BPos + ImVec2(BSize.x, 0), BPos + ImVec2(BSize.x, CH), col, BoxThick);
                    Draw->AddLine(BPos + ImVec2(0, BSize.y), BPos + ImVec2(CW, BSize.y), col, BoxThick);
                    Draw->AddLine(BPos + ImVec2(0, BSize.y), BPos + ImVec2(0, BSize.y - CH), col, BoxThick);
                    Draw->AddLine(BPos + BSize, BPos + ImVec2(BSize.x - CW, BSize.y), col, BoxThick);
                    Draw->AddLine(BPos + BSize, BPos + ImVec2(BSize.x, BSize.y - CH), col, BoxThick);
                    break;
                }
                }
            }

            // ==========================================
            // BOX FILL
            // ==========================================
            if (Globals::Visuals::Box_Fill) {
                ImU32 topColor = FlotationDevice(Globals::Visuals::Colors::BoxFill_Top);
                ImU32 botColor = FlotationDevice(Globals::Visuals::Colors::BoxFill_Bottom);
                Draw->AddRectFilledMultiColor(
                    BPos,
                    BPos + BSize,
                    topColor, topColor,
                    botColor, botColor
                );
            }

            // ==========================================
            // HEALTHBAR
            // FIX: Убран gap, толщина бара не зависит от Thickness
            // ==========================================
            if (Globals::Visuals::Healthbar) {
                float HealthPct = std::clamp(Player.Health / 100.f, 0.f, 1.f);
                float r = 1.f - HealthPct;
                float g = HealthPct;
                ImU32 hpColor = IM_COL32(
                    (int)(r * 255),
                    (int)(g * 255),
                    0, 255
                );

                switch (Globals::Visuals::Healthbar_Type) {
                case 0: // Слева вертикальная
                {
                    float BarWidth = 4.f; // FIX: фиксированная ширина бара
                    ImVec2 BarPos = BPos - ImVec2(BarWidth + 3.f, 0);
                    // Фон
                    Draw->AddRectFilled(
                        BarPos,
                        BarPos + ImVec2(BarWidth, Height),
                        IM_COL32(0, 0, 0, 180)
                    );
                    // Полоска (снизу вверх)
                    float filled = Height * HealthPct;
                    Draw->AddRectFilled(
                        BarPos + ImVec2(0, Height - filled),
                        BarPos + ImVec2(BarWidth, Height),
                        hpColor
                    );
                    break;
                }
                case 1: // Снизу горизонтальная
                {
                    float BarHeight = 4.f; // FIX: фиксированная высота бара
                    ImVec2 BarPos = BPos + ImVec2(0, Height + 2.f);
                    // Фон
                    Draw->AddRectFilled(
                        BarPos,
                        BarPos + ImVec2(Width, BarHeight),
                        IM_COL32(0, 0, 0, 180)
                    );
                    // Полоска
                    Draw->AddRectFilled(
                        BarPos,
                        BarPos + ImVec2(Width * HealthPct, BarHeight),
                        hpColor
                    );
                    break;
                }
                }
            }

            // ==========================================
            // SKELETON
            // ==========================================
            if (Globals::Visuals::Skeleton || Globals::Visuals::Chams) {

                // FIX: Добавляем RobloxOffset к экранным координатам костей
                auto GetScreen = [&](
                    const SDK::Instance& inst,
                    ImVec2& out) -> bool
                    {
                        if (inst.Address == 0) return false;
                        SDK::Part part(inst.Address);
                        SDK::Vector3 pos = part.Get_PartPosition();
                        if (std::isnan(pos.x)) return false;
                        SDK::Vector2 s = Ve.World_To_Screen(pos);
                        if (s.x <= 0 || s.y <= 0) return false;
                        out = ImVec2(s.x + RobloxOffset.x, s.y + RobloxOffset.y);
                        return true;
                    };

                auto DrawBone = [&](
                    const SDK::Instance& from,
                    const SDK::Instance& to,
                    ImU32 color,
                    float thickness)
                    {
                        ImVec2 s1, s2;
                        if (!GetScreen(from, s1)) return;
                        if (!GetScreen(to, s2)) return;
                        Draw->AddLine(s1, s2, color, thickness);
                    };

                ImU32 skelColor = FlotationDevice(Globals::Visuals::Colors::Skeleton);
                ImU32 chamsColor = FlotationDevice(Globals::Visuals::Colors::Chams);

                auto Bone = [&](
                    const SDK::Instance& from,
                    const SDK::Instance& to)
                    {
                        if (Globals::Visuals::Chams)
                            DrawBone(from, to, chamsColor, 4.0f);
                        if (Globals::Visuals::Skeleton)
                            DrawBone(from, to, skelColor, 1.2f);
                    };

                bool isR15 = Player.UpperTorso.Address != 0;

                if (isR15) {
                    Bone(Player.Head, Player.UpperTorso);
                    Bone(Player.UpperTorso, Player.LowerTorso);

                    Bone(Player.UpperTorso, Player.LeftUpperArm);
                    Bone(Player.LeftUpperArm, Player.LeftLowerArm);
                    Bone(Player.LeftLowerArm, Player.LeftHand);

                    Bone(Player.UpperTorso, Player.RightUpperArm);
                    Bone(Player.RightUpperArm, Player.RightLowerArm);
                    Bone(Player.RightLowerArm, Player.RightHand);

                    Bone(Player.LowerTorso, Player.LeftUpperLeg);
                    Bone(Player.LeftUpperLeg, Player.LeftLowerLeg);
                    Bone(Player.LeftLowerLeg, Player.LeftFoot);

                    Bone(Player.LowerTorso, Player.RightUpperLeg);
                    Bone(Player.RightUpperLeg, Player.RightLowerLeg);
                    Bone(Player.RightLowerLeg, Player.RightFoot);
                }
                else {
                    Bone(Player.Head, Player.Torso);
                    Bone(Player.Torso, Player.LeftArm);
                    Bone(Player.Torso, Player.RightArm);
                    Bone(Player.Torso, Player.LeftLeg);
                    Bone(Player.Torso, Player.RightLeg);
                }
            }

            // ==========================================
            // NAME
            // ==========================================
            if (Globals::Visuals::Name) {
                std::string displayName;
                switch (Globals::Visuals::Name_Type) {
                case 0: displayName = Player.Name; break;
                case 1: displayName = Player.Name; break; // Display Name (пока = Name, добавь Display_Name в кэш если нужно)
                case 2: displayName = Player.Name; break;
                default: displayName = Player.Name; break;
                }
                ImVec2 textSize = ImGui::CalcTextSize(displayName.c_str());
                Outline(
                    ImVec2(Top.x - textSize.x / 2.f, BPos.y - 15.f),
                    displayName.c_str(),
                    Globals::Visuals::Colors::Name
                );
            }

            // ==========================================
            // DISTANCE
            // ==========================================
            if (Globals::Visuals::Distance) {
                char dBuf[32];
                sprintf_s(dBuf, "[%dm]", (int)Player.Distance);
                ImVec2 textSize = ImGui::CalcTextSize(dBuf);
                Outline(
                    ImVec2(Top.x - textSize.x / 2.f, Bottom.y + 5.f),
                    dBuf,
                    Globals::Visuals::Colors::Distance
                );
            }

            // ==========================================
            // HEALTH TEXT
            // ==========================================
            if (Globals::Visuals::Health) {
                char hBuf[32];
                sprintf_s(hBuf, "%d HP", (int)Player.Health);
                Outline(
                    ImVec2(BPos.x + BSize.x + 5.f, BPos.y + right_text_y),
                    hBuf,
                    Globals::Visuals::Colors::Health
                );
                right_text_y += 14.f;
            }

            // ==========================================
            // RIG TYPE
            // ==========================================
            if (Globals::Visuals::Rig_Type) {
                const char* rig = Player.UpperTorso.Address ? "R15" : "R6";
                Outline(
                    ImVec2(BPos.x + BSize.x + 5.f, BPos.y + right_text_y),
                    rig,
                    Globals::Visuals::Colors::Rig_Type
                );
                right_text_y += 14.f;
            }

            // ==========================================
            // TOOL
            // ==========================================
            if (Globals::Visuals::Tool && Player.Tool_Name != "None") {
                Outline(
                    ImVec2(BPos.x + BSize.x + 5.f, BPos.y + right_text_y),
                    Player.Tool_Name.c_str(),
                    Globals::Visuals::Colors::Tool
                );
                right_text_y += 14.f;
            }

        } // конец for
    } // конец RunService
} // конец namespace Visuals