#include "DeerStudio/StudioAPI/UI.h"
#include "DeerRender/FrameBuffer.h"
#include "DeerRender/Log.h"

#include "DeerStudio/AngelScriptEngine.h"
#include "DeerStudio/AngelScriptEngine/ErrorHandle.h"

#include "DeerStudio/Fonts.h"
#include "angelscript.h"
#include "imgui.h"
#include "scriptany.h"

#include <string>

namespace Deer {
	namespace StudioAPI {
		namespace DragDropPayload {
			CScriptAny* payload;
		}

		void separator() { ImGui::Separator(); }

		void title(std::string& txt) {
			ImGui::PushFont(titleText);
			ImGui::Text("%s", txt.c_str());
			ImGui::PopFont();
		}

		void titleEnd(std::string& txt) {
			ImGui::PushFont(titleText);
			textEnd(txt);
			ImGui::PopFont();
		}

		void titleCenter(std::string& txt) {
			ImGui::PushFont(titleText);
			textCenter(txt);
			ImGui::PopFont();
		}

		void titleCenterY(std::string& txt, int height) {
			ImGui::PushFont(titleText);

			float textHeight = ImGui::GetFontSize();
			float yOffset = (height - textHeight) * 0.5f;
			ImGui::SetCursorPosY(ImGui::GetCursorPosY() + yOffset);

			ImGui::Text("%s", txt.c_str());
			ImGui::PopFont();
		}

		void sameline() { ImGui::SameLine(); }

		bool button(std::string& txt) { return ImGui::Button(txt.c_str()); }

		bool buttonCenter(std::string& txt) {
			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float textWidth =
			    ImGui::CalcTextSize(txt.c_str(), nullptr, false).x;
			float padding =
			    (sizeX - textWidth - ImGui::GetStyle().FramePadding.x * 2) *
			    0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			return ImGui::Button(txt.c_str());
		}

		bool buttonEnd(std::string& txt) {
			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float textWidth =
			    ImGui::CalcTextSize(txt.c_str(), nullptr, false).x;
			float padding =
			    (sizeX - textWidth - ImGui::GetStyle().FramePadding.x * 2);

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			return ImGui::Button(txt.c_str());
		}

		bool cartIconButton(const std::string& label, const std::string& icon, int iconSize, int width)
		{
		    ImGui::BeginGroup();
		
			int offset = 16;
		    float textHeight = ImGui::GetTextLineHeight();
		    float itemWidth  = width;
		    float itemHeight = iconSize + textHeight + offset * 2;
		
		    // Clickable area uses the full width
		    ImGui::InvisibleButton(label.c_str(), ImVec2(itemWidth, itemHeight));
		    bool hovered = ImGui::IsItemHovered();
		    bool pressed = ImGui::IsItemClicked();
		
		    ImDrawList* dl = ImGui::GetWindowDrawList();
		    ImVec2 p = ImGui::GetItemRectMin();
		    ImVec2 q = ImGui::GetItemRectMax();
		
		    // Background
		    dl->AddRectFilled(
		        p, q,
		        hovered ? IM_COL32(255,255,255,30) : IM_COL32(255,255,255,10),
		        4.0f
		    );
		
		    // Center icon horizontally
		    ImVec2 iconPos(
		        p.x + (itemWidth - iconSize) * 0.5f,
		        p.y + 2.0f + offset
		    );
		
		    int texId = StudioAPI::getIconId("Icons/" + icon);
		    dl->AddImage(
		        (ImTextureID)texId,
		        iconPos,
		        ImVec2(iconPos.x + iconSize, iconPos.y + iconSize),
		        ImVec2(0,1),
		        ImVec2(1,0)
		    );
		
		    // Center text
		    ImVec2 textSize = ImGui::CalcTextSize(label.c_str());
		    ImVec2 textPos(
		        p.x + (itemWidth - textSize.x) * 0.5f,
		        iconPos.y + iconSize + 2.0f
		    );
		    dl->AddText(textPos, ImGui::GetColorU32(ImGuiCol_Text), label.c_str());
		
		    ImGui::EndGroup();
		    return pressed;
		}

		bool cartIconButton_frameBuffer(const std::string& label, FrameBufferHandleStruct icon, int iconSize, int width)
		{
		    ImGui::BeginGroup();
		
			int offset = 16;
		    float textHeight = ImGui::GetTextLineHeight();
		    float itemWidth  = width;
		    float itemHeight = iconSize + textHeight + offset * 2;
		
		    // Clickable area uses the full width
		    ImGui::InvisibleButton(label.c_str(), ImVec2(itemWidth, itemHeight));
		    bool hovered = ImGui::IsItemHovered();
		    bool pressed = ImGui::IsItemClicked();
		
		    ImDrawList* dl = ImGui::GetWindowDrawList();
		    ImVec2 p = ImGui::GetItemRectMin();
		    ImVec2 q = ImGui::GetItemRectMax();
		
		    // Background
		    dl->AddRectFilled(
		        p, q,
		        hovered ? IM_COL32(255,255,255,30) : IM_COL32(255,255,255,10),
		        4.0f
		    );
		
		    // Center icon horizontally
		    ImVec2 iconPos(
		        p.x + (itemWidth - iconSize) * 0.5f,
		        p.y + 2.0f + offset
		    );
		
			FrameBuffer& frameBuffer = Resource<FrameBuffer>::unsafeFromId(icon.frameBufferId).getData(); 
			frameBuffer.bind();
		    ImTextureID texId = frameBuffer.getTextureBufferID();
		    dl->AddImage(
		        (ImTextureID)texId,
		        iconPos,
		        ImVec2(iconPos.x + iconSize, iconPos.y + iconSize),
		        ImVec2(0,1),
		        ImVec2(1,0)
		    );
			frameBuffer.unbind();
		
		    // Center text
		    ImVec2 textSize = ImGui::CalcTextSize(label.c_str());
		    ImVec2 textPos(
		        p.x + (itemWidth - textSize.x) * 0.5f,
		        iconPos.y + iconSize + 2.0f
		    );
		    dl->AddText(textPos, ImGui::GetColorU32(ImGuiCol_Text), label.c_str());
		
		    ImGui::EndGroup();
		    return pressed;
		}

		void textColor(float r, float g, float b, std::string& msg) {
			ImGui::TextColored(ImVec4(r, g, b, 1.0f), "%s", msg.c_str());
		}

		void text(std::string& msg) { ImGui::Text("%s", msg.c_str()); }

		bool isPanelActive() {
			return ImGui::IsWindowFocused(
			    ImGuiFocusedFlags_RootAndChildWindows);
		}

		void textEnd(std::string& msg) {
			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float textWidth =
			    ImGui::CalcTextSize(msg.c_str(), nullptr, false).x;
			float padding = (sizeX - textWidth);

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			ImGui::Text("%s", msg.c_str());
		}

		void textCenter(std::string& msg) {
			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float textWidth =
			    ImGui::CalcTextSize(msg.c_str(), nullptr, false).x;
			float padding = (sizeX - textWidth) * 0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			ImGui::Text("%s", msg.c_str());
		}

		void drawIcon(std::string& name, int size) {
			int iconId = StudioAPI::getIconId("Icons/" + name);

			if (iconId < 0) {
				DEER_EDITOR_ENGINE_ERROR("Invalid icon name {0}", name.c_str());
				AngelScriptEngine::raiseError();
				return;
			}

			ImGui::Image((void*)(uint64_t)iconId, ImVec2(size, size), ImVec2(0, 1), ImVec2(1, 0));
		}

		void drawIcon_resource(Resource<Texture> texture, int size) {
			int iconId = texture.getData().getTextureID();
			ImGui::Image((void*)(uint64_t)iconId, ImVec2(size, size), ImVec2(0, 1), ImVec2(1, 0));
		}
		void drawIconHighlight_resource(Resource<Texture> texture, int size) {
			int iconId = texture.getData().getTextureID();
			ImGui::Image((void*)(uint64_t)iconId, ImVec2(size, size), ImVec2(0, 1), ImVec2(1, 0), ImVec4(0.5f, 0.6f, 0.8f, 1.0f),  ImVec4(0,0,0,0));
		}

