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

////////////////////////////////////////////////////////////////////////////////////
//	Game Editor Window

#include "UserWidgets.h"
#include "GameEditorWindow.h"
#include "GameState.h"
#include "SchemaEdit.h"
#include "CanvasEditorView.h"
#include "NodeEditorView.h"
#include "TerminalView.h"
#include "TextEditorView.h"
#include "FlatEntityListView.h"
#include "ComponentTypeEditorView.h"
#include "EntityTreePropertyEditorView.h"
#include "BlueprintsNodeEditorView.h"
#include "ImageView.h"
#include "RenderView.h"
#include "FileBrowser.h"
#include "IUiSystem.h"
#include "imgui_memory_editor.h"
#include <algorithm>

namespace GameEngine {

GameEditorWindow::GameEditorWindow(ApplicationFramework::IApplication& app, int width, int height, const char* title)
    : MainWindow(app, width, height, title)
    , m_application(app)
{
    m_views.AddItem(std::make_unique<CanvasEditorView>());
    // m_views.AddItem(std::make_unique<NodeEditorView>());
    // m_views.AddItem(std::make_unique<BlueprintsNodeEditorView>());
    m_views.AddItem(std::make_unique<FlatEntityListView>(m_entityComponentSystem));
    m_views.AddItem(std::make_unique<EntityTreePropertyEditorView>(m_entityComponentSystem));
    m_views.AddItem(std::make_unique<ComponentTypeEditorView>(app, m_entityComponentSystem));
    m_views.AddItem(std::make_unique<TextEditorView>(app));
    m_views.AddItem(std::make_unique<TerminalView>(app));
    m_views.AddItem(std::make_unique<ImageView>(app));
    m_views.AddItem(std::make_unique<RenderView>(app));
    m_views.AddItem(std::make_unique<FileBrowser>(app));
}

// virtual
GameEditorWindow::~GameEditorWindow()
{
}

// virtual
void GameEditorWindow::Initialize()
{
    ParentClass::Initialize();
    m_pluginManager.Initialize();
    m_entityComponentSystem.Initialize();
    m_views.Initialize();
}

// virtual
void GameEditorWindow::Shutdown()
{
    m_views.Shutdown();
    m_entityComponentSystem.Shutdown();
    m_pluginManager.Shutdown();
    ParentClass::Shutdown();
}

// virtual
void GameEditorWindow::Update()
{
    ParentClass::Update();
    m_pluginManager.Update();
    m_views.Update();

    ShowObjectTemplateEditor();
    ShowUserWidgetsTest();
    ShowMemoryEditor();
}

// virtual
void GameEditorWindow::AddMenus()
{
    ParentClass::AddMenus();

    bool newEntity = false;
    if (ImGui::BeginMenu("Entities"))
    {
        ImGui::MenuItem("Add New Entity", NULL, &newEntity);
        ImGui::EndMenu();
    }
    bool newComponent = false;
    if (ImGui::BeginMenu("Components"))
    {
        ImGui::MenuItem("Add New Component", NULL, &newComponent);
        ImGui::EndMenu();
    }
    if (ImGui::BeginMenu("View"))
    {
        m_views.AddShowMenuItem();
        ImGui::MenuItem("Show Object Template Editor", NULL, &showObjectTemplateEditor);
        ImGui::MenuItem("Show Memory Editor", NULL, &showMemoryEditor);
        ImGui::MenuItem("Show User Widgets Test", NULL, &showUserWidgetsTest);
        ImGui::EndMenu();
    }
}

void GameEditorWindow::ShowObjectTemplateEditor()
{
    bool* p_open = &showObjectTemplateEditor;
    if (!*p_open)
        return;
    if (ImGui::Begin("Object Template Editor", p_open))
    {
        ImGui::Text("Object Template:");
        //m_nodeEditor.Update();
    }
    ImGui::End();
}

void GameEditorWindow::ShowMemoryEditor()
{
    bool* p_open = &showMemoryEditor;
    if (!*p_open)
        return;

    ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[m_application.UiSystem()->FixedFontIndex()]);
    if (ImGui::Begin("Memory Editor", p_open))
    {
        static MemoryEditor mem_edit_2;
        mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
    }
    ImGui::End();
    ImGui::PopFont();
}

// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this.
//
// Usage:
//   // Create a window and draw memory editor inside it:
//   static MemoryEditor mem_edit_1;
//   static char data[0x10000];
//   size_t data_size = 0x10000;
//   mem_edit_1.DrawWindow("Memory Editor", data, data_size);
//
// Usage:
//   // If you already have a window, use DrawContents() instead:
//   static MemoryEditor mem_edit_2;
//   ImGui::Begin("MyWindow")
//   mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
//   ImGui::End();

void GameEditorWindow::ShowUserWidgetsTest()
{
    static bool toggle = false;
    static int ival = 0;
    static float fval = 0;
    static double dval = 0;
    static float curveValues[100] = { 10, 20, 30 };
    static int curveValueCount = 3;

    if (!showUserWidgetsTest)
        return;
    if (ImGui::Begin("User Widgets", &showUserWidgetsTest))
    {
        // Various widgets
        ImGui::ToggleButton("toggle test", &toggle);
        ImGui::SpinInt("int", &ival);
        ImGui::SpinFloat("float", &fval);
        ImGui::SpinDouble("double", &dval);

        // Curve types as beziers
        const char* curveTypes[] = {
            "easeLinear",     "easeInSine",     "easeOutSine",   "easeInOutSine",
            "easeInQuad",     "easeOutQuad",    "easeInOutQuad", "easeInCubic",
            "easeOutCubic",   "easeInOutCubic", "easeInQuart",   "easeOutQuart",
            "easeInOutQuart", "easeInQuint",    "easeOutQuint",  "easeInOutQuint",
            "easeInExpo",     "easeOutExpo",    "easeInOutExpo", "easeInCirc",
            "easeOutCirc",    "easeInOutCirc",  "easeInBack",    "easeOutBack",    
            "easeInOutBack"
        };
        float curveData[][4] = {
            { 0.000f, 0.000f, 1.000f, 1.000f }, { 0.470f, 0.000f, 0.745f, 0.715f }, { 0.390f, 0.575f, 0.565f, 1.000f }, { 0.445f, 0.050f, 0.550f, 0.950f },
            { 0.550f, 0.085f, 0.680f, 0.530f }, { 0.250f, 0.460f, 0.450f, 0.940f }, { 0.455f, 0.030f, 0.515f, 0.955f }, { 0.550f, 0.055f, 0.675f, 0.190f },
            { 0.215f, 0.610f, 0.355f, 1.000f }, { 0.645f, 0.045f, 0.355f, 1.000f }, { 0.895f, 0.030f, 0.685f, 0.220f }, { 0.165f, 0.840f, 0.440f, 1.000f },
            { 0.770f, 0.000f, 0.175f, 1.000f }, { 0.755f, 0.050f, 0.855f, 0.060f }, { 0.230f, 1.000f, 0.320f, 1.000f }, { 0.860f, 0.000f, 0.070f, 1.000f },
            { 0.950f, 0.050f, 0.795f, 0.035f }, { 0.190f, 1.000f, 0.220f, 1.000f }, { 1.000f, 0.000f, 0.000f, 1.000f }, { 0.600f, 0.040f, 0.980f, 0.335f },
            { 0.075f, 0.820f, 0.165f, 1.000f }, { 0.785f, 0.135f, 0.150f, 0.860f }, { 0.600f, -0.28f, 0.735f, 0.045f }, { 0.175f, 0.885f, 0.320f, 1.275f },
            { 0.680f, -0.55f, 0.265f, 1.550f },
        };
        ImGui::PushItemWidth(100);
        static int curveType = 0;
        int lastCurveType = curveType;
        ImGui::Combo("curve type", &curveType, curveTypes, IM_ARRAYSIZE(curveTypes));
        static float curve[4] = { 0.000f, 0.000f, 1.000f, 1.000f };
        if (lastCurveType != curveType)
            memcpy(curve, curveData[curveType], sizeof(curve));
        ImGui::Bezier(curveTypes[curveType], curve);

        // Curve editor
        static ImVec2 points[12] = { { ImGui::CurveTerminator, 0 } };
        static int pointIdx = -1;
        ImGui::Curve("curve", ImVec2(500, 150), std::size(points), points, &pointIdx);
        //float ImGui::CurveValue(float p, int maxpoints, const ImVec2* points);
        //float ImGui::CurveValueSmooth(float p, int maxpoints, const ImVec2* points);
    }
    ImGui::End();
}

} // GameEngine namespace
