//
// Created by sfd on 25-5-25.
//

#include "EditorLayer.h"

#include <imgui.h>
#include <iostream>
#include <glm/gtc/type_ptr.hpp>

#include "glm/ext/matrix_clip_space.hpp"

namespace Hazel
{
    EditorLayer::EditorLayer()
        : Layer("HazelEditor"), m_CameraController((float)Application::Get().GetWindow().GetWidth() / (float)Application::Get().GetWindow().GetHeight())
    {
    }

    void EditorLayer::OnAttach()
    {

    	FrameBufferSpecification spec;
    	spec.Width = Application::Get().GetWindow().GetWidth();
    	spec.Height = Application::Get().GetWindow().GetHeight();
        m_FrameBuffer = FrameBuffer::Create(spec);

    	m_ViewPortSize = { spec.Width, spec.Height };

    	m_LogoTexture = Texture2D::Create("assets/textures/iceLogo.png");
    	m_CheckerBoardTexture = Texture2D::Create("assets/textures/Checkerboard.png");

    	m_ActiveScene = CreateRef<Scene>();

    	// Entity
    	m_SquareEntity = m_ActiveScene->CreateEntity("Square A");
    	m_SquareEntity.AddComponent<SpriteRendererComponent>(glm::vec4{0.2f, 0.3f, 0.8f, 1.0f});

	    auto redSquare = m_ActiveScene->CreateEntity("Square B");
    	redSquare.AddComponent<SpriteRendererComponent>(glm::vec4{1.0f, 0.0f, 0.0f, 1.0f});




    	m_CameraEntity = m_ActiveScene->CreateEntity("Camera A");
    	m_CameraEntity.AddComponent<CameraComponent>();
    	m_PrimaryCamera = true;


    	m_SecondCamera = m_ActiveScene->CreateEntity("Camera B");
    	m_SecondCamera.AddComponent<CameraComponent>();
    	m_SecondCamera.GetComponent<CameraComponent>().Primary = false;


    	class CameraController : public ScriptableEntity
    	{
    	public:
    		void OnUpdate(const TimeStep ts)
    		{
				auto& translation = GetComponent<TransformComponent>().Translation;
    			static const auto state = SDL_GetKeyboardState(nullptr);
    			if (state[SDL_SCANCODE_A])
    				translation.x -= ts * speed;
    			if (state[SDL_SCANCODE_D])
    				translation.x += ts * speed;
    			if (state[SDL_SCANCODE_W])
    				translation.y += ts * speed;
    			if (state[SDL_SCANCODE_S])
    				translation.y -= ts * speed;
    		}
    	private:
    		float speed = 2.0f;
    	};

		m_CameraEntity.AddComponent<NativeScriptComponent>().Bind<CameraController>();
		m_SecondCamera.AddComponent<NativeScriptComponent>().Bind<CameraController>();


    	m_SceneHierachyPanel.SetContext(m_ActiveScene);
    }

    void EditorLayer::OnDetech()
    {
    }

    void EditorLayer::OnUpdate(TimeStep& ts)
    {
    	// reset Renderer Draw Stats
    	Renderer2D::ResetStats();

    	if (const auto& spec = m_FrameBuffer->GetSpecification();
    		spec.Width != m_ViewPortSize.x || spec.Height != m_ViewPortSize.y)
    	{
		    m_FrameBuffer->Resize((uint32_t)m_ViewPortSize.x, (uint32_t)m_ViewPortSize.y);
		    m_CameraController.OnResize(m_ViewPortSize.x, m_ViewPortSize.y);

    		m_ActiveScene->OnViewportResize(m_ViewPortSize.x, m_ViewPortSize.y);

	    }

	    // update camera
	    if (m_ViewportFocused && m_ViewportHovered)
	    {
		    m_CameraController.OnUpdate(ts);
	    }

	    // update Renderer
	    m_FrameBuffer->Bind();
	    RendererCommand::SetClearColor(m_BackgroundColor);
	    RendererCommand::Clear();


	    // Renderer2D::BeginScene(m_cameraController.GetCamera());

	    // update Scene
	    m_ActiveScene->OnUpdate(ts);

	    // Renderer2D::DrawQuad({0, 0.5f}, {1.0f, 1.0f}, {0.2f, 0.3f, 0.8f, 1.0f});
	    // Renderer2D::DrawQuad({0, -0.5f}, {1.0f, 1.0f}, {0.8f, 0.2f, 0.2f, 1.0f});
	    // Renderer2D::DrawQuad({-1, 0}, {1, 1}, m_LogoTexture);
	    // Renderer2D::DrawQuad({1, 0}, {1, 1}, m_CheckerBoardTexture);

	    // Renderer2D::EndScene();
	    m_FrameBuffer->UnBind();
    }

