#pragma once
#include "OS-ImGui/imgui/imgui.h"
#include "../game/entity.h"
#include "../game/math.h"
#include <vector>

class ESPRenderer {
public:
    ESPRenderer(int width, int height) : screenWidth(width), screenHeight(height) {}
    
    void RenderESP(const std::vector<Entity>& entities, const Vector3& localPos, const Matrix& viewMatrix);
    
private:
    int screenWidth;
    int screenHeight;
    
    // Drawing functions
    void DrawBox(const Vector2& head, const Vector2& origin, ImU32 color, bool filled, bool isVisible);
    void DrawHealthBar(const Vector2& head, const Vector2& origin, int health, int maxHealth);
    void DrawShieldBar(const Vector2& head, const Vector2& origin, int shield, int maxShield, int armorType, bool hasHealthBar);
    void DrawDistance(const Vector2& position, float distance);
    void DrawCorneredBox(float x, float y, float w, float h, ImU32 color, float thickness, bool filled);
    void DrawSkeleton(Entity& entity, const Matrix& viewMatrix, ImU32 color, float thickness);
};

inline void ESPRenderer::DrawSkeleton(Entity& entity, const Matrix& viewMatrix, ImU32 color, float thickness) {
    if (!entity.hasBones || entity.cachedBones.empty())
        return;

    auto GetScreenPos = [&](int indexInList) -> Vector2 {
        return WorldToScreen(entity.cachedBones[indexInList], viewMatrix, (float)screenWidth, (float)screenHeight);
    };

    ImDrawList* draw = ImGui::GetBackgroundDrawList();
    auto DrawBoneLine = [&](int idx1, int idx2) {
        Vector2 p1 = GetScreenPos(idx1);
        Vector2 p2 = GetScreenPos(idx2);
        if (p1.IsValid(screenWidth, screenHeight) && p2.IsValid(screenWidth, screenHeight)) {
            // Outline for visibility
            draw->AddLine(ImVec2(p1.x, p1.y), ImVec2(p2.x, p2.y), IM_COL32(0, 0, 0, 200), thickness + 1.0f);
            draw->AddLine(ImVec2(p1.x, p1.y), ImVec2(p2.x, p2.y), color, thickness);
        }
    };

    // Connections
    DrawBoneLine(0, 1); // Head to Neck
    DrawBoneLine(1, 2); // Neck to Spine
    DrawBoneLine(2, 3); // Spine to Pelvis
    
    // Arms
    DrawBoneLine(2, 4); // Spine to L_Shoulder
    DrawBoneLine(4, 5); // L_Shoulder to L_Elbow
    DrawBoneLine(5, 6); // L_Elbow to L_Hand
    
    DrawBoneLine(2, 7); // Spine to R_Shoulder
    DrawBoneLine(7, 8); // R_Shoulder to R_Elbow
    DrawBoneLine(8, 9); // R_Elbow to R_Hand
    
    // Legs
    DrawBoneLine(3, 10); // Pelvis to L_Hip
    DrawBoneLine(10, 11); // L_Hip to L_Knee
    DrawBoneLine(11, 12); // L_Knee to L_Foot
    
    DrawBoneLine(3, 13); // Pelvis to R_Hip
    DrawBoneLine(13, 14); // R_Hip to R_Knee
    DrawBoneLine(14, 15); // R_Knee to R_Foot
}

