#pragma once

#include "client/window/window.h"
#include "components/button/button.h"
#include "components/field/field.h"
#include "components/field_label/field_label.h"
#include "components/field_select/field_select.h"
#include "components/field_select_button/field_select_button.h"
#include "components/field_select_option/field_select_option.h"
#include "components/field_select_options/field_select_options.h"
#include "components/field_text/field_text.h"
#include "core/mount_config/mount_config.h"
#include "core/mount_type/mount_type.h"
#include "core/projectile_config/projectile_config.h"
#include "core/projectile_type/projectile_type.h"
#include "core/ship_config/ship_config.h"
#include "core/ship_type/ship_type.h"
#include "pages/admin_menu/admin_menu.h"
#include "pages/buy_loadout/buy_loadout.h"
#include "pages/edit_loadout/edit_loadout.h"
#include "pages/loading_match/loading_match.h"
#include "pages/login/login.h"
#include "pages/match_finder/match_finder.h"
#include "pages/mount_editor/mount_editor.h"
#include "pages/projectile_editor/projectile_editor.h"
#include "pages/server_browser/server_browser.h"
#include "pages/server_lobby/server_lobby.h"
#include "pages/ship_editor/ship_editor.h"
#include "pages/view_loadouts/view_loadouts.h"
#include "spdlog/spdlog.h"
#include <RmlUi/Core.h>
#include <RmlUi/Core/Elements/ElementFormControlInput.h>
#include <RmlUi/Debugger.h>
#include <RmlUi_Platform_SDL.h>
#include <RmlUi_Renderer_SDL.h>

enum class RmlView {
    none,
    login,
    buy_loadout,
    view_loadouts,
    edit_loadout,
    match_finder,
    loading_match,
    ship_editor,
    mount_editor,
    projectile_editor,
    server_browser,
    server_lobby,
    admin_menu
};

class RmlManager {
  public:
    Rml::Context *context = nullptr;

    UI::Login::Page login;
    UI::BuyLoadout::Page buy_loadout;
    UI::ViewLoadouts::Page view_loadouts;
    UI::EditLoadout::Page edit_loadout;
    UI::MatchFinder::Page match_finder;
    UI::LoadingMatch::Page loading_match;
    UI::AdminMenu::Page admin_menu;
    UI::ShipEditor::Page ship_editor;
    UI::MountEditor::Page mount_editor;
    UI::ProjectileEditor::Page projectile_editor;
    UI::ServerBrowser::Page server_browser;
    UI::ServerLobby::Page server_lobby;

    SystemInterface_SDL *system_interface = nullptr;
    RenderInterface_SDL *render_interface = nullptr;

    RmlManager(SDL_Window *window, SDL_Renderer *renderer, unordered_map<ShipType, ShipConfig> &ship_configs, unordered_map<MountType, MountConfig> &mount_configs, unordered_map<ProjectileType, ProjectileConfig> &projectile_configs) {
        system_interface = new SystemInterface_SDL(window);
        render_interface = new RenderInterface_SDL(renderer);

        Rml::SetSystemInterface(system_interface);
        Rml::SetRenderInterface(render_interface);

        Rml::Initialise();

        auto [window_w, window_h] = get_screen_size(window);

        SPDLOG_INFO("Initializing Rml context w, h: {} {}", window_w, window_h);

        context = Rml::CreateContext("main", Rml::Vector2i(window_w, window_h));

        Rml::LoadFontFace("build/assets/fonts/LatoLatin-Regular.ttf");
        Rml::LoadFontFace("build/assets/fonts/NotoEmoji-Regular.ttf", true);

        // Register components
        static Rml::ElementInstancerGeneric<UI::Button::Component> button_instancer;
        Rml::Factory::RegisterElementInstancer("button", &button_instancer);

        static Rml::ElementInstancerGeneric<UI::Field::Component> field_instancer;
        Rml::Factory::RegisterElementInstancer("field", &field_instancer);

        static Rml::ElementInstancerGeneric<UI::FieldLabel::Component> field_label_instancer;
        Rml::Factory::RegisterElementInstancer("field-label", &field_label_instancer);

        static Rml::ElementInstancerGeneric<UI::FieldText::Component> field_text_instancer;
        Rml::Factory::RegisterElementInstancer("field-text", &field_text_instancer);

        static Rml::ElementInstancerGeneric<UI::FieldSelect::Component> field_select_instancer;
        Rml::Factory::RegisterElementInstancer("field-select", &field_select_instancer);

        static Rml::ElementInstancerGeneric<UI::FieldSelectOption::Component> field_select_option_instancer;
        Rml::Factory::RegisterElementInstancer("field-select-option", &field_select_option_instancer);

        static Rml::ElementInstancerGeneric<UI::FieldSelectButton::Component> field_select_button_instancer;
        Rml::Factory::RegisterElementInstancer("field-select-button", &field_select_button_instancer);

        static Rml::ElementInstancerGeneric<UI::FieldSelectOptions::Component> field_select_options_instancer;
        Rml::Factory::RegisterElementInstancer("field-select-options", &field_select_options_instancer);

        // Register documents
        login.initialize(context);
        buy_loadout.initialize(context, ship_configs);
        view_loadouts.initialize(context, ship_configs);
        edit_loadout.initialize(context);
        match_finder.initialize(context);
        loading_match.initialize(context);
        ship_editor.initialize(context, ship_configs);
        mount_editor.initialize(context, mount_configs);
        projectile_editor.initialize(context, projectile_configs);
        server_browser.initialize(context);
        server_lobby.initialize(context);
        admin_menu.initialize(context);

        if (!Rml::Debugger::Initialise(context)) {
            SPDLOG_WARN("Failed to initialize Rml::Debugger");
        }
    }

