/*
	GameEngine and Editor
	by John Ryland
	Copyright (c) 2023
*/

////////////////////////////////////////////////////////////////////////////////////
//	Component Type Editor View

#include "ComponentTypeEditorView.h"
#include "IconsFontAwesome5.h"
#include "IUiSystem.h"
#include "SchemaEdit.h"
#include "imgui.h"
#include <algorithm>

namespace GameEngine {

ComponentTypeEditorView::ComponentTypeEditorView(const ApplicationFramework::IApplication& app,
                                                 const EcsSystem& entityComponentSystem)
    : m_application(app)
    , m_entityComponentSystem(entityComponentSystem)
{
}

void ComponentTypeEditorView::AddShowMenuItem()
{
    ImGui::MenuItem("Show Component Type Editor", NULL, &m_open);
}

static
void ShowFieldTypes(GameRuntime::Schema* schema, GameRuntime::VariableDescription* vars, int count, int iconFontIndex)
{
    for (int i = 0; i < count; ++i)
    {
        auto& var = vars[i];

        if (var.variableType == GameRuntime::VariableTypeEnum::E_Struct)
        {
            if (ImGui::TreeNodeEx(ConvertRuntimeString(schema, var.name).c_str(), ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanFullWidth))
            {
                ShowFieldTypes(schema, schema->variables->variables + var.structDescription.variableDescriptionTableOffset, var.structDescription.numberOfFields, iconFontIndex);
                ImGui::TreePop();
            }
        }
        else
        {
            std::string entName = ConvertRuntimeString(schema, var.name);
            entName += "##" + std::to_string((size_t)&var);

            const char* varTypes[] = {
                "E_Void", "E_Bool", "E_Enum", "E_String", "E_Number", "E_EncodedNumber", "E_Color", "E_Entity", "E_Asset" // , "E_Struct", "E_Component"
            };

            //ImGui::NewLine();
            ImGui::PushItemWidth(100);

    //IMGUI_API bool          BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0);
    //IMGUI_API void          EndCombo(); // only call EndCombo() if BeginCombo() returns true!            
    //IMGUI_API bool          Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height
    //IMGUI_API bool          Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0));      // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
            
            //ImGui::InputText(entName.c_str());

            ImGui::Combo(entName.c_str(), (int*)&var.variableType, varTypes, IM_ARRAYSIZE(varTypes));

            ImGui::SameLine(200);

            const char* strLens[] = {  "32", "64", "128", "256", "512" };
            int strLensCount[] = {  32, 64, 128, 256, 512 };
            //int tmpMaxCharacterCount = 0;
            //while (var.stringDescription.maximumCharacterCount < )

            const char* numTypes[] = {
                "E_UInt8", "E_UInt16", "E_UInt32", "E_UInt64", "E_Int8", "E_Int16", "E_Int32", "E_Int64", "E_Float32", "E_Float64", "E_Angle", "E_Ratio"
            };
            const char* colorTypes[] = {
                "E_RGB32", "E_RGBA32"
            };

            switch (var.variableType)
            {
                case GameRuntime::VariableTypeEnum::E_Enum:
                    // Enter list of name values
                    break;
                case GameRuntime::VariableTypeEnum::E_String:
                    ImGui::Combo("String length", (int*)&var.stringDescription.maximumCharacterCount, strLens, IM_ARRAYSIZE(strLens));
                    break;
                case GameRuntime::VariableTypeEnum::E_Number:
                    ImGui::Combo("Number type", (int*)&var.numberDescription.numberType, numTypes, IM_ARRAYSIZE(numTypes));
                    break;
                //case GameRuntime::E_EncodedNumber:
                //    break;
                case GameRuntime::VariableTypeEnum::E_Color:
                    ImGui::Combo("Color type", (int*)&var.colorDescription.colorType, colorTypes, IM_ARRAYSIZE(colorTypes));
                    break;
                default:
                    break;
            }

            ImGui::PushItemWidth(10);
//            ImGui::Button(("X##" + std::to_string((size_t)&var)).c_str());

            // TODO : still need to work on these:

            ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[iconFontIndex]);
            ImGui::SameLine(360);
            ImGui::Button((ICON_FA_CARET_DOWN "##" + entName).c_str());
            ImGui::SameLine(375);
            ImGui::Button((ICON_FA_CARET_UP "##" + entName).c_str());
            ImGui::SameLine(390);
            ImGui::Button((ICON_FA_GRIP_VERTICAL "##" + entName).c_str());
            ImGui::SameLine(405);
            ImGui::Button((/*ICON_FA_CLOSE*/ "X" "##" + entName).c_str());
            ImGui::PopFont();
            //ImGui::Selectable(std::to_string((size_t)&var).c_str());

            if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
            {
                int n_next = i + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);
                if (n_next >= 0 && n_next < count)
                {
                    auto varCopy = var;
                    vars[i] = vars[n_next];
                    vars[n_next] = varCopy;
                    ImGui::ResetMouseDragDelta();
                }
            }

            ImGui::PopItemWidth();

            ImGui::PopItemWidth();

            //ImGui::NewLine();
            //ImGui::Spacing();
       }
    }
}

