#include "esp_text.h"

#include "impl/ConfigModule.h"
#include "impl/MinimapModule.h"

#include <algorithm>

namespace esp_text
{
    namespace
    {
        struct pending_draw
        {
            float distance = 0.f;
            float font_size = 0.f;
            bool top_left = false;
            ImVec2 pos{};
            ImVec4 color{};
            std::string text;
        };

        std::vector<pending_draw> pending_draws;
        minimap_marker_settings markers;

        constexpr const char k_default_player[] = "\xE2\x88\x86";
        constexpr const char k_default_entity[] = "\xE2\x96\xA0";
        constexpr const char k_default_quest[] = "\xC3\x97";
        constexpr const char k_default_exfil[] = "\xE2\x97\x89";
        constexpr const char k_default_transit[] = "\xE2\x97\x88";
        constexpr const char k_default_height_up[] = "\xE2\x96\xB2";
        constexpr const char k_default_height_down[] = "\xE2\x96\xBC";

        std::string resolve_symbol(const std::string& configured, const char* fallback)
        {
            return configured.empty() ? std::string(fallback) : configured;
        }

        float scaled_size(const int size)
        {
            return static_cast<float>(size) * menu::get_scale_factor();
        }

        void refresh_minimap_markers()
        {
            if (!minimap_module_self)
            {
                markers.player = k_default_player;
                markers.entity = k_default_entity;
                markers.quest = k_default_quest;
                markers.exfil = k_default_exfil;
                markers.transit = k_default_transit;
                markers.height_up = k_default_height_up;
                markers.height_down = k_default_height_down;
                markers.size_player = scaled_size(10);
                markers.size_entity = scaled_size(7);
                markers.size_quest = scaled_size(13);
                markers.size_exfil = scaled_size(10);
                markers.size_transit = scaled_size(10);
                markers.size_height_up = scaled_size(7);
                markers.size_height_down = scaled_size(7);
                return;
            }

            markers.player = resolve_symbol(minimap_module_self->minimap_sym_player->get(), k_default_player);
            markers.entity = resolve_symbol(minimap_module_self->minimap_sym_entity->get(), k_default_entity);
            markers.quest = resolve_symbol(minimap_module_self->minimap_sym_quest->get(), k_default_quest);
            markers.exfil = resolve_symbol(minimap_module_self->minimap_sym_exfil->get(), k_default_exfil);
            markers.transit = resolve_symbol(minimap_module_self->minimap_sym_transit->get(), k_default_transit);
            markers.height_up = resolve_symbol(minimap_module_self->minimap_sym_height_up->get(), k_default_height_up);
            markers.height_down = resolve_symbol(minimap_module_self->minimap_sym_height_down->get(), k_default_height_down);

            markers.size_player = scaled_size(minimap_module_self->minimap_sym_player_size->get_value());
            markers.size_entity = scaled_size(minimap_module_self->minimap_sym_entity_size->get_value());
            markers.size_quest = scaled_size(minimap_module_self->minimap_sym_quest_size->get_value());
            markers.size_exfil = scaled_size(minimap_module_self->minimap_sym_exfil_size->get_value());
            markers.size_transit = scaled_size(minimap_module_self->minimap_sym_transit_size->get_value());
            markers.size_height_up = scaled_size(minimap_module_self->minimap_sym_height_up_size->get_value());
            markers.size_height_down = scaled_size(minimap_module_self->minimap_sym_height_down_size->get_value());
        }
    }

    settings current_settings()
    {
        settings s;
        if (!config_module) return s;

        s.outline = config_module->esp_text_outline->get_value();
        s.background = config_module->esp_text_background->get_value();
        s.clustering = config_module->esp_text_clustering->get_value();
        s.depth_sort = config_module->esp_text_depth_sort->get_value();
        return s;
    }

    bool minimap_marker_outline_enabled()
    {
        return minimap_module_self && minimap_module_self->minimap_marker_outline->get_value();
    }

    float esp_font_size()
    {
        constexpr int fallback = 12;
        const int size = config_module ? config_module->esp_text_font_size->get_value() : fallback;
        return static_cast<float>(size) * menu::get_scale_factor();
    }

    float quest_esp_font_size()
    {
        constexpr int fallback = 12;
        const int size = config_module ? config_module->quest_esp_font_size->get_value() : fallback;
        return static_cast<float>(size) * menu::get_scale_factor();
    }

    float minimap_icon_size()
    {
        constexpr int fallback = 10;
        const int size = minimap_module_self
            ? minimap_module_self->minimap_sym_player_size->get_value()
            : fallback;
        return static_cast<float>(size) * menu::get_scale_factor();
    }

    float minimap_label_font_size()
    {
        constexpr int fallback = 9;
        const int size = minimap_module_self
            ? minimap_module_self->minimap_text_font_size->get_value()
            : fallback;
        return static_cast<float>(size) * menu::get_scale_factor();
    }

    const minimap_marker_settings& minimap_markers()
    {
        return markers;
    }

    void begin_frame()
    {
        pending_draws.clear();
        refresh_minimap_markers();
    }

    void queue_centered(const ImVec2& pos, const ImVec4& col, const std::string& text, const float distance,
                        const float font_size)
    {
        pending_draws.push_back({distance, font_size, false, pos, col, text});
    }

    void queue_topleft(const ImVec2& top_left, const ImVec4& col, const std::string& text, const float distance,
                       const float font_size)
    {
        pending_draws.push_back({distance, font_size, true, top_left, col, text});
    }

    void flush_pending(ImDrawList* draw_list, ImFont* font)
    {
        if (pending_draws.empty()) return;

        const settings s = current_settings();
        std::sort(pending_draws.begin(), pending_draws.end(),
            [](const pending_draw& a, const pending_draw& b)
            {
                return a.distance > b.distance;
            });

        for (const pending_draw& entry : pending_draws)
        {
            if (entry.top_left)
                draw_topleft(draw_list, font, entry.font_size, entry.pos, entry.color, entry.text, s);
            else
                draw_centered(draw_list, font, entry.font_size, entry.pos, entry.color, entry.text, s);
        }

        pending_draws.clear();
    }
}