#pragma once

#include "core/asset_helpers/save_mount_config.h"
#include "core/mount_config/mount_config.h"
#include "core/mount_type/mount_type.h"
#include "core/projectile_type/projectile_type.h"
#include "spdlog/spdlog.h"
#include <RmlUi/Core.h>
#include <RmlUi/Core/Elements/ElementFormControlInput.h>
#include <RmlUi/Debugger.h>
#include <RmlUi_Backend.h>
#include <unordered_map>
#include <utility>
#include <vector>

using std::string;
using std::unordered_map;
using std::vector;

using Rml::DataModelHandle;
using Rml::Event;
using Rml::VariantList;

namespace UI::MountEditor {

struct ViewModel {
    vector<string> mount_name;
    string mount_image;
    int selected_mount_idx = 0;
    int projectile_type = 0;
    vector<string> projectile_types;
    std::string cooldown = "0";
    std::string scale = "0";
    std::string ang_vel = "0";
};

class Page {
  public:
    Rml::ElementDocument *document = nullptr;

    vector<MountConfig> ordered_mount_configs;

    ViewModel vm;

    void initialize(Rml::Context *context, unordered_map<MountType, MountConfig> mount_configs) {
        // Init view model
        for (size_t i = 0; i < ordered_mount_types.size(); i++) {
            ordered_mount_configs.push_back(mount_configs[ordered_mount_types[i]]);

            vm.mount_name.push_back(ordered_mount_type_names[i]);
        }

        vm.projectile_types = ordered_projectile_type_names;

        select_mount(0);

        // Bind view model
        if (Rml::DataModelConstructor constructor = context->CreateDataModel("mount_editor")) {
            constructor.RegisterArray<vector<string>>();

            if (auto sh = constructor.RegisterStruct<ViewModel>()) {
                sh.RegisterMember("mount_name", &ViewModel::mount_name);
                sh.RegisterMember("mount_image", &ViewModel::mount_image);
                sh.RegisterMember("selected_mount_idx", &ViewModel::selected_mount_idx);
                sh.RegisterMember("projectile_type", &ViewModel::projectile_type);
                sh.RegisterMember("projectile_types", &ViewModel::projectile_types);
                sh.RegisterMember("cooldown", &ViewModel::cooldown);
                sh.RegisterMember("scale", &ViewModel::scale);
                sh.RegisterMember("ang_vel", &ViewModel::ang_vel);
            }

            constructor.BindEventCallback(
                "on_cooldown_change",
                [this](DataModelHandle handle, Event &event, const VariantList &) {
                    auto value_str = event.GetParameter<Rml::String>("value", "");
                    vm.cooldown = value_str;
                    handle.DirtyVariable("vm");
                });

            constructor.BindEventCallback(
                "on_scale_change",
                [this](DataModelHandle handle, Event &event, const VariantList &) {
                    auto value_str = event.GetParameter<Rml::String>("value", "");
                    vm.scale = value_str;
                    handle.DirtyVariable("vm");
                });

            constructor.BindEventCallback(
                "on_ang_vel_change",
                [this](DataModelHandle handle, Event &event, const VariantList &) {
                    auto value_str = event.GetParameter<Rml::String>("value", "");
                    vm.ang_vel = value_str;
                    handle.DirtyVariable("vm");
                });

            constructor.BindEventCallback(
                "on_projectile_type_change",
                [this](DataModelHandle handle, Event &event, const VariantList &) {
                    auto value_str = event.GetParameter<Rml::String>("value", "");
                    auto value = stoi(value_str);
                    vm.projectile_type = value;
                    SPDLOG_INFO("Selected projectile type: {}", value);
                    handle.DirtyVariable("vm");
                    handle.DirtyVariable("get_selected_projectile_type_display_value");
                });

            constructor.BindEventCallback(
                "on_select_mount",
                [this](DataModelHandle handle, Event &, const VariantList &args) {
                    int idx = args[0].Get<int>();
                    select_mount(idx);
                    handle.DirtyVariable("vm");
                    handle.DirtyVariable("get_selected_projectile_type_display_value");
                });

            constructor.BindEventCallback(
                "on_save_mount",
                [this](DataModelHandle handle, Event &, const VariantList &) {
                    save_mount();
                    handle.DirtyVariable("vm");
                });

            constructor.BindFunc(
                "get_selected_projectile_type_display_value",
                [this](Rml::Variant &out) {
                    auto projectile_type = static_cast<ProjectileType>(vm.projectile_type);
                    out = to_string(projectile_type);
                });

            constructor.Bind("vm", &vm);

            document = context->LoadDocument("build/assets/ui/pages/mount_editor/mount_editor.rml");

            data_model_handle = constructor.GetModelHandle();
        }
    }

    void show() {
        document->Show();
    }

    void hide() {
        document->Hide();
    }

  private:
    Rml::DataModelHandle data_model_handle;

    void select_mount(int idx) {
        auto &mount_config = ordered_mount_configs[idx];

        vm.mount_image = "../../../../" + mount_config.image_path;

        vm.selected_mount_idx = idx;

        vm.projectile_type = static_cast<int>(mount_config.projectile_type);
        vm.cooldown = std::to_string(mount_config.cooldown);
        vm.scale = std::to_string(mount_config.scale);
        vm.ang_vel = std::to_string(mount_config.rot_ang_vel);
    }

    void save_mount() {
        auto &mount_config = ordered_mount_configs[vm.selected_mount_idx];

        mount_config.projectile_type = static_cast<uint32_t>(vm.projectile_type);
        mount_config.cooldown = stof(vm.cooldown);
        mount_config.scale = stof(vm.scale);
        mount_config.rot_ang_vel = stof(vm.ang_vel);

        save_mount_config(mount_config);
    }
};

} // namespace UI::MountEditor
