#include <pch.hpp>

#include <core.hpp>
#include <events/application_event.hpp>
#include <imgui/imgui_layer.hpp>
#include <core/time.hpp>
#include <renderer/renderer.hpp>
#include <application.hpp>
#include <input/input_system.hpp>

#include <imgui.h>

namespace OpenEngine {

    Application::Application()
    {
        OE_CORE_ASSERT(!instance, "Application already exists!");
        instance = this;

        window = std::unique_ptr<Window>(Window::Create());
        window->SetEventCallback(BIND_EVENT_FN(Application::OnEvent));

        Renderer::Init();

        imgui_layer = new ImGuiLayer();
        PushOverlay(imgui_layer);
    }

    void Application::Run()
    {
        while(running) {
            Time::Get().Update();

            for (Layer* layer : layer_stack)
                layer->OnUpdate();

            imgui_layer->Begin();

            for (Layer* layer : layer_stack)
                layer->OnImGuiRender();

            imgui_layer->End();

            window->OnUpdate();
        }
    }

    void Application::OnEvent(Event& event)
    {
        EventDispatcher dispatcher(event);
        dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(Application::OnWindowClose));
        dispatcher.Dispatch<WindowResizeEvent>(BIND_EVENT_FN(Application::OnWindowResize));

        for (auto it = layer_stack.end(); it != layer_stack.begin();) {
            (*--it)->OnEvent(event);
            if (event.handled)
                break;
        }
    }

    bool Application::OnWindowClose(WindowCloseEvent& event)
    {
        running = false;

        return true;
    }

    bool Application::OnWindowResize(WindowResizeEvent& event)
    {
        Renderer::OnWindowResize(event.GetWidth(), event.GetHeight());

        return false;
    }

    void Application::PushLayer(Layer* layer)
    {
        layer_stack.PushLayer(layer);
    }

    void Application::PushOverlay(Layer* overlay)
    {
        layer_stack.PushOverlay(overlay);
    }
}
