#include "visuals.h"

void draw_text_with_outline(ImDrawList* draw_list, const ImVec2& pos, ImU32 outline_color, ImU32 text_color, const char* text, float thickness = 1.0f) {
    ImVec2 offsets[] = {
        ImVec2(-thickness, -thickness),
        ImVec2(thickness, -thickness),
        ImVec2(-thickness, thickness),
        ImVec2(thickness, thickness)
    };

    for (int i = 0; i < 4; ++i) {
        ImVec2 offset_pos = ImVec2(pos.x + offsets[i].x, pos.y + offsets[i].y);
        draw_list->AddText(offset_pos, outline_color, text);
    }

    draw_list->AddText(pos, text_color, text);
}

void rbx::visuals::esp() {
	auto draw = ImGui::GetBackgroundDrawList();

	{
		std::lock_guard<std::mutex> lock(cache::mutex_v);
		for (auto p : cache::targets_v) {
			if (!p.player_object.self) continue;

            if (cheats::visuals::esp) {
                auto head_pos = p.head.get_part_position();
                auto hrp_pos = p.humanoidrootpart.get_part_position();

                auto head_screen = globals::g_visual_engine.world_to_screen({ head_pos.x, head_pos.y + 1.f, head_pos.z });
                auto hrp_screen = globals::g_visual_engine.world_to_screen({ hrp_pos.x, hrp_pos.y - .4f, hrp_pos.z });
                if (head_screen.x == -1 || hrp_screen.x == -1) continue;
    
                auto width = std::abs(head_screen.y - hrp_screen.y) * 0.8f;
                auto height = std::abs(head_screen.y - hrp_screen.y) * 2.f;

                auto w = width * 0.4f;
                auto h = height * 0.25f;

                rbx::vector2_t top_left = { head_screen.x - width, head_screen.y };
                rbx::vector2_t bottom_right = { head_screen.x + width, head_screen.y + height };

                switch (cheats::visuals::esp_style) {
                case 0:
                    // box esp
                    if (cheats::visuals::outline)
                        draw->AddRect({ top_left.x, top_left.y }, { bottom_right.x, bottom_right.y }, IM_COL32_BLACK, 0.f, 0, 3.f);

                    draw->AddRect({ top_left.x, top_left.y }, { bottom_right.x, bottom_right.y }, cheats::visuals::main_color);
                    break;
                case 1:
                    // corner esp
                    ImVec2 tl[] = {
                        { top_left.x, top_left.y },
                        { top_left.x + w, top_left.y }
                    };

                    ImVec2 tl2[] = {
                        { top_left.x, top_left.y },
                        { top_left.x, top_left.y + h }
                    };

                    ImVec2 tr[] = {
                        { bottom_right.x, top_left.y },
                        { bottom_right.x - w, top_left.y }
                    };

                    ImVec2 tr2[] = {
                        { bottom_right.x, top_left.y },
                        { bottom_right.x, top_left.y + h }
                    };

                    ImVec2 bl[] = {
                        { top_left.x, bottom_right.y },
                        { top_left.x + w, bottom_right.y }
                    };

                    ImVec2 bl2[] = {
                        { top_left.x, bottom_right.y },
                        { top_left.x, bottom_right.y - h }
                    };

                    ImVec2 br[] = {
                        { bottom_right.x, bottom_right.y },
                        { bottom_right.x - w, bottom_right.y }
                    };

                    ImVec2 br2[] = {
                        { bottom_right.x, bottom_right.y },
                        { bottom_right.x, bottom_right.y - h }
                    };

                    // note : i used polylines to improve performance
                    // cuz normal lines were lagging

                    if (cheats::visuals::outline) {
                        draw->AddPolyline(tl, 2, IM_COL32_BLACK, 0, 3.f);
                        draw->AddPolyline(tl2, 2, IM_COL32_BLACK, 0, 3.f);
                        draw->AddPolyline(tr, 2, IM_COL32_BLACK, 0, 3.0f);
                        draw->AddPolyline(tr2, 2, IM_COL32_BLACK, 0, 3.f);
                        draw->AddPolyline(bl, 2, IM_COL32_BLACK, 0, 3.0f);
                        draw->AddPolyline(bl2, 2, IM_COL32_BLACK, 0, 3.f);
                        draw->AddPolyline(br, 2, IM_COL32_BLACK, 0, 3.0f);
                        draw->AddPolyline(br2, 2, IM_COL32_BLACK, 0, 3.f);
                    }

                    draw->AddPolyline(tl, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(tl2, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(tr, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(tr2, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(bl, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(bl2, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(br, 2, cheats::visuals::main_color, 0, 1.f);
                    draw->AddPolyline(br2, 2, cheats::visuals::main_color, 0, 1.f);
                    break;
                }

                if (cheats::visuals::filled) {
                    auto col = cheats::visuals::main_color;
                    col.Value.w *= 0.4f;
                    draw->AddRectFilled({ top_left.x, top_left.y }, { bottom_right.x, bottom_right.y }, col);
                }
            }

            if (cheats::visuals::skeleton) {
                if (p.rig == rig_type::r6) {
                    // r6

                    for (auto bone_set : r6_bones) {
                        auto fb = find_bone(p, bone_set.from);
                        auto tb = find_bone(p, bone_set.to);

                        auto from = globals::g_visual_engine.world_to_screen(fb.bone_part.get_part_position());
                        auto to = globals::g_visual_engine.world_to_screen(tb.bone_part.get_part_position());

                        if (cheats::visuals::outline)
                            draw->AddLine({ from.x, from.y }, { to.x, to.y }, IM_COL32_BLACK, 3.f);

                        draw->AddLine({ from.x, from.y }, { to.x, to.y }, cheats::visuals::main_color);
                    }
                }
                else if (p.rig == rig_type::r15) {
                    // r15

                    for (auto bone_set : r15_bones) {
                        auto fb = find_bone(p, bone_set.from);
                        auto tb = find_bone(p, bone_set.to);

                        auto from = globals::g_visual_engine.world_to_screen(fb.bone_part.get_part_position());
                        auto to = globals::g_visual_engine.world_to_screen(tb.bone_part.get_part_position());

                        if (cheats::visuals::outline)
                            draw->AddLine({ from.x, from.y }, { to.x, to.y }, IM_COL32_BLACK, 3.f);

                        draw->AddLine({ from.x, from.y }, { to.x, to.y }, cheats::visuals::main_color);
                    }
                }
                else {
                    // wat ;-;
                }
            }
		}
	}
}

void rbx::visuals::misc() {
	auto draw = ImGui::GetBackgroundDrawList();

    POINT x;
    GetCursorPos(&x);
    ScreenToClient(globals::our_window, &x);
    ImVec2 m = { float(x.x), float(x.y) };

    if (cheats::combat::fov) {
        if (cheats::visuals::outline)
            draw->AddCircle(m, cheats::combat::fov_radius, ImColor(0, 0, 0), 0, 3.f);

        draw->AddCircle(m, cheats::combat::fov_radius, cheats::visuals::main_color);
    }

	{
		std::lock_guard<std::mutex> lock(cache::mutex_v);
		for (auto p : cache::targets_v) {
			if (!p.player_object.self) continue;

            auto head = p.head.get_part_position();
            auto hrp = p.humanoidrootpart.get_part_position();

            auto head_s = globals::g_visual_engine.world_to_screen({ head.x, head.y + 1.f, head.z });
            auto hrp_s = globals::g_visual_engine.world_to_screen({ hrp.x, hrp.y - .4f, hrp.z });
            if (head_s.x == -1 || hrp_s.x == -1) continue;

            auto w = std::abs(head_s.y - hrp_s.y) * 0.8f;
            auto h = std::abs(head_s.y - hrp_s.y) * 2.f;

            rbx::vector2_t top_left = { head_s.x - w, head_s.y };
            rbx::vector2_t bottom_right = { head_s.x + w, head_s.y + h };

            if (cheats::visuals::name) {
                auto center_x = (top_left.x + bottom_right.x) / 2.f;
                auto text_size = ImGui::CalcTextSize(p.name.c_str());
                if (cheats::visuals::outline)
                    draw_text_with_outline(draw, { center_x - text_size.x / 2.f, top_left.y - 20.f }, IM_COL32_BLACK, cheats::visuals::main_color, p.name.c_str());
                else
                    draw->AddText({ center_x - text_size.x / 2.f, top_left.y - 20.f }, cheats::visuals::main_color, p.name.c_str());
            }

            if (cheats::visuals::distance) {
                auto d = p.distance > 10.f ? std::format("[ {:.1f}m ]", p.distance) : std::format("[ {:.0f}m ]", p.distance);
                auto center_x = (top_left.x + bottom_right.x) / 2.f;
                auto text_size = ImGui::CalcTextSize(d.c_str());
                if (cheats::visuals::outline)
                    draw_text_with_outline(draw, { center_x - text_size.x / 2.f, bottom_right.y + 17.f }, IM_COL32_BLACK, cheats::visuals::main_color, d.c_str());
                else
                    draw->AddText({ center_x - text_size.x / 2.f, bottom_right.y + 17.5f }, cheats::visuals::main_color, d.c_str());
            }

            if (cheats::visuals::health) {
                float h = p.health;
                auto h2 = h / 100.f;
                auto hh = bottom_right.y - top_left.y;
                auto hh2 = hh * h2;
                auto ht = bottom_right.y - hh2;

                if (cheats::visuals::outline)
                    draw->AddRectFilled({ top_left.x - 7.5f, top_left.y - 0.5f }, { top_left.x - 4.5f, bottom_right.y + 0.5f }, IM_COL32_BLACK);

                draw->AddRectFilled({ top_left.x - 7.0f, ht }, { top_left.x - 5.0f, bottom_right.y }, ImColor(36, 255, 109));
            }

            if (cheats::visuals::tracers) {
                auto h = globals::g_visual_engine.world_to_screen(p.humanoidrootpart.get_part_position());
                
                if (cheats::visuals::outline)
                    draw->AddLine(m, { h.x, h.y }, IM_COL32_BLACK, 3.f);

                draw->AddLine(m, { h.x, h.y }, cheats::visuals::main_color);
            }

            if (cheats::visuals::head_dot) {
                auto h = globals::g_visual_engine.world_to_screen(p.head.get_part_position());

                if (cheats::visuals::outline) {
                    draw->AddCircle({ h.x, h.y }, 9, IM_COL32_BLACK);
                    draw->AddCircle({ h.x, h.y }, 11, IM_COL32_BLACK);
                }

                draw->AddCircle({ h.x, h.y }, 10, cheats::visuals::main_color);
            }
		}
	}
}

void rbx::visuals::render() {
    esp();
	misc();
}