inline void ESPRenderer::DrawCorneredBox(float x, float y, float w, float h, ImU32 color, float thickness, bool filled) {
    ImDrawList* draw = ImGui::GetBackgroundDrawList();
    
    // Background fill
    if (filled) {
        draw->AddRectFilled(ImVec2(x, y), ImVec2(x + w, y + h), IM_COL32(0, 0, 0, 40));
    }

    // Black outline for better visibility
    ImU32 outlineColor = IM_COL32(0, 0, 0, 200);
    float outlineThickness = thickness + 1.5f;

    auto DrawCorner = [&](float x1, float y1, float x2, float y2, float x3, float y3, ImU32 col, float thick) {
        draw->AddLine(ImVec2(x1, y1), ImVec2(x2, y2), col, thick);
        draw->AddLine(ImVec2(x1, y1), ImVec2(x3, y3), col, thick);
    };

    float lineW = w / 4.0f;
    float lineH = h / 4.0f;
    
    // Top left
    DrawCorner(x, y, x + lineW, y, x, y + lineH, outlineColor, outlineThickness);
    DrawCorner(x, y, x + lineW, y, x, y + lineH, color, thickness);
    
    // Top right
    DrawCorner(x + w, y, x + w - lineW, y, x + w, y + lineH, outlineColor, outlineThickness);
    DrawCorner(x + w, y, x + w - lineW, y, x + w, y + lineH, color, thickness);
    
    // Bottom left
    DrawCorner(x, y + h, x + lineW, y + h, x, y + h - lineH, outlineColor, outlineThickness);
    DrawCorner(x, y + h, x + lineW, y + h, x, y + h - lineH, color, thickness);
    
    // Bottom right
    DrawCorner(x + w, y + h, x + w - lineW, y + h, x + w, y + h - lineH, outlineColor, outlineThickness);
    DrawCorner(x + w, y + h, x + w - lineW, y + h, x + w, y + h - lineH, color, thickness);
}

inline void ESPRenderer::DrawBox(const Vector2& head, const Vector2& origin, ImU32 color, bool filled, bool isVisible) {
    float height = abs(origin.y - head.y);
    float width = height * 0.55f;
    float x = head.x - (width / 2.0f);
    float y = head.y;
    
    DrawCorneredBox(x, y, width, height, color, 1.5f, filled);
}

inline void ESPRenderer::DrawHealthBar(const Vector2& head, const Vector2& origin, int health, int maxHealth) {
    if (maxHealth <= 0) return;
    
    float height = abs(origin.y - head.y);
    float width = height * 0.55f;
    float x = head.x - (width / 2.0f);
    float y = head.y;
    
    ImDrawList* draw = ImGui::GetBackgroundDrawList();
    
    float barHeight = (float)std::clamp(health, 0, maxHealth) / (float)maxHealth * height;
    float barY = y + (height - barHeight);
    
    float barWidth = 3.0f;
    float posX = x - barWidth - 3.0f;

    // Outer outline
    draw->AddRect(
        ImVec2(posX - 1, y - 1),
        ImVec2(posX + barWidth + 1, y + height + 1),
        IM_COL32(0, 0, 0, 220),
        0, 0, 1.0f
    );

    // Background
    draw->AddRectFilled(
        ImVec2(posX, y),
        ImVec2(posX + barWidth, y + height),
        IM_COL32(20, 20, 20, 180)
    );
    
    // Health color based on percentage
    float hpPercent = (float)health / (float)maxHealth;
    ImU32 hpColor = IM_COL32(0, 255, 0, 255); // Green
    if (hpPercent < 0.3f) hpColor = IM_COL32(255, 0, 0, 255); // Red
    else if (hpPercent < 0.7f) hpColor = IM_COL32(255, 255, 0, 255); // Yellow

    // Health bar
    draw->AddRectFilled(
        ImVec2(posX, barY),
        ImVec2(posX + barWidth, y + height),
        hpColor
    );

    // Segments (every 25 health)
    if (maxHealth > 25) {
        for (int i = 1; i < (maxHealth / 25); i++) {
            float segY = y + height - (height * (i * 25.0f / maxHealth));
            draw->AddLine(ImVec2(posX, segY), ImVec2(posX + barWidth, segY), IM_COL32(0, 0, 0, 150));
        }
    }
}