		void drawIconHighlight(std::string& name, int size) {
			int iconId = StudioAPI::getIconId(name);

			if (iconId < 0) {
				DEER_EDITOR_ENGINE_ERROR("Invalid icon name {0}", name.c_str());
				AngelScriptEngine::raiseError();
				return;
			}

			ImGui::Image((void*)(uint64_t)iconId, ImVec2(size, size), ImVec2(0, 1), ImVec2(1, 0), ImVec4(0.5f, 0.6f, 0.8f, 1.0f), ImVec4(0,0,0,0));
		}

		void drawFrameBuffer(FrameBufferHandleStruct frameBuffer_handle, int sizeX, int sizeY) {

			Resource<FrameBuffer> frameBufferResource = Resource<FrameBuffer>::unsafeFromId(frameBuffer_handle.frameBufferId);
			FrameBuffer& frameBuffer = frameBufferResource.getData();
			frameBuffer.bind();
			int frameBufferId = frameBuffer.getTextureBufferID(0);

			ImGui::Image((void*)(uint64_t)frameBufferId, ImVec2(sizeX, sizeY), ImVec2(0, 1), ImVec2(1, 0));
			frameBuffer.unbind();
		}

		void drawFrameBufferCentered(FrameBufferHandleStruct frameBuffer_handle, int _x, int _y) {
			Resource<FrameBuffer> frameBufferResource = Resource<FrameBuffer>::unsafeFromId(frameBuffer_handle.frameBufferId);
			FrameBuffer& frameBuffer = frameBufferResource.getData();
			int frameBufferId = frameBuffer.getTextureBufferID();

			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float iconWidth = _x;
			float padding = (sizeX - iconWidth) * 0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			ImGui::Image((void*)(uint64_t)frameBufferId, ImVec2(_x, _y),
			             ImVec2(0, 1), ImVec2(1, 0));
		}

		void drawIconCentered_resource(Resource<Texture> texture, int size) {
			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float iconWidth = size;
			float padding = (sizeX - iconWidth) * 0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			drawIcon_resource(texture, size);
		}

		void drawIconCentered(std::string& name, int size) {
			int iconId = StudioAPI::getIconId("Icons/" + name);
			if (iconId < 0) {
				DEER_EDITOR_ENGINE_ERROR("Invalid icon name {0}", name.c_str());
				AngelScriptEngine::raiseError();
				return;
			}

			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float iconWidth = size;
			float padding = (sizeX - iconWidth) * 0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			drawIcon(name, size);
		}
		void drawIconCenteredHighlight_resource(Resource<Texture> texture, int size) {
			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float iconWidth = size;
			float padding = (sizeX - iconWidth) * 0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			drawIconHighlight_resource(texture, size);
		}

		void drawIconCenteredHighlight(std::string& name, int size) {
			int iconId = StudioAPI::getIconId("Icons/" + name);
			if (iconId < 0) {
				DEER_EDITOR_ENGINE_ERROR("Invalid icon name {0}", name.c_str());
				AngelScriptEngine::raiseError();
				return;
			}

			float sizeX;
			if (ImGui::GetColumnsCount() > 1)
				sizeX = ImGui::GetColumnWidth(-1);
			else
				sizeX = ImGui::GetContentRegionAvail().x;

			float iconWidth = size;
			float padding = (sizeX - iconWidth) * 0.5f;

			if (padding > 0.0f)
				ImGui::SetCursorPosX(ImGui::GetCursorPosX() + padding);

			drawIconHighlight(name, size);
		}

		bool isItemClicked(int mouse) { return ImGui::IsItemClicked(mouse); }

		bool isMouseDoubleClicked(int mouse) {
			return ImGui::IsMouseDoubleClicked(mouse);
		}

