#include "DeerStudio/EditorEngine/DockPanelObject.h"
#include "DeerStudio/EditorEngine/ErrorHandle.h"
#include "DeerStudio/EditorEngine.h"

#include "Deer/Log.h"

#include "angelscript.h"
#include <string>

#include "imgui.h"

namespace Deer {
    EditorEngine::DockPanelObject::DockPanelObject(asITypeInfo* _type)
        : type (_type), isValid(false) {
        
        // Constructor
        // "type@ type()" 
        std::string callString(type->GetName());
        callString += " @";
        callString += type->GetName();
        callString += "()";

        asIScriptFunction* factory = type->GetFactoryByDecl(callString.c_str());
        
        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->Prepare(factory),
            type->GetName()
        );
        
        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->Execute(),
            type->GetName()
        );

        // Return value contains the ref to a asIScriptObject in the location provided
        object = *(asIScriptObject**)scriptContext->GetAddressOfReturnValue();
        if (!object){
            DEER_UI_ENGINE_ERROR("Could not create object", _type->GetName());
            return;
        }
        object->AddRef();
        
        renderFunction = type->GetMethodByDecl("void onRender()");
        if (!renderFunction) {
            DEER_UI_ENGINE_ERROR("Could not load void onRender() from {0}", _type->GetName());
            return;
        }

        menuBarFunction = type->GetMethodByDecl("void onMenuBar()");
        initFunction = type->GetMethodByDecl("void onInit()");

        isValid = true;
    }

    EditorEngine::DockPanelObject::~DockPanelObject() {
        if (object)
            object->Release();
    }

    void EditorEngine::DockPanelObject::invalidate() {
        DEER_UI_ENGINE_ERROR("Last error was caused executing {0}", type->GetName());
        isValid = false;
    }

    void EditorEngine::DockPanelObject::init() {  
        if (!initFunction)
            return;
        
        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->Prepare(initFunction),
            type->GetName()
        );

        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->SetObject(object),
            type->GetName()
        );

        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->Execute(),
            type->GetName()
        );
    }

    void EditorEngine::DockPanelObject::executeRender() {
        // We cache the result because the user can remove the flag while executing
        bool hasPadding = flags & DockPannelFlag_PannelPadding;
        if (hasPadding)
            ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));

        if (menuBarFunction) {
            ImGui::Begin(type->GetName(), (bool*)0, ImGuiWindowFlags_MenuBar);

		    if (ImGui::BeginMenuBar()) {    
                AS_CHECK_ADDITIONAL_INFO(
                    scriptContext->Prepare(menuBarFunction),
                    type->GetName()
                );

                AS_CHECK_ADDITIONAL_INFO(
                    scriptContext->SetObject(object),
                    type->GetName()
                );

                AS_CHECK_ADDITIONAL_INFO(
                    scriptContext->Execute(),
                    type->GetName()
                );

                ImGui::EndMenuBar();
            } 
        } else {
            ImGui::Begin(type->GetName());
        }

        // This is to make sure that right click activates the window
        if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) &&
            (ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle))) {
            ImGui::SetWindowFocus();
        }

        if (!isValid) {
            ImGui::TextColored(ImVec4(1, 0.3f, 0.3f, 1), "There was a runtime error");
            ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "Please check the log");
            ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1), "Plese restart the interface");
            ImGui::End();
            return;
        }
        
        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->Prepare(renderFunction),
            type->GetName()
        );

        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->SetObject(object),
            type->GetName()
        );

        AS_CHECK_ADDITIONAL_INFO(
            scriptContext->Execute(),
            type->GetName()
        );

        ImGui::End();

        if (hasPadding)
            ImGui::PopStyleVar();
    }

    EditorEngine::DockPanelObject::DockPanelObject(DockPanelObject&& other) noexcept 
        : isValid(other.isValid), renderFunction(other.renderFunction), type(other.type), object(other.object), menuBarFunction(other.menuBarFunction), initFunction(other.initFunction) {
        
        other.isValid = false;
        other.renderFunction = nullptr;
        other.type = nullptr;
        other.object = nullptr;
        other.menuBarFunction = nullptr;
        other.initFunction = nullptr;   
    }

    EditorEngine::DockPanelObject& EditorEngine::DockPanelObject::operator=(EditorEngine::DockPanelObject&& other) noexcept {
        if (this != &other) {
            isValid = other.isValid;
            renderFunction = other.renderFunction;
            type = other.type;
            object = other.object;
            menuBarFunction = other.menuBarFunction;
            initFunction = other.initFunction;

            other.isValid = false;
            other.renderFunction = nullptr;
            other.type = nullptr;
            other.object = nullptr;
            other.menuBarFunction = nullptr;
            other.initFunction = nullptr;
        }

        return *this;
    }

}