void ComponentTypeEditorView::Update()
{
    if (!m_open)
        return;
    if (ImGui::Begin("Component Editor", &m_open))
    {
        StringList compTypes = m_entityComponentSystem.AllComponentTypeNames();
        compTypes.insert(compTypes.begin(), "*New*");
        ImGui::Text("Component:");
        ImGui::Separator();

        static int item_current_idx = 0; // Here we store our selection data as an index.
        if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN,  10.7 * ImGui::GetTextLineHeightWithSpacing())))
        {
            for (int n = 0; n < compTypes.size(); n++)
            {
                const bool is_selected = (item_current_idx == n);
                if (ImGui::Selectable(compTypes[n].c_str(), is_selected))
                    item_current_idx = n;

                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
                if (is_selected)
                    ImGui::SetItemDefaultFocus();
            }
            ImGui::EndListBox();
        }

        ImGui::Separator();
        ImGui::Text("Layout:");

        std::string selectedComponent = compTypes[item_current_idx];
        auto componentTypeId = m_entityComponentSystem.FindComponentTypeByName(selectedComponent);
        ImGui::Text("Selected Index: %i", item_current_idx);
        ImGui::Text("Selected Component: %s", selectedComponent.c_str());
        ImGui::Text("Selected ComponentId: %lu", componentTypeId);

        if (componentTypeId != -1ULL)
        {
            auto schema = m_entityComponentSystem.GetSchema();
            auto& compRef = m_entityComponentSystem.GetComponentType(componentTypeId);
            //if (ImGui::CollapsingHeader(compRef.name.c_str(), ImGuiTreeNodeFlags_DefaultOpen))
            {
                auto* vars = schema->variables->variables + compRef.fields.variableDescriptionTableOffset;
                ShowFieldTypes(schema, vars, compRef.fields.numberOfFields, m_application.UiSystem()->IconFontIndex());
            }
        }

        ImGui::Separator();

        static int clicked = 0;
        if (ImGui::Button("Add Field"))
            clicked++;

        if (clicked & 1)
        {
            ImGui::Begin("Fields", &m_open);

            StringList compTypes = m_entityComponentSystem.AllComponentTypeNames();
            std::vector<const char*> compTypesCStr;
            for (auto& ct : compTypes)
                compTypesCStr.emplace_back(ct.c_str());

            static int item_current = 0;
            ImGui::SameLine();
            if (ImGui::ListBox(" ", &item_current, compTypesCStr.data(), compTypesCStr.size(), 10))
            {
                clicked++;
                std::string compTypeSelected = compTypes[item_current];
                auto compTypeId = m_entityComponentSystem.FindComponentTypeByName(compTypeSelected);
                printf(" component selected: %s  id: %lu\n", compTypeSelected.c_str(), compTypeId);
            }

            ImGui::End();
        }

    }
    ImGui::End();
}

void ComponentTypeEditorView::Initialize()
{
}

void ComponentTypeEditorView::Shutdown()
{
}

void ComponentTypeEditorView::Show()
{
}

} // GameEngine namespace
