#pragma once

#include "Raybyte/Core/ApplicationContext.h"

#include <string>

#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <imgui.h>
#include <imgui_internal.h>

namespace Raybyte {

	namespace UI {

		static void BeginPropertyGrid()
		{
			ImGui::Columns(2);
		}

		static void EndPropertyGrid()
		{
			ImGui::Columns(1);
		}

		static void TextCenteredX(const char* fmt, ...)
		{
			va_list args;
			va_start(args, fmt);
			float fontSize = ImGui::GetFontSize() * strlen(fmt) * 0.5f;
			ImGui::SetCursorPosX(ImGui::GetWindowSize().x * 0.5f - fontSize + (fontSize * 0.5f));
			ImGui::TextV(fmt, args);
			va_end(args);
		}

		static void TextCentered(const char* fmt, ...)
		{
			va_list args;
			va_start(args, fmt);
			float fontSize = ImGui::GetFontSize() * strlen(fmt) * 0.5f;
			ImGui::SetCursorPosX(ImGui::GetWindowSize().x * 0.5f - fontSize + (fontSize * 0.5f));
			ImGui::SetCursorPosY(ImGui::GetWindowSize().y * 0.5f);
			ImGui::TextV(fmt, args);
			va_end(args);
		}

		static void SameLine(float offsetFromStartX = 0.0f, float spacing = -1.0f)
		{
			ImGui::SameLine(offsetFromStartX, spacing);
		}

		static void SetCursorPositionX(float x)
		{
			ImGui::SetCursorPosX(x);
		}

		static void SetCursorPositionY(float y)
		{
			ImGui::SetCursorPosY(y);
		}

		static bool CollapsingHeader(const std::string& label)
		{
			return ImGui::CollapsingHeader(label.c_str());
		}

		static bool Button(const std::string& label, const glm::vec2& size = { 0.0f, 0.0f })
		{
			bool pressed = ImGui::Button(label.c_str(), { size.x, size.y });
			ImGui::NextColumn();
			return pressed;
		}

		static bool Property(const std::string& label, bool& value)
		{
			bool modified = false;

			ImGui::Text(label.c_str());
			ImGui::NextColumn();
			ImGui::PushItemWidth(-1.0f);

			std::string id = "##" + label;
			if (ImGui::Checkbox(id.c_str(), &value))
				modified = true;

			ImGui::PopItemWidth();
			ImGui::NextColumn();

			return modified;
		}

		static bool Property(const std::string& label, float& value, float speed = 0.1f, float min = 0.0f, float max = 0.0f, bool readOnly = false)
		{
			bool modified = false;

			ImGui::Text(label.c_str());
			ImGui::NextColumn();
			ImGui::PushItemWidth(-1.0f);

			std::string id = "##" + label;
			if (!readOnly)
			{
				if (ImGui::DragFloat(id.c_str(), &value, speed, min, max))
					modified = true;
			}
			else
			{
				ImGui::InputFloat(id.c_str(), &value, 0.0f, 0.0f, "%.3f", ImGuiInputTextFlags_ReadOnly);
			}

			ImGui::PopItemWidth();
			ImGui::NextColumn();

			return modified;
		}

		static bool Property(const std::string& label, glm::vec3& value, float speed = 0.1f, float min = 0.0f, float max = 0.0f, bool readOnly = false)
		{
			bool modified = false;

			ImGui::Text(label.c_str());
			ImGui::NextColumn();
			ImGui::PushItemWidth(-1.0f);

			std::string id = "##" + label;
			if (!readOnly)
			{
				if (ImGui::DragFloat3(id.c_str(), glm::value_ptr(value), speed, min, max))
					modified = true;
			}
			else
			{
				ImGui::InputFloat3(id.c_str(), glm::value_ptr(value), "%.3f", ImGuiInputTextFlags_ReadOnly);
			}

			ImGui::PopItemWidth();
			ImGui::NextColumn();

			return modified;
		}

		static bool Dropdown(const std::string& label, const char* options[], int32& selected, uint32 size)
		{
			bool modified = false;

			ImGui::Text(label.c_str());
			ImGui::NextColumn();
			ImGui::PushItemWidth(-1.0f);

			std::string id = "##" + label;
			if (ImGui::Combo(id.c_str(), &selected, options, size))
				modified = true;

			ImGui::PopItemWidth();
			ImGui::NextColumn();

			return modified;
		}

		static bool ColorEdit(const std::string& label, glm::vec3& value)
		{
			bool modified = false;

			ImGui::Text(label.c_str());
			ImGui::NextColumn();
			ImGui::PushItemWidth(-1.0f);

			std::string id = "##" + label;
			if (ImGui::ColorEdit3(id.c_str(), glm::value_ptr(value)))
				modified = true;

			ImGui::PopItemWidth();
			ImGui::NextColumn();

			return modified;
		}

		static bool ColorEdit(const std::string& label, glm::vec4& value)
		{
			bool modified = false;

			ImGui::Text(label.c_str());
			ImGui::NextColumn();
			ImGui::PushItemWidth(-1.0f);

			std::string id = "##" + label;
			if (ImGui::ColorEdit4(id.c_str(), glm::value_ptr(value)))
				modified = true;

			ImGui::PopItemWidth();
			ImGui::NextColumn();

			return modified;
		}

		static void Image(Ref<Image2D> image, const glm::vec2& size, const glm::vec2& uv0 = { 0.0f, 0.0f }, const glm::vec2& uv1 = { 1.0f, 1.0f })
		{
			ApplicationContext::GetInstance()->GetImGuiRenderer()->Image(image, { size.x, size.y }, { uv0.x, uv0.y }, { uv1.x, uv1.y });
		}

		static void Image(Ref<Image2D> image, uint32 layer, const glm::vec2& size, const glm::vec2& uv0 = { 0.0f, 0.0f }, const glm::vec2& uv1 = { 1.0f, 1.0f })
		{
			ApplicationContext::GetInstance()->GetImGuiRenderer()->Image(image, layer, { size.x, size.y }, { uv0.x, uv0.y }, { uv1.x, uv1.y });
		}

		static bool ImageButton(Ref<Image2D> image, const glm::vec2& size, const glm::vec2& uv0 = { 0.0f, 0.0f }, const glm::vec2& uv1 = { 1.0f, 1.0f }, int32 framePadding = -1, const glm::vec4& bgColor = { 0.0f, 0.0f, 0.0f, 0.0f }, const glm::vec4& tintColor = { 1.0f, 1.0f, 1.0f, 1.0f })
		{
			return ApplicationContext::GetInstance()->GetImGuiRenderer()->ImageButton(image, { size.x, size.y }, { uv0.x, uv0.y }, { uv1.x, uv1.y }, framePadding, { bgColor.x, bgColor.y, bgColor.z, bgColor.w }, { tintColor.x, tintColor.y, tintColor.z, tintColor.w });
		}

		static void ImageMip(Ref<Image2D> image, uint32 mip, const glm::vec2& size, const glm::vec2& uv0 = { 0.0f, 0.0f }, const glm::vec2& uv1 = { 1.0f, 1.0f })
		{
			ApplicationContext::GetInstance()->GetImGuiRenderer()->Image(image, mip, { size.x, size.y }, { uv0.x, uv0.y }, { uv1.x, uv1.y });
		}

	}

}