		bool menuItem(std::string& txt) { return ImGui::MenuItem(txt.c_str()); }

		void subMenu(std::string& txt, asIScriptFunction* func) {
			if (ImGui::BeginMenu(txt.c_str())) {
				if (AngelScriptEngine::scriptContext &&
				    AngelScriptEngine::scriptContext->PushState() == asSUCCESS) {

					AS_CHECK(AngelScriptEngine::scriptContext->Prepare(func));
					AS_CHECK(AngelScriptEngine::scriptContext->Execute());

					AngelScriptEngine::scriptContext->PopState();
				}
				ImGui::EndMenu();
			}
		}

		void menuItemDisabled(std::string& txt) {
			ImGui::BeginDisabled();
			ImGui::MenuItem(txt.c_str());
			ImGui::EndDisabled();
		}

		void dragDropSource(std::string& id, CScriptAny* data, std::string& debugText) {
			if (DragDropPayload::payload && !ImGui::GetDragDropPayload()) {
				DragDropPayload::payload->Release();
				DragDropPayload::payload = nullptr;
			}

			ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
			if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
				if (DragDropPayload::payload)
					DragDropPayload::payload->Release();

				data->AddRef();
				DragDropPayload::payload = data;

				ImGui::SetDragDropPayload(id.c_str(), nullptr, 0);
				ImGui::Text("%s", debugText.c_str());

				ImGui::EndDragDropSource();
			}

			ImGui::PopStyleVar();
		}

		void dragDropTarget(std::string& id, asIScriptFunction* func) {
			if (ImGui::BeginDragDropTarget()) {
				if (ImGui::AcceptDragDropPayload(id.c_str()) &&
				    DragDropPayload::payload) {

					if (AngelScriptEngine::scriptContext &&
					    AngelScriptEngine::scriptContext->PushState() == asSUCCESS) {

						AS_CHECK(AngelScriptEngine::scriptContext->Prepare(func));
						AS_CHECK(AngelScriptEngine::scriptContext->SetArgObject(0, DragDropPayload::payload));
						AS_CHECK(AngelScriptEngine::scriptContext->Execute());

						AngelScriptEngine::scriptContext->PopState();
					}

					DragDropPayload::payload->Release();
					DragDropPayload::payload = nullptr;
				}
				ImGui::EndDragDropTarget();
			}
		}

		bool inputText(std::string& id, std::string& in, std::string& text) {
			static char buffer[256];
			strncpy(buffer, in.c_str(), sizeof(buffer));
			buffer[sizeof(buffer) - 1] = '\0';

			bool edited = ImGui::InputText(id.c_str(), buffer, sizeof(buffer));
			if (edited) {
				text = buffer;
			}

			return edited;
		}

		bool checkbox(std::string& label, bool value) {
			ImGui::Checkbox(label.c_str(), &value);

			return value;
		}

		bool checkboxDisabled(std::string& label, bool value) {
			ImGui::BeginDisabled();
			ImGui::Checkbox(label.c_str(), &value);
			ImGui::EndDisabled();

			return value;
		}

		float magicSlider(std::string& txt, float value, float speed) {

			ImGui::PushID(txt.c_str());

			static ImGuiID id = 0;

			float tmp = value;
			bool value_changed = false;

			ImGui::Columns(2, nullptr, false);

			ImGui::Text("%s", txt.c_str());
			ImGui::SameLine();

			ImGui::NextColumn();

			if (id == ImGui::GetID(txt.c_str())) {
				// — Input mode —
				ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
				if (ImGui::InputFloat("##input", &tmp, 0.0f, 0.0f, "%.2f",
				                      ImGuiInputTextFlags_EnterReturnsTrue |
				                          ImGuiInputTextFlags_AutoSelectAll)) {
					value_changed = true;
					id = 0;
				}
				// Cancel if click away or Esc
				if (!ImGui::IsItemActive() && !ImGui::IsItemHovered()) {
					value_changed = true;
					id = 0;
				}
			} else {
				// — Drag mode (unbounded) —
				ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
				ImGui::DragFloat("##input", &tmp, speed, 0.0f, 0.0f, "%.2f");
				if (ImGui::IsItemActive())
					value_changed = true;

				// Click to enter edit mode
				if (ImGui::IsItemHovered() &&
				    ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
					id = ImGui::GetID(txt.c_str());
					ImGui::SetKeyboardFocusHere(0);
				}
			}

			ImGui::Columns();

			if (value_changed)
				value = tmp;

			ImGui::PopID();
			return value;
		}

