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

#include "SandBox2D.h"

#include <imgui.h>

#include "glm/gtc/type_ptr.hpp"

static constexpr uint32_t s_MapWidth = 24;
static const char* s_MapTiles =
	"WWWWWWWDDDDDDDDWWWAWWWWW"
	"WWWWWWWDWWWWWWWWWWWWWWWW"
	"WWWWWWWWWDDDDDDDDDDWWWWW"
	"WWWWWWWDDDDDDDWWWWWWWWWW"
	"WWWWWDDDDDDDDDDWWWWWWWWW"
	"WWWWDDDDDDDDDDWDDDDDWWWW"
	"WWDDDDDDWWWWWWWWDDDDDWWW"
	"WDDDDDDWWWWWWWWWDDDDWWWW"
	"WDDDDDDDDDDWWWWDDDDWWWWW"
	"WWWWDDDDDDDDDDDDDDWWWWWW"
	"WWWWWWWWWWWWWWWWWWWWWWWW"
	"WWWWWWWWWWWWWWWWWWWWWWWW";

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

void SandBox2D::OnAttach()
{
	HZ_PROFILE_FUNCTION();
	m_LogoTexture = Hazel::Texture2D::Create("assets/textures/iceLogo.png");
	m_Texture     = Hazel::Texture2D::Create("assets/textures/spritesheet-tiles-double.png");

	m_MapWidth = s_MapWidth;
	m_MapHeight = strlen(s_MapTiles) / s_MapWidth;

	m_defaultTexture = Hazel::SubTexture2D::CreateFromCoords(m_Texture, {13, 3}, {128, 128});

	m_TextureMap['D'] = Hazel::SubTexture2D::CreateFromCoords(m_Texture, {1, 2}, {128, 128});
	m_TextureMap['W'] = Hazel::SubTexture2D::CreateFromCoords(m_Texture, {0, 11}, {128, 128});
	// m_SubTexture  = Hazel::SubTexture2D::CreateFromCoords(m_Texture, {8, 5}, {128, 128}, {1, 2});
	Hazel::FrameBufferSpecification specification;
	specification.Width  = 1280;
	specification.Height = 720;
	m_FrameBuffer = Hazel::FrameBuffer::Create(specification);

	m_Particle.ColorBegin        = {0 / 255.f, 212 / 255.f, 123 / 255.f, 1.0f};
	m_Particle.ColorEnd          = {254 / 255.f, 109 / 255.f, 41 / 255.f, 1.0f};
	m_Particle.SizeBegin         = 0.3f, m_Particle.SizeVariation = 0.3f, m_Particle.SizeEnd = 0.0f;
	m_Particle.LifeTime          = 1.0f;
	m_Particle.Velocity          = {0.0f, 0.0f};
	m_Particle.VelocityVariation = {2.0f, 2.0f};
	m_Particle.Position          = {0.0f, 0.0f};
}

void SandBox2D::OnDetech()
{
	HZ_PROFILE_FUNCTION();
}

