#pragma once

#include "../Module.h"
#include "../ModuleCategory.h"
#include "../../config/ActionRowValue.h"
#include "../../config/CheckboxValue.h"
#include "../../config/FloatSliderValue.h"
#include "../../config/MenuSectionValue.h"
#include "../../config/SelectValue.h"
#include "../../gui/menu/imgui_addons.h"
#include "../../module/minimap.h"
#include "../game_state.h"
#include "../../util/i18n.h"
#include "../../util/logger.h"

class MapEditorModule : public Module
{
public:
    MapEditorModule() : Module("Map Editor", MapEditor, "map_editor_mod")
    {
    }

    CheckboxValue* enable_editor = conf(new CheckboxValue(false, "enable_editor_cb"));
    CheckboxValue* minimap_calibrate = conf(new CheckboxValue(false, "calibrate_cb"));
    ActionRowValue* export_exfil_coords = conf(new ActionRowValue([&]
    {
        minimap::editor_export_exfil_coords();
    }, "export_exfil_coords_btn"));
    CheckboxValue* show_zone_outlines = conf(new CheckboxValue(false, "show_floor_zones_cb"));
    CheckboxValue* show_abstract_overlay = conf(new CheckboxValue(false, "abstract_overlay_satellite_cb"));
    FloatSliderValue* abstract_overlay_opacity = conf(new FloatSliderValue(0.45f, 0.1f, 1.f, "abstract_overlay_opacity_itm"));
    SelectValue* preview_style = conf(new SelectValue(0, {"abstract_sel", "satellite_sel"}, "preview_style_sel"));
    MenuSectionValue* section_actions_ = conf(new MenuSectionValue("actions_sec"));
    ActionRowValue* apply_btn_ = conf(new ActionRowValue([&]
    {
        minimap::editor_apply_changes();
    }, "apply_btn"));
    ActionRowValue* reload_configs_btn_ = conf(new ActionRowValue([&]
    {
        minimap::reload_map_configs_from_disk();
    }, "reload_json_configs_btn"));
    ActionRowValue* reload_btn_ = conf(new ActionRowValue([&]
    {
        minimap::editor_reload_from_active();
    }, "reload_from_active_map_btn"));
    ActionRowValue* save_btn_ = conf(new ActionRowValue([&]
    {
        std::string path;
        minimap::editor_save_to_edit_folder(path);
    }, "save_json_maps_edit_btn"));

    void draw_overlay(ImDrawList* draw_list) override
    {
        (void)draw_list;
    }

    void application_update() override
    {
    }

    void gameworld_update(const Il2CppClass* game_world_class, Il2CppObjectInstance game_world_instance,
                          Il2CppObjectInstance main_player) override
    {
        (void)game_world_class;
        (void)game_world_instance;
        (void)main_player;
        if (game_state::is_in_raid)
            minimap::editor_tick();
    }

    void init() override
    {
    }

    void prepare_menu_draw() override
    {
        minimap::editor_tick();
        const minimap::map_editor_state& editor = minimap::editor_state();
        enable_editor->set_value(editor.enabled);
        minimap_calibrate->set_value(editor.minimap_calibrate);
        show_zone_outlines->set_value(editor.show_zone_outlines);
        show_abstract_overlay->set_value(editor.show_abstract_overlay);
        abstract_overlay_opacity->set_value(editor.abstract_overlay_opacity);
        preview_style->set(std::to_string(editor.preview_style == minimap::map_style::satellite_style ? 1 : 0));
    }

    void draw_menu_extra() override
    {
        const bool disabled = !enable_editor->get_value();
        minimap::map_editor_state& editor = minimap::editor_state();

        if (enable_editor->get_value() != editor.enabled)
        {
            minimap::editor_set_enabled(enable_editor->get_value());
            if (editor.enabled && !editor.location_id.empty())
                minimap::request_active_map(editor.location_id);
        }

        editor.preview_style = preview_style->get_value() == 1
            ? minimap::map_style::satellite_style
            : minimap::map_style::abstract_style;
        editor.minimap_calibrate = minimap_calibrate->get_value();
        editor.show_zone_outlines = show_zone_outlines->get_value();
        editor.show_abstract_overlay = show_abstract_overlay->get_value();
        editor.abstract_overlay_opacity = abstract_overlay_opacity->get_value();
        export_exfil_coords->set_disabled(false);
        show_zone_outlines->set_disabled(disabled);
        const bool satellite_preview = editor.preview_style == minimap::map_style::satellite_style;
        show_abstract_overlay->set_disabled(disabled || !satellite_preview);
        abstract_overlay_opacity->set_disabled(disabled || !satellite_preview || !show_abstract_overlay->get_value());

        const bool actions_disabled = disabled || !editor.session_ready;
        apply_btn_->set_disabled(actions_disabled);
        reload_configs_btn_->set_disabled(false);
        reload_btn_->set_disabled(disabled);
        save_btn_->set_disabled(actions_disabled);

        const float sf = menu::get_scale_factor();
        ImGui::Dummy(ImVec2(0, 4.f * sf));

        if (editor.session_ready)
        {
            ImGui::TextColored(menu::palette::accent2, "%s %s",
                i18n::tr("location_inf").c_str(), editor.location_id.c_str());
            if (editor.changes_applied)
                ImGui::TextColored(menu::palette::muted, "%s", i18n::tr("preview_applied_inf").c_str());
            else
                ImGui::TextColored(menu::palette::muted, "%s",
                    i18n::tr("preview_edit_then_apply_inf").c_str());
        }
        else
        {
            ImGui::TextColored(menu::palette::muted, "%s",
                i18n::tr("enter_raid_map_bg_inf").c_str());
        }

        ImGui::Dummy(ImVec2(0, 6.f * sf));
        ImGui::TextColored(menu::palette::muted, "%s",
            i18n::tr("reload_json_info_inf").c_str());
        ImGui::TextColored(menu::palette::muted, "%s",
            i18n::tr("enable_minimap_preview_inf").c_str());
        if (editor.preview_style == minimap::map_style::satellite_style)
        {
            ImGui::TextColored(menu::palette::muted, "%s",
                i18n::tr("abstract_overlay_info_inf").c_str());
        }

        draw_calibration_block("abstract_sel", editor.abstract, disabled);
        draw_calibration_block("satellite_sel", editor.satellite, disabled, true);
        draw_overlay_block(editor, disabled);
    }

private:
    static bool draw_cal_field(const char* label, float* value, const bool disabled)
    {
        return imgui_addons::input_float(label, value, 0.1f, disabled);
    }

    static bool draw_cal_int_field(const char* label, int* value, const bool disabled)
    {
        float as_float = static_cast<float>(*value);
        if (!imgui_addons::input_float(label, &as_float, 1.f, disabled))
            return false;
        *value = static_cast<int>(as_float);
        return true;
    }

    static void draw_calibration_block(const char* title_key, minimap::map_style_calibration& cal,
                                       const bool disabled, const bool satellite_fields = false)
    {
        const float sf = menu::get_scale_factor();
        ImGui::PushID(title_key);
        ImGui::Dummy(ImVec2(0, 8.f * sf));
        ImGui::TextColored(menu::palette::accent2, "%s", i18n::tr(title_key).c_str());

        draw_cal_field("bounds_corner_0_x_itm", &cal.bounds_corner0_x, disabled);
        draw_cal_field("bounds_corner_0_z_itm", &cal.bounds_corner0_z, disabled);
        draw_cal_field("bounds_corner_1_x_itm", &cal.bounds_corner1_x, disabled);
        draw_cal_field("bounds_corner_1_z_itm", &cal.bounds_corner1_z, disabled);

        if (satellite_fields)
        {
            draw_cal_field("transform_0_itm", &cal.transform[0], disabled);
            draw_cal_field("transform_1_itm", &cal.transform[1], disabled);
            draw_cal_field("transform_2_itm", &cal.transform[2], disabled);
            draw_cal_field("transform_3_itm", &cal.transform[3], disabled);
        }

        draw_cal_int_field("coordinate_rotation_itm", &cal.coordinate_rotation, disabled);
        draw_cal_field("rotation_deg_itm", &cal.rotation_deg, disabled);
        draw_cal_field("offset_x_itm", &cal.offset_x, disabled);
        draw_cal_field("offset_z_itm", &cal.offset_z, disabled);
        ImGui::PopID();
    }

    static void draw_overlay_block(minimap::map_editor_state& editor, const bool disabled)
    {
        if (editor.overlay_layers.empty())
            return;

        const float sf = menu::get_scale_factor();
        ImGui::PushID("overlay");
        ImGui::Dummy(ImVec2(0, 8.f * sf));
        ImGui::TextColored(menu::palette::accent2, "%s", i18n::tr("overlay_layers_inf").c_str());

        std::vector<const char*> layer_names;
        layer_names.reserve(editor.overlay_layers.size());
        for (const auto& layer : editor.overlay_layers)
            layer_names.push_back(layer.name.c_str());

        editor.selected_overlay = (std::min)(editor.selected_overlay,
            static_cast<int>(editor.overlay_layers.size()) - 1);
        editor.selected_overlay = (std::max)(editor.selected_overlay, 0);

        imgui_addons::select("layer_sel", &editor.selected_overlay, layer_names, disabled);

        auto& layer = editor.overlay_layers[static_cast<size_t>(editor.selected_overlay)];
        if (layer.extents.empty())
        {
            ImGui::PopID();
            return;
        }

        editor.selected_extent = (std::min)(editor.selected_extent, static_cast<int>(layer.extents.size()) - 1);
        editor.selected_extent = (std::max)(editor.selected_extent, 0);

        static std::vector<std::string> extent_label_storage;
        extent_label_storage.clear();
        extent_label_storage.reserve(layer.extents.size());
        for (size_t i = 0; i < layer.extents.size(); ++i)
            extent_label_storage.push_back(i18n::tr("extent_sel") + " " + std::to_string(i + 1));

        std::vector<const char*> extent_labels;
        extent_labels.reserve(extent_label_storage.size());
        for (const auto& label : extent_label_storage)
            extent_labels.push_back(label.c_str());

        imgui_addons::select("extent_sel", &editor.selected_extent, extent_labels, disabled);

        auto& extent = layer.extents[static_cast<size_t>(editor.selected_extent)];
        draw_cal_field("height_min_itm", &extent.height_min, disabled);
        draw_cal_field("height_max_itm", &extent.height_max, disabled);
        ImGui::PopID();
    }
};