		glm::vec3 magicSlider3(std::string& txt, glm::vec3 value, float speed) {
			static ImGuiID id = 0;

			glm::vec3 tmp = value;
			bool value_changed = false;

			ImGui::Columns(4, nullptr, false);

			ImGui::PushID(txt.c_str());
			ImGui::Text("%s", txt.c_str());
			ImGui::NextColumn();

			for (int i = 0; i < 3; i++) {
				ImGui::PushID(i);
				if (id == ImGui::GetID("##input")) {
					// — Input mode —
					ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
					if (ImGui::InputFloat(
					        "##input", &tmp[i], 0.0f, 0.0f, "%.2f",
					        ImGuiInputTextFlags_EnterReturnsTrue |
					            ImGuiInputTextFlags_AutoSelectAll)) {
						value_changed = true;
						id = 0;
					}
					// Cancel if click away or Esc
					if (!ImGui::IsItemActive() && !ImGui::IsItemHovered()) {
						value_changed = true;
						id = 0;
					}
				} else {
					// — Drag mode (unbounded) —
					ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
					ImGui::DragFloat("##input", &tmp[i], speed, 0.0f, 0.0f,
					                 "%.2f");
					if (ImGui::IsItemActive())
						value_changed = true;

					// Click to enter edit mode
					if (ImGui::IsItemHovered() &&
					    ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
						id = ImGui::GetID("##input");
						ImGui::SetKeyboardFocusHere(-1);
					}
				}
				ImGui::PopID();
				ImGui::NextColumn();
			}
			ImGui::Columns();
			ImGui::PopID();

			if (value_changed)
				value = tmp;

			return value;
		}

		bool isKeyDown(int key) { return ImGui::IsKeyDown((ImGuiKey)key); }

		bool isKeyPressed(int key) {
			return ImGui::IsKeyPressed((ImGuiKey)key, false);
		}

		bool isMouseDragging(int key) {
			if (key == ImGuiKey_MouseRight)
				return ImGui::IsMouseDragging(ImGuiMouseButton_Right);
			else if (key == ImGuiKey_MouseLeft)
				return ImGui::IsMouseDragging(ImGuiMouseButton_Left);
			else if (key == ImGuiKey_MouseMiddle)
				return ImGui::IsMouseDragging(ImGuiMouseButton_Middle);
			return false;
		}

		float slider(std::string& label, float value, float min, float max) {
			ImGui::SliderFloat(label.c_str(), &value, min, max);
			return value;
		}

		int sliderInt(std::string& label, int value, int min, int max) {
			ImGui::SliderInt(label.c_str(), &value, min, max);
			return value;
		}

		float getMouseDeltaX() {
			ImGuiIO& io = ImGui::GetIO();
			return io.MouseDelta.x;
		}

		float getMouseDeltaY() {
			ImGuiIO& io = ImGui::GetIO();
			return io.MouseDelta.y;
		}

		float getMouseDragDeltaX() { return ImGui::GetMouseDragDelta().x; }

		float getMouseDragDeltaY() { return ImGui::GetMouseDragDelta().y; }

		int getAvailableSizeX() { return ImGui::GetContentRegionAvail().x; }

		int getAvailableSizeY() { return ImGui::GetContentRegionAvail().y; }

		void disablePanelPadding(bool value) {
			// TODO
			// if (currentDockPanelExecution) {
			//	currentDockPanelExecution->setFlag(DockPanelFlag_PanelPadding,
			//	                                   value);
			//}
		}
	} // namespace StudioAPI
} // namespace Deer