#include "editor.h"

#include "engine.h"
#include "tools/scenepicker.h"
#include "scene/scenetree.h"
#include "tools/resourcepack.h"
#include "editor/objecteditor.h"
#include "sparrowshell/sparrowshell.h"

#include <imgui/imgui.h>

#include <SparrowRenderer/mesh.h>
#include <SparrowRenderer/pbrmaterial.h>
#include <SparrowRenderer/deferredpipeline.h>
#include <SparrowRenderer/trackballcamera.h>

#include <SparrowInput/input.h>

#include <glm/ext.hpp>

Editor::Editor() :
    m_pickerNode(new ScenePicker()),
    m_selectedMesh(nullptr),
    m_editedResourcePack(nullptr),
    m_objectEditor(nullptr),
    m_materialEditorEnabled(false),
    m_editorEnabled(false)
{
    m_objectEditor = new ObjectEditor();
    addChild(m_pickerNode);
    m_pickerNode->setEnabled(false);
    m_pickerNode->setVisible(false);
}

Editor::~Editor()
{
    delete m_pickerNode;
}

void Editor::update()
{
    SceneNode::update();

    if(m_editorEnabled)
        gui();

    if(m_materialEditorEnabled)
        materialGui();

    if(m_editedResourcePack != nullptr)
        m_editedResourcePack->gui();
}

void Editor::gui()
{
    if(ImGui::BeginMainMenuBar())
    {
        if(ImGui::BeginMenu("File"))
        {
            ImGui::MenuItem("Load scene");
            ImGui::EndMenu();
        }
        if(ImGui::BeginMenu("Tools"))
        {
            ImGui::MenuItem("Material editor", NULL, &m_materialEditorEnabled);

            DeferredPipeline* pipeline = dynamic_cast<DeferredPipeline*>(getScene()->getPipeline());
            bool pipeline_gui_status = pipeline->isDebugGuiVisible();
            if(ImGui::MenuItem("Rendering pipeline debug gui", NULL, &pipeline_gui_status))
                pipeline->toggleDebugGui();

            bool resroucePackEditorStatus = m_editedResourcePack;
            if(ImGui::MenuItem("Resource pack editor editor", NULL, &resroucePackEditorStatus))
                toggleResourcePackGui();

            bool pickerStatus = m_pickerNode->isEnabled();
            if(ImGui::MenuItem("Picker", NULL, &pickerStatus))
                togglePicker();

            ImGui::EndMenu();
        }
        ImGui::EndMainMenuBar();
    }
}

void Editor::materialGui()
{
    static int selectedOption = 0;

    ImGui::Begin("Material Editor", &m_materialEditorEnabled);
    std::string options;
    std::vector<Mesh*> meshes;
    options += "None\n";
    meshes.push_back(nullptr);
    int i = 1;
    for(SceneIterator<GeometryNode*>* geometryIt = getScene()->getGeometry(); geometryIt->isValid(); geometryIt->next())
    {
        options += geometryIt->getItem()->mesh->getName();
        options += '\n';
        meshes.push_back(geometryIt->getItem()->mesh);
        if(geometryIt->getItem()->mesh == m_selectedMesh)
            selectedOption = i;
    }

    for(char &c : options)
    {
        if(c == '\n')
            c = '\0';
    }
    if(ImGui::Combo("Mesh", &selectedOption, options.c_str()))
    {
        m_selectedMesh = meshes[selectedOption];
    }

    if(m_selectedMesh == nullptr)
        ImGui::Text("No mesh selected");
    else
    {
        PBRMaterial* mat = dynamic_cast<PBRMaterial*>(m_selectedMesh->getMaterial());
        ImGui::ColorEdit3("albedo", &mat->albedo.r);
        ImGui::ColorEdit3("emission", &mat->emission.r);
        ImGui::SliderFloat("metallic", &mat->metallic, 0, 1);
        ImGui::SliderFloat("roughness", &mat->roughness, 0, 1);
    }

    ImGui::End();
}

void Editor::toggleEditor()
{
    static Camera* backupCamera = nullptr;
    static bool backupMouseGrabbedState = false;
    static std::string backupInputContext = "";

    m_editorEnabled = !m_editorEnabled;
    if(m_editorEnabled)
    {
        // input context
        if(getEngine().getShell()->isEnabled())
            getEngine().getShell()->toggleShell();
        backupInputContext = getEngine().getInput()->getCurrentContext();
        getEngine().getInput()->setCurrentContext("editor");

        // mouse grab
        backupMouseGrabbedState = getEngine().isMouseGrabbed();
        if(getEngine().isMouseGrabbed())
            getEngine().toggleMouseVisibility();

        // camera controller
        backupCamera = getEngine().getScene()->getCamera();
        getEngine().getScene()->setMainCamera(m_objectEditor->getCamera());
        getEngine().getScene()->getRootObject()->addChild(m_objectEditor);
    }
    else
    {
        // restore all previous states
        if(getEngine().isMouseGrabbed() != backupMouseGrabbedState)
            getEngine().toggleMouseVisibility();
        getEngine().getScene()->setMainCamera(backupCamera);
        getEngine().getScene()->getRootObject()->removeChild(m_objectEditor);
        if(getEngine().getShell()->isEnabled())
            getEngine().getShell()->toggleShell();
        getEngine().getInput()->setCurrentContext(backupInputContext);
    }
}

void Editor::togglePicker()
{
    bool pickerEnabled = m_pickerNode->isEnabled();
    m_pickerNode->setEnabled(!pickerEnabled);
    m_pickerNode->setVisible(!pickerEnabled);
}

void Editor::toggleMaterialEditor()
{
    m_materialEditorEnabled = !m_materialEditorEnabled;
}

void Editor::toggleRenderingPipelineGui()
{
    DeferredPipeline* pipeline = dynamic_cast<DeferredPipeline*>(getScene()->getPipeline());
    pipeline->toggleDebugGui();
}

void Editor::toggleResourcePackGui()
{
    if(m_editedResourcePack == nullptr)
        m_editedResourcePack = new ResourcePack();
    else
    {
        delete m_editedResourcePack;
        m_editedResourcePack = nullptr;
    }
}