    // TODO: Call from wherever you SDL_PollEvent in tick().
    // Returns false if RmlUi consumed the event.
    bool handle_event(SDL_Window *window, SDL_Event &ev) {
        if (!context) {
            return true;
        }

        return RmlSDL::InputEventHandler(context, window, ev);
    }

    void render() {
        // Update the context to reflect any changes resulting from input events, animations,
        // modified and added elements, or changed data in data bindings.
        context->Update();

        // Prepare the application for rendering, such as by clearing the window. This calls
        // into the RmlUi backend interface, replace with your own procedures as appropriate.
        // render_interface->BeginFrame();

        // Render the user interface. All geometry and other rendering commands are now
        // submitted through the render interface.
        context->Render();

        // Present the rendered content, such as by swapping the swapchain. This calls into
        // the RmlUi backend interface, replace with your own procedures as appropriate.
        // render_interface->EndFrame();
    }

    void shutdown() {
        Rml::Shutdown();
    }

    void show_view(RmlView rml_view) {
        switch (rml_view) {
        case RmlView::login:
            login.show();
            break;
        case RmlView::buy_loadout:
            buy_loadout.show();
            break;
        case RmlView::view_loadouts:
            view_loadouts.show();
            break;
        case RmlView::edit_loadout:
            edit_loadout.show();
            break;
        case RmlView::match_finder:
            match_finder.show();
            break;
        case RmlView::loading_match:
            loading_match.show();
            break;
        case RmlView::ship_editor:
            ship_editor.show();
            break;
        case RmlView::mount_editor:
            mount_editor.show();
            break;
        case RmlView::projectile_editor:
            projectile_editor.show();
            break;
        case RmlView::server_browser:
            server_browser.show();
            break;
        case RmlView::server_lobby:
            server_lobby.show();
            break;
        case RmlView::admin_menu:
            admin_menu.show();
            break;
        default:
            break;
        }
    }

    void hide_view(RmlView rml_view) {
        switch (rml_view) {
        case RmlView::login:
            login.hide();
            break;
        case RmlView::buy_loadout:
            buy_loadout.hide();
            break;
        case RmlView::view_loadouts:
            view_loadouts.hide();
            break;
        case RmlView::edit_loadout:
            edit_loadout.hide();
            break;
        case RmlView::match_finder:
            match_finder.hide();
            break;
        case RmlView::loading_match:
            loading_match.hide();
            break;
        case RmlView::ship_editor:
            ship_editor.hide();
            break;
        case RmlView::mount_editor:
            mount_editor.hide();
            break;
        case RmlView::projectile_editor:
            projectile_editor.hide();
            break;
        case RmlView::server_browser:
            server_browser.hide();
            break;
        case RmlView::server_lobby:
            server_lobby.hide();
            break;
        case RmlView::admin_menu:
            admin_menu.hide();
            break;
        default:
            break;
        }
    }

    vector<UiEvent> drain_ui_events() {
        vector<UiEvent> ui_events;

        ui_events.append_range(admin_menu.drain_ui_events());

        return ui_events;
    }
};
