#include "DeerRender/ImGui/ImGuiLayer.h"

#include "DeerRender/Application.h"
#include "DeerRender/Events/Event.h"
#include "DeerRender/Input.h"
#include "DeerRender/Log.h"
#include "DeerRender/Render/RenderCommand.h"

// THIS ORDER
#include "Plattform/OpenGL/imgui_impl_opengl3.h"
// THIS ORDER
#include "glad/glad.h"
// THIS ORDER
#include "GLFW/glfw3.h"
// THIS ORDER
#include "imgui.h"
// THIS ORDER
#include "backends/imgui_impl_glfw.h"
// THIS ORDER
#include "ImGuizmo.h"

namespace Deer {
	void ImGuiLayer::onAttach() {
		ImGui::CreateContext();
		ImGui::StyleColorsDark();

		ImGuiIO& io = ImGui::GetIO();
		io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
		io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
		io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;

		ImGui_ImplOpenGL3_Init("#version 410");
		Application::getWindow().initImGUI();

		io.DisplaySize =
		    ImVec2(Application::getWindow().getWitdth(),
		           Application::getWindow().getHeight());
	}

	void ImGuiLayer::onDetach() {}

	void ImGuiLayer::begin() {
		ImGui_ImplOpenGL3_NewFrame();
		ImGui::NewFrame();
		ImGuizmo::BeginFrame();
	}

	void ImGuiLayer::end() {
		RenderCommand::unbindFrameBuffer();

		ImGui::EndFrame();
		ImGui::Render();
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
	}

	void ImGuiLayer::onEvent(Event& e) {
		EventDispatcher dispacher(e);

		// dispacher.dispatch<MouseButtonPressedEvent>(std::bind(&ImGuiLayer::onMouseButtonPressedEvent,
		// this, std::placeholders::_1));
		// dispacher.dispatch<MouseButtonReleasedEvent>(std::bind(&ImGuiLayer::onMouseButtonReleasedEvent,
		// this, std::placeholders::_1));
		// dispacher.dispatch<MouseMovedEvent>(std::bind(&ImGuiLayer::onMouseMovedEvent,
		// this, std::placeholders::_1));
		// dispacher.dispatch<MouseScrolledEvent>(std::bind(&ImGuiLayer::onMouseScrollEvent,
		// this, std::placeholders::_1));
		// dispacher.dispatch<KeyPressedEvent>(std::bind(&ImGuiLayer::onKeyPressedEvent,
		// this, std::placeholders::_1));
		// dispacher.dispatch<KeyTypedEvent>(std::bind(&ImGuiLayer::onKeyTypedEvent,
		// this, std::placeholders::_1));
		dispacher.dispatch<WindowResizeEvent>(std::bind(
		    &ImGuiLayer::onWindowResizeEvent, this, std::placeholders::_1));
	}

	bool ImGuiLayer::onMouseButtonPressedEvent(MouseButtonPressedEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.MouseDown[e.getMouseButton()] = true;
		return false;
	}

	bool ImGuiLayer::onMouseButtonReleasedEvent(MouseButtonReleasedEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.MouseDown[e.getMouseButton()] = false;
		return false;
	}

	bool ImGuiLayer::onMouseMovedEvent(MouseMovedEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.MousePos = ImVec2(e.getX(), e.getY());
		return false;
	}

	bool ImGuiLayer::onMouseScrollEvent(MouseScrolledEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.MouseWheel += e.getYOffset();
		io.MouseWheelH += e.getXOffset();

		return false;
	}

	bool ImGuiLayer::onKeyPressedEvent(KeyPressedEvent& e) {
	    ImGuiIO& io = ImGui::GetIO();
	    ImGuiKey key = static_cast<ImGuiKey>(e.getKeyCode());
		
	    // Modern way: register key press
	    io.AddKeyEvent(key, true);
		
	    // Modifier keys can also be updated automatically or manually:
	    io.AddKeyEvent(ImGuiKey_LeftCtrl, ImGui::IsKeyPressed(ImGuiKey_LeftCtrl) || ImGui::IsKeyPressed(ImGuiKey_RightCtrl));
	    io.AddKeyEvent(ImGuiKey_LeftShift, ImGui::IsKeyPressed(ImGuiKey_LeftShift) || ImGui::IsKeyPressed(ImGuiKey_RightShift));
	    io.AddKeyEvent(ImGuiKey_LeftAlt, ImGui::IsKeyPressed(ImGuiKey_LeftAlt) || ImGui::IsKeyPressed(ImGuiKey_RightAlt));
	    io.AddKeyEvent(ImGuiKey_LeftSuper, ImGui::IsKeyPressed(ImGuiKey_LeftSuper) || ImGui::IsKeyPressed(ImGuiKey_RightSuper));
		
	    return false;
	}


	bool ImGuiLayer::onKeyReleasedEvent(KeyReleasedEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.AddKeyEvent((ImGuiKey)e.getKeyCode(),
	                   ImGui::IsKeyPressed(ImGuiKey_LeftSuper) || ImGui::IsKeyPressed(ImGuiKey_RightSuper));
		

		return false;
	}

	bool ImGuiLayer::onKeyTypedEvent(KeyTypedEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.AddInputCharacter((unsigned short)e.getKeyCode());

		if (e.getKeyCode() == DEER_KEY_BACKSPACE) {
			io.AddInputCharacter(ImGuiKey::ImGuiKey_Backspace);
		}

		return false;
	}

	bool ImGuiLayer::onWindowResizeEvent(WindowResizeEvent& e) {
		ImGuiIO& io = ImGui::GetIO();
		io.DisplaySize = ImVec2(e.getWidth(), (int)e.getHeight());
		glViewport(0, 0, (int)e.getWidth(), (int)e.getHeight());

		return false;
	}
} // namespace Deer