    void EditorLayer::OnImGuiRender()
    {
	    static bool showDockspace = true;
	    if (showDockspace)
	    {
		    static bool               dockspaceOpen   = true;
		    static bool               opt_fullscreen  = true;
		    static bool               opt_padding     = false;
		    static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;

		    // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
		    // because it would be confusing to have two docking targets within each others.
		    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
		    if (opt_fullscreen)
		    {
			    const ImGuiViewport* viewport = ImGui::GetMainViewport();
			    ImGui::SetNextWindowPos(viewport->WorkPos);
			    ImGui::SetNextWindowSize(viewport->WorkSize);
			    ImGui::SetNextWindowViewport(viewport->ID);
			    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
			    ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
			    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
				    ImGuiWindowFlags_NoMove;
			    window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
		    }
		    else
		    {
			    dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
		    }

		    // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background
		    // and handle the pass-thru hole, so we ask Begin() to not render a background.
		    if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
			    window_flags |= ImGuiWindowFlags_NoBackground;

		    // Important: note that we proceed even if Begin() returns false (aka window is collapsed).
		    // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
		    // all active windows docked into it will lose their parent and become undocked.
		    // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
		    // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
		    if (!opt_padding)
			    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
		    ImGui::Begin("DockSpace Demo", &dockspaceOpen, window_flags);
		    if (!opt_padding)
			    ImGui::PopStyleVar();

		    if (opt_fullscreen)
			    ImGui::PopStyleVar(2);

		    // Submit the DockSpace
		    ImGuiIO& io = ImGui::GetIO();
	    	ImGuiStyle& style = ImGui::GetStyle();
	    	style.WindowMinSize.x = 350.0f;
		    if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
		    {
			    ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
			    ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
		    }

		    if (ImGui::BeginMenuBar())
		    {
			    if (ImGui::BeginMenu("Options"))
			    {
				    // Disabling fullscreen would allow the window to be moved to the front of other windows,
				    // which we can't undo at the moment without finer window depth/z control.
				    // ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen);
				    // ImGui::MenuItem("Padding", NULL, &opt_padding);
				    // ImGui::Separator();

				    if (ImGui::MenuItem("Exit")) { Hazel::Application::Get().Close(); }

				    ImGui::EndMenu();
			    }

			    ImGui::EndMenuBar();
		    }

	    	m_SceneHierachyPanel.OnImGuiRender();
		    {
			    ImGui::Begin("Render Status");

			    auto stats = Renderer2D::GetStats();
			    ImGui::Text("Renderer BatchInfo: ");
			    ImGui::Text("Draw Calls: %d", stats.DrawCalls);
			    ImGui::Text("Quads: %d", stats.QuadCount);
			    ImGui::Text("Vertices: %d", stats.GetTotalVertexCount());
			    ImGui::Text("Indices: %d", stats.GetTotalIndexCount());

			    ImGui::Separator();

			    if (ImGui::Checkbox("Camera A", &m_PrimaryCamera))
			    {
				    m_CameraEntity.GetComponent<CameraComponent>().Primary = m_PrimaryCamera;
				    m_SecondCamera.GetComponent<CameraComponent>().Primary = !m_PrimaryCamera;
			    }

			    ImGui::Separator();

			    ImGui::Text("viewPortSize: (%.2f, %.2f)", m_ViewPortSize.x, m_ViewPortSize.y);
			    ImGui::End();
		    }

		    {
			    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0.0f, 0.0f});
			    ImGui::Begin("Viewport");
			    m_ViewportFocused = ImGui::IsWindowFocused();
			    m_ViewportHovered = ImGui::IsWindowHovered();


			    ImVec2 viewPortPanelSize = ImGui::GetContentRegionAvail();
			    if (m_ViewPortSize != *reinterpret_cast<glm::vec2*>(&viewPortPanelSize))
			    {
				    m_ViewPortSize = {viewPortPanelSize.x, viewPortPanelSize.y};
			    }
			    // ImGui::Text("Viewport: (%.2f, %.2f)", viewPortPanelSize.x, viewPortPanelSize.y);
			    ImGui::Image(m_FrameBuffer->GetColorAttachmentID(), {m_ViewPortSize.x, m_ViewPortSize.y}, {0, 1},
			                 {1, 0});

			    ImGui::End();
			    ImGui::PopStyleVar();
		    }

		    ImGui::End();
	    }
    }

    void EditorLayer::OnEvent(SDL_Event& e)
    {
	    if (m_ViewportFocused && m_ViewportHovered)
	    {
		    m_CameraController.OnEvent(e);
	    }
    }
}
