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

////////////////////////////////////////////////////////////////////////////////////
//	Entity Tree Property Editor View

#include "EntityTreePropertyEditorView.h"
#include "IconsFontAwesome5.h"
#include "SchemaEdit.h"
#include "imgui.h"
#include <algorithm>

namespace GameEngine {

// Layout of the 'SceneObject' component type
struct SceneObject
{
	EntityId Self;
	EntityId Parent;
	EntityId FirstChild;
	EntityId NextSibling;
	char     Name[64];
	uint64_t Layer;
};

void EntityTreePropertyEditorView::AddShowMenuItem()
{
    ImGui::MenuItem("Show Scene Heirarchy", NULL, &m_showObjectTreeView);
    ImGui::MenuItem("Show Object Properties", NULL, &m_showPropertyEditor);
}

void EntityTreePropertyEditorView::Update()
{
    ShowEntityProperties();
    ShowObjectTree();
}

void EntityTreePropertyEditorView::Initialize()
{
}

void EntityTreePropertyEditorView::Shutdown()
{
}

void EntityTreePropertyEditorView::AddTreeNode(SceneObject* sceneObj)
{
    ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen;
    if (m_selectedEntityId == sceneObj->Self)
        node_flags |= ImGuiTreeNodeFlags_Selected;

    auto childId = sceneObj->FirstChild;
    if (childId == -1)
        node_flags |= ImGuiTreeNodeFlags_Leaf;

    bool isOpen = ImGui::TreeNodeEx((void*)(intptr_t)sceneObj->Self, node_flags, "%s", sceneObj->Name);
    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
        m_selectedEntityId = sceneObj->Self;

    if (isOpen)
    {
        while (childId != -1)
        {
            auto& entRef = m_entityComponentSystem.GetEntity(childId);
            if (entRef.components.count(m_sceneObjectId))
            {
                SceneObject* childObj = (SceneObject*)entRef.components.at(m_sceneObjectId);
                AddTreeNode(childObj);
                childId = childObj->NextSibling;
            }
            else
            {
                childId = -1;
            }

        }
        ImGui::TreePop();
    }
}

void EntityTreePropertyEditorView::ShowObjectTree()
{
    bool* p_open = &m_showObjectTreeView;
    if (!*p_open)
        return;
    if (ImGui::Begin("Scene Heirarchy", p_open /*, ImGuiWindowFlags_MenuBar */ ))
    {
        m_sceneObjectId = m_entityComponentSystem.FindComponentTypeByName("SceneObject");
        auto roots = m_entityComponentSystem.FindEntitiesWithComponentType(m_entityComponentSystem.FindComponentTypeByName("RootNode"));
        assert(roots.size() <= 1);
        if (roots.size())
        {
            auto& entRef = m_entityComponentSystem.GetEntity(roots[0]);
            if (entRef.components.count(m_sceneObjectId))
                AddTreeNode((SceneObject*)entRef.components.at(m_sceneObjectId));
        }
    }
    ImGui::End();
}

void ShowFields(GameRuntime::Schema* schema, GameRuntime::VariableDescription* vars, int count, uint8_t* entData)
{
    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))
            {
                ShowFields(schema, schema->variables->variables + var.structDescription.variableDescriptionTableOffset, var.structDescription.numberOfFields, entData);
                ImGui::TreePop();
            }
        }
        else
        {
            std::string entName = ConvertRuntimeString(schema, var.name);
            entName += "##" + std::to_string((size_t)entData);
            std::vector<std::string> enumStrings;
            std::vector<int> enumValue;
            std::vector<const char*> enumStringsCStr;
            static int current = 1;
            static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
            switch (var.variableType)
            {
                case GameRuntime::VariableTypeEnum::E_Void:
                    ImGui::Text("%s", entName.c_str());
                    break;
                case GameRuntime::VariableTypeEnum::E_Bool:
                    ImGui::Checkbox(entName.c_str(), (bool*)entData);
                    break;
                case GameRuntime::VariableTypeEnum::E_Enum:
                    for (int e = 0; e < var.enumDescription.numberOfEnumValues; e++)
                    {
                        auto& enumEnt = schema->enums->values[var.enumDescription.nameValueTableOffset + e];
                        enumStrings.push_back(ConvertRuntimeString(schema, enumEnt.name));
                        enumValue.push_back(enumEnt.value);
                        if (*(int*)entData == enumEnt.value)
                            current = e;
                    }
                    for (int e = 0; e < var.enumDescription.numberOfEnumValues; e++)
                        enumStringsCStr.push_back(enumStrings[e].c_str());
                    ImGui::Combo(entName.c_str(), &current, enumStringsCStr.data(), enumStringsCStr.size());
                    *(int*)entData = enumValue[current];
                    break;
                case GameRuntime::VariableTypeEnum::E_String:
                    ImGui::InputText(entName.c_str(), (char*)entData, var.stringDescription.maximumCharacterCount);//   &str[0], IM_ARRAYSIZE(str)); 
                    break;
                case GameRuntime::VariableTypeEnum::E_Number:
                    switch (var.numberDescription.numberType)
                    {
                        case E_UInt8:
                        case E_UInt16:
                        case E_UInt32:
                        case E_UInt64:
                        case E_Int8:
                        case E_Int16:
                        case E_Int32:
                        case E_Int64:
                            ImGui::InputInt(entName.c_str(), (int*)entData, 1.0f);
                            break;
                        case E_Float32:
                            ImGui::InputFloat(entName.c_str(), (float*)entData, 1.0f);
                            break;
                        case E_Float64:
                            ImGui::InputDouble(entName.c_str(), (double*)entData, 1.0f);
                            break;
                        case E_Angle:
                            ImGui::SliderAngle(entName.c_str(), (float*)entData, 1.0f);
                            break;
                        case E_Ratio:
                            ImGui::SliderFloat(entName.c_str(), (float*)entData, 0.0f, 1.0f, "ratio = %.3f");
                            break;
                    }
                    break;
                //case GameRuntime::E_EncodedNumber:
                //    break;
                case GameRuntime::VariableTypeEnum::E_Color:
                    col4f[0] = entData[0] / 255.0;
                    col4f[1] = entData[1] / 255.0;
                    col4f[2] = entData[2] / 255.0;
                    col4f[3] = entData[3] / 255.0;
                    if (var.colorDescription.colorType == GameRuntime::E_RGBA32)
                        ImGui::ColorEdit4(entName.c_str(), col4f);
                    else
                        ImGui::ColorEdit3(entName.c_str(), col4f);
                    entData[0] = (uint8_t)(col4f[0] * 255.0);
                    entData[1] = (uint8_t)(col4f[1] * 255.0);
                    entData[2] = (uint8_t)(col4f[2] * 255.0);
                    entData[3] = (uint8_t)(col4f[3] * 255.0);
                    break;
                case GameRuntime::VariableTypeEnum::E_Entity:
                    // TODO: combo-box selection
                    ImGui::InputInt(entName.c_str(), (int*)entData);
                    break;
                case GameRuntime::VariableTypeEnum::E_Asset:
                    ImGui::Text("asset");
                    break;
                case GameRuntime::VariableTypeEnum::E_Struct:
                    ImGui::Text("struct");
                    break;
                case GameRuntime::VariableTypeEnum::E_Component:
                    ImGui::Text("component");
                    break;
            }
        }

        entData += CalculateFieldSize(*schema, (size_t)entData, var);
    }
}

bool AddPopup(bool openCond, const char* caption, const char* message)
{
    bool okayClicked = false;
    static bool dont_ask_me_next_time = false;

    if (openCond)
    {
        if (dont_ask_me_next_time)
            return true;
        else
            ImGui::OpenPopup(caption);
    }

    // Always center this window when appearing
    ImVec2 center = ImGui::GetMainViewport()->GetCenter();
    ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));

    if (ImGui::BeginPopupModal(caption, NULL, ImGuiWindowFlags_AlwaysAutoResize))
    {
        ImGui::Text("%s", message);
        ImGui::Separator();
        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
        ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
        ImGui::PopStyleVar();
        if (ImGui::Button("OK", ImVec2(120, 0)))
        {
            okayClicked = true;
            ImGui::CloseCurrentPopup();
        }
        ImGui::SetItemDefaultFocus();
        ImGui::SameLine();
        if (ImGui::Button("Cancel", ImVec2(120, 0)))
        {
            ImGui::CloseCurrentPopup();
        }
        ImGui::EndPopup();
    }

    return okayClicked;
}

void EntityTreePropertyEditorView::ShowEntityProperties()
{
    bool* p_open = &m_showPropertyEditor;
    if (!*p_open)
        return;
    if (ImGui::Begin("Object Properties", p_open))
    {
        auto schema = m_entityComponentSystem.GetSchema();

        auto entityId = m_selectedEntityId;
        auto entName = "Entity #" + std::to_string(m_selectedEntityId) + " Properties:";

        ImGui::Separator();
        ImGui::Text("%s", entName.c_str());
        auto& entRef = m_entityComponentSystem.GetEntity(entityId);

        auto sceneObjectTypeId = m_entityComponentSystem.FindComponentTypeByName("SceneObject");
        if (entRef.components.count(sceneObjectTypeId))
        {
            ImGui::Text("Name: %s", ((SceneObject*)entRef.components.at(sceneObjectTypeId))->Name);
            ImGui::Separator();
        }

        for (auto& comp : entRef.components)
        {
            auto compId = comp.first;
            void* entData = (void*)comp.second;
            auto& compRef = m_entityComponentSystem.GetComponentType(compId);
            if (compRef.name != "SceneObject")
            {
                if (ImGui::CollapsingHeader(compRef.name.c_str(), ImGuiTreeNodeFlags_DefaultOpen))
                {
                    auto* vars = schema->variables->variables + compRef.fields.variableDescriptionTableOffset;
                    ShowFields(schema, vars, compRef.fields.numberOfFields, (uint8_t*)entData);
                }
            }
        }

        static int clicked = 0;

        if (AddPopup(ImGui::Button("Delete.."), "Delete?", "All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"))
            clicked++;

        if (ImGui::Button("Add Component"))
            clicked++;

        if (clicked & 1)
        {
            ImGui::Begin("Components", p_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();
}

} // GameEngine namespace