inline void ESPRenderer::DrawShieldBar(const Vector2& head, const Vector2& origin, int shield, int maxShield, int armorType, bool hasHealthBar) {
    if (maxShield <= 0) return;
    
    float height = abs(origin.y - head.y);
    float width = height * 0.55f;
    float x = head.x - (width / 2.0f);
    float y = head.y;
    
    ImDrawList* draw = ImGui::GetBackgroundDrawList();
    
    float barHeight = (float)std::clamp(shield, 0, maxShield) / (float)maxShield * height;
    float barY = y + (height - barHeight);
    
    float barWidth = 3.0f;
    float posX = hasHealthBar ? (x - (barWidth * 2) - 6.0f) : (x - barWidth - 3.0f);

    // Outer outline
    draw->AddRect(
        ImVec2(posX - 1, y - 1),
        ImVec2(posX + barWidth + 1, y + height + 1),
        IM_COL32(0, 0, 0, 220),
        0, 0, 1.0f
    );

    // Background
    draw->AddRectFilled(
        ImVec2(posX, y),
        ImVec2(posX + barWidth, y + height),
        IM_COL32(20, 20, 20, 180)
    );
    
    // Shield color based on armor type
    ImU32 shieldColor = IM_COL32(255, 255, 255, 255); // White (Level 1)
    if (armorType == 2) shieldColor = IM_COL32(0, 180, 255, 255); // Blue (Level 2)
    else if (armorType == 3) shieldColor = IM_COL32(200, 0, 255, 255); // Purple (Level 3)
    else if (armorType == 4) shieldColor = IM_COL32(255, 200, 0, 255); // Gold (Level 4)
    else if (armorType == 5) shieldColor = IM_COL32(255, 50, 50, 255); // Red (Level 5)

    // Shield bar
    draw->AddRectFilled(
        ImVec2(posX, barY),
        ImVec2(posX + barWidth, y + height),
        shieldColor
    );

    // Segments (every 25 shield)
    if (maxShield > 25) {
        for (int i = 1; i < (maxShield / 25); i++) {
            float segY = y + height - (height * (i * 25.0f / maxShield));
            draw->AddLine(ImVec2(posX, segY), ImVec2(posX + barWidth, segY), IM_COL32(0, 0, 0, 150));
        }
    }
}

inline void ESPRenderer::DrawDistance(const Vector2& position, float distance) {
    ImDrawList* draw = ImGui::GetBackgroundDrawList();
    
    char distText[32];
    sprintf_s(distText, "[ %.0fm ]", distance / 39.37f); // Convert inches to meters
    
    ImVec2 textSize = ImGui::CalcTextSize(distText);
    ImVec2 pos = ImVec2(position.x - textSize.x / 2.0f, position.y + 5);

    // Background shadow/glow
    draw->AddRectFilled(
        ImVec2(pos.x - 2, pos.y - 1),
        ImVec2(pos.x + textSize.x + 2, pos.y + textSize.y + 1),
        IM_COL32(0, 0, 0, 120),
        2.0f
    );

    draw->AddText(
        pos,
        IM_COL32(255, 255, 255, 255),
        distText
    );
}

inline void ESPRenderer::RenderESP(const std::vector<Entity>& entities, const Vector3& localPos, const Matrix& viewMatrix) {
    for (const auto& entity : entities) {
        if (!entity.valid) continue;

        // World to screen
        Vector3 headWorld = entity.position + Vector3(0, 0, 72); // Head height
        Vector2 head = WorldToScreen(headWorld, viewMatrix, (float)screenWidth, (float)screenHeight);
        Vector2 feet = WorldToScreen(entity.position, viewMatrix, (float)screenWidth, (float)screenHeight);
        
        // Validate screen coordinates
        if (!head.IsValid((float)screenWidth, (float)screenHeight) || 
            !feet.IsValid((float)screenWidth, (float)screenHeight)) {
            continue;
        }
        
        // Color based on status
        ImU32 boxColor = IM_COL32(255, 0, 0, 255); // Default Red
        if (entity.isKnocked) {
            boxColor = IM_COL32(150, 150, 150, 255); // Grey for knocked
        } else if (entity.isVisible) {
            boxColor = IM_COL32(0, 255, 255, 255); // Cyan for visible
        } else if (entity.isDummy) {
            boxColor = IM_COL32(255, 255, 0, 255); // Yellow for dummies
        }
        
        // Draw box
        DrawBox(head, feet, boxColor, true, entity.isVisible);
        
        // Draw health bar
        if (entity.maxHealth > 0) {
            DrawHealthBar(head, feet, entity.health, entity.maxHealth);
        }
        
        // Draw shield bar
        if (entity.maxShield > 0) {
            DrawShieldBar(head, feet, entity.shield, entity.maxShield, entity.armorType, entity.maxHealth > 0);
        }
        
        // Draw distance
        DrawDistance(feet, entity.distance);
    }
}