void SandBox2D::OnUpdate(Hazel::TimeStep& ts)
{
	auto mouseState = SDL_GetMouseState(NULL, NULL);
	// PROFILE_SCOPE("SandBox2D::OnUpdate");
	HZ_PROFILE_FUNCTION();

	Hazel::Renderer2D::ResetStats();
	m_CameraController.OnUpdate(ts);

	{
		HZ_PROFILE_SCOPE("Renderer Prep");

		m_FrameBuffer->Bind();
		Hazel::RendererCommand::SetClearColor(m_BackgroundColor);
		Hazel::RendererCommand::Clear();
	}

	{
		HZ_PROFILE_SCOPE("Renderer Draw");
		Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());

		for (unsigned int y = 0; y < m_MapHeight; y++)
		{
			for (unsigned int x = 0; x < m_MapWidth; x++)
			{
				const char tileType = s_MapTiles[y * m_MapWidth + x];
				Hazel::Ref<Hazel::SubTexture2D> texture;
				if (m_TextureMap.find(tileType) != m_TextureMap.end())
				{
					texture = m_TextureMap[tileType];
				}else
				{
					texture = m_defaultTexture;
				}

				Hazel::Renderer2D::DrawQuad({x - m_MapWidth / 2.0f, m_MapHeight / 2.0f - y, -0.1f}, {1.0f, 1.0f}, texture);
			}
		}

		Hazel::Renderer2D::EndScene();

		if ((mouseState & SDL_BUTTON_LMASK) && m_CurrentWindowID == Hazel::Application::Get().GetWindow().
			GetMainWindowID())
		{
			auto width  = Hazel::Application::Get().GetWindow().GetWidth();
			auto height = Hazel::Application::Get().GetWindow().GetHeight();

			float x, y;
			SDL_GetMouseState(&x, &y);
			auto bounds    = m_CameraController.GetBounds();
			auto cameraPos = m_CameraController.GetCamera().GetPosition();

			x                   = (x / width) * bounds.GetWidth() - bounds.GetWidth() * 0.5f;
			y                   = bounds.GetHeight() * 0.5f - (y / height) * bounds.GetHeight();
			m_Particle.Position = {x + cameraPos.x, y + cameraPos.y};
			for (int i = 0; i < 5; i++)
			{
				m_ParticleSystem.Emit(m_Particle);
			}
		}

		m_ParticleSystem.OnUpdate(ts);
		m_ParticleSystem.OnRender(m_CameraController.GetCamera());
	}
	m_FrameBuffer->UnBind();

}

void SandBox2D::OnImGuiRender()
{

	ImGui::Begin("Settings");
	ImGui::Image(m_FrameBuffer->GetColorAttachmentID(), {1280.f, 720.f});
	ImGui::End();

	static bool showDockspace = false;
	if (showDockspace)
	{
		// const auto cameraRotation = m_CameraController.GetCamera().GetRotation();
		const auto cameraPosition = m_CameraController.GetCamera().GetPosition();
		const auto windowWidth    = Hazel::Application::Get().GetWindow().GetWidth();
		const auto windowHeight   = Hazel::Application::Get().GetWindow().GetHeight();

		auto stats = Hazel::Renderer2D::GetStats();


		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();
		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();
		}

		{
			ImGui::Begin("Hazel Layer");

			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::Text("WindowSize: (%u, %u)", windowWidth, windowHeight);

			ImGui::Text("Camera");
			// ImGui::Text("Camera Rotation: %f", cameraRotation);
			ImGui::Text("Camera Position: ( %.2f, %.2f, %.2f)", cameraPosition.x, cameraPosition.y, cameraPosition.z);

			ImGui::NewLine();
			ImGui::Text("frame: %.3f", ImGui::GetIO().Framerate);
			ImGui::ColorEdit4("Square Color", glm::value_ptr(m_BackgroundColor));


			ImGui::NewLine();
			ImGui::ColorEdit4("begin Color", glm::value_ptr(m_Particle.ColorBegin));
			ImGui::ColorEdit4("End Color", glm::value_ptr(m_Particle.ColorEnd));

			ImGui::NewLine();
			for (auto& profileResult : m_ProfileResults)
			{
				ImGui::Text("%s: %.3fms", profileResult.Name, profileResult.Time);
			}
			m_ProfileResults.clear();

			ImGui::NewLine();
			static bool isSync    = Hazel::Application::Get().GetWindow().IsVSync();
			bool        laststate = isSync;
			ImGui::Checkbox("isVSync", &isSync);
			if (isSync != laststate)
			{
				Hazel::Application::Get().GetWindow().SetVSync(isSync);
			}
			uint32_t textureID = m_FrameBuffer->GetColorAttachmentID();
			ImGui::Image(textureID, ImVec2(1280.f, 720.f));

			ImGui::End();
		}

		ImGui::End();
	}
}

void SandBox2D::OnEvent(SDL_Event& e)
{
	if (m_CurrentWindowID != e.window.windowID)
		m_CurrentWindowID = e.window.windowID;

	m_CameraController.OnEvent(e);
}
