#include "InlineBehaviorTreeEditor.hpp"
#include "../lua/LuaBehaviorTreeApi.hpp"
#include <string.h>

std::vector<InlineBehaviorTreeEditor::EditOp> &
InlineBehaviorTreeEditor::getOps()
{
	static std::vector<EditOp> ops;
	return ops;
}

void InlineBehaviorTreeEditor::queueOp(EditOp op)
{
	getOps().push_back(op);
}

void InlineBehaviorTreeEditor::applyOps(BehaviorTreeNode &root)
{
	for (auto &op : getOps()) {
		BehaviorTreeNode *parent = op.parent ? op.parent : &root;
		switch (op.type) {
		case EditOp::Add:
			if (op.parent &&
			    op.childIndex <= op.parent->children.size())
				op.parent->children.insert(
					op.parent->children.begin() +
						op.childIndex,
					BehaviorTreeNode{ .type = op.addType });
			break;
		case EditOp::AddLuaNode:
			if (op.parent) {
				BehaviorTreeNode child;
				child.type = "luaTask";
				child.name = op.luaNodeName;
				op.parent->children.insert(
					op.parent->children.begin() +
						op.childIndex,
					child);
			}
			break;
		case EditOp::Remove:
			if (op.parent &&
			    op.childIndex < op.parent->children.size())
				op.parent->children.erase(
					op.parent->children.begin() +
					op.childIndex);
			break;
		case EditOp::Move:
			if (op.parent &&
			    op.childIndex < op.parent->children.size()) {
				size_t newIndex =
					(int)op.childIndex + op.moveDelta;
				if (newIndex >= 0 &&
				    newIndex < op.parent->children.size() &&
				    newIndex != op.childIndex) {
					auto child =
						op.parent
							->children[op.childIndex];
					op.parent->children.erase(
						op.parent->children.begin() +
						op.childIndex);
					op.parent->children.insert(
						op.parent->children.begin() +
							newIndex,
						child);
				}
			}
			break;
		}
	}
	getOps().clear();
}

static void makeLabel(const BehaviorTreeNode &node, char *buf, size_t sz)
{
	if (node.type == "debugPrint")
		snprintf(buf, sz, "[print] %s",
			 node.name.empty() ? "(no msg)" : node.name.c_str());
	else if (node.type == "setAnimationState")
		snprintf(buf, sz, "[anim] %s",
			 node.name.empty() ? "(no state)" : node.name.c_str());
	else if (node.type == "isAnimationEnded")
		snprintf(buf, sz, "[ended?] %s",
			 node.name.empty() ? "(no sm)" : node.name.c_str());
	else if (node.isLeaf())
		snprintf(buf, sz, "[%s] %s", node.type.c_str(),
			 node.name.empty() ? "(unnamed)" : node.name.c_str());
	else
		snprintf(buf, sz, "[%s]", node.type.c_str());
}

static ImVec4 typeColorVec(const char *type)
{
	if (!strcmp(type, "sequence"))
		return ImVec4(0.53f, 1.0f, 0.53f, 1.0f);
	if (!strcmp(type, "selector"))
		return ImVec4(1.0f, 1.0f, 0.53f, 1.0f);
	if (!strcmp(type, "invert"))
		return ImVec4(1.0f, 0.53f, 0.53f, 1.0f);
	if (!strcmp(type, "task"))
		return ImVec4(0.53f, 0.53f, 1.0f, 1.0f);
	if (!strcmp(type, "check"))
		return ImVec4(0.53f, 1.0f, 1.0f, 1.0f);
	if (!strcmp(type, "debugPrint"))
		return ImVec4(1.0f, 0.53f, 1.0f, 1.0f);
	if (!strcmp(type, "setAnimationState"))
		return ImVec4(1.0f, 0.67f, 0.27f, 1.0f);
	if (!strcmp(type, "isAnimationEnded"))
		return ImVec4(0.27f, 1.0f, 0.67f, 1.0f);
	if (!strcmp(type, "delay"))
		return ImVec4(1.0f, 0.84f, 0.0f, 1.0f);
	if (!strcmp(type, "teleportToChild"))
		return ImVec4(1.0f, 0.41f, 0.71f, 1.0f);
	if (!strcmp(type, "disablePhysics"))
		return ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
	if (!strcmp(type, "enablePhysics"))
		return ImVec4(0.3f, 0.8f, 0.3f, 1.0f);
	if (!strcmp(type, "sendEvent"))
		return ImVec4(1.0f, 0.6f, 0.0f, 1.0f);
	if (!strcmp(type, "luaTask"))
		return ImVec4(0.67f, 0.4f, 1.0f, 1.0f);
	return ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
}

// Helper: render a submenu of registered Lua node names.
// Returns true if a node was selected.
bool InlineBehaviorTreeEditor::renderLuaNodeSubmenu(BehaviorTreeNode *parent,
						    size_t childIndex)
{
	auto names = editScene::getRegisteredLuaNodeNames();
	if (names.empty()) {
		ImGui::MenuItem("(no Lua nodes registered)", nullptr, false,
				false);
		return false;
	}

	bool selected = false;
	for (const auto &name : names) {
		if (ImGui::MenuItem(name.c_str())) {
			queueOp(EditOp{ .type = EditOp::AddLuaNode,
					.parent = parent,
					.childIndex = childIndex,
					.luaNodeName = name });
			selected = true;
		}
	}
	return selected;
}

void InlineBehaviorTreeEditor::renderNode(BehaviorTreeNode &node,
					  BehaviorTreeNode *parent,
					  BehaviorTreeNode *&selected, int &id)
{
	int myId = id++;
	bool isLeaf = node.isLeaf();
	ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
				   ImGuiTreeNodeFlags_OpenOnDoubleClick;
	if (isLeaf)
		flags |= ImGuiTreeNodeFlags_Leaf |
			 ImGuiTreeNodeFlags_NoTreePushOnOpen;
	if (selected == &node)
		flags |= ImGuiTreeNodeFlags_Selected;

	char label[256];
	makeLabel(node, label, sizeof(label));

	ImGui::PushID(myId);
	ImGui::PushStyleColor(ImGuiCol_Text,
			      ImGui::ColorConvertFloat4ToU32(
				      typeColorVec(node.type.c_str())));
	bool open = ImGui::TreeNodeEx(label, flags);
	ImGui::PopStyleColor();

	if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
		selected = &node;

	if (ImGui::BeginPopupContextItem()) {
		if (ImGui::MenuItem("Add Child")) {
			queueOp(EditOp{ .type = EditOp::Add,
					.parent = &node,
					.childIndex = node.children.size(),
					.addType = "task" });
		}
		if (ImGui::MenuItem("Add Delay")) {
			queueOp(EditOp{ .type = EditOp::Add,
					.parent = &node,
					.childIndex = node.children.size(),
					.addType = "delay" });
		}
		if (ImGui::MenuItem("Add Teleport To Child")) {
			queueOp(EditOp{ .type = EditOp::Add,
					.parent = &node,
					.childIndex = node.children.size(),
					.addType = "teleportToChild" });
		}
		if (ImGui::MenuItem("Add Send Event")) {
			queueOp(EditOp{ .type = EditOp::Add,
					.parent = &node,
					.childIndex = node.children.size(),
					.addType = "sendEvent" });
		}
		if (ImGui::BeginMenu("Add Lua Task")) {
			renderLuaNodeSubmenu(&node, node.children.size());
			ImGui::EndMenu();
		}
		if (parent && ImGui::MenuItem("Remove")) {
			for (size_t i = 0; i < parent->children.size(); i++) {
				if (&parent->children[i] == &node) {
					queueOp(EditOp{ .type = EditOp::Remove,
							.parent = parent,
							.childIndex = i });
					if (selected == &node)
						selected = nullptr;
					break;
				}
			}
		}
		if (parent && ImGui::MenuItem("Move Up")) {
			for (size_t i = 0; i < parent->children.size(); i++) {
				if (&parent->children[i] == &node) {
					queueOp(EditOp{ .type = EditOp::Move,
							.parent = parent,
							.childIndex = i,
							.moveDelta = -1 });
					break;
				}
			}
		}
		if (parent && ImGui::MenuItem("Move Down")) {
			for (size_t i = 0; i < parent->children.size(); i++) {
				if (&parent->children[i] == &node) {
					queueOp(EditOp{ .type = EditOp::Move,
							.parent = parent,
							.childIndex = i,
							.moveDelta = 1 });
					break;
				}
			}
		}
		ImGui::EndPopup();
	}

	// Inline buttons
	if (!isLeaf) {
		ImGui::SameLine();
		if (ImGui::SmallButton("+")) {
			queueOp(EditOp{ .type = EditOp::Add,
					.parent = &node,
					.childIndex = node.children.size(),
					.addType = "task" });
		}
	}
	if (parent) {
		ImGui::SameLine();
		if (ImGui::SmallButton("-")) {
			for (size_t i = 0; i < parent->children.size(); i++) {
				if (&parent->children[i] == &node) {
					queueOp(EditOp{ .type = EditOp::Remove,
							.parent = parent,
							.childIndex = i });
					if (selected == &node)
						selected = nullptr;
					break;
				}
			}
		}
		ImGui::SameLine();
		if (ImGui::SmallButton("^")) {
			for (size_t i = 0; i < parent->children.size(); i++) {
				if (&parent->children[i] == &node) {
					queueOp(EditOp{ .type = EditOp::Move,
							.parent = parent,
							.childIndex = i,
							.moveDelta = -1 });
					break;
				}
			}
		}
		ImGui::SameLine();
		if (ImGui::SmallButton("v")) {
			for (size_t i = 0; i < parent->children.size(); i++) {
				if (&parent->children[i] == &node) {
					queueOp(EditOp{ .type = EditOp::Move,
							.parent = parent,
							.childIndex = i,
							.moveDelta = 1 });
					break;
				}
			}
		}
	}

	if (!isLeaf && open) {
		for (auto &child : node.children)
			renderNode(child, &node, selected, id);
		ImGui::TreePop();
	}
	ImGui::PopID();
}

void InlineBehaviorTreeEditor::renderProps(BehaviorTreeNode *node)
{
	if (!node)
		return;
	const char *types[] = { "sequence",
				"selector",
				"invert",
				"task",
				"check",
				"debugPrint",
				"setAnimationState",
				"isAnimationEnded",
				"setBit",
				"checkBit",
				"setValue",
				"checkValue",
				"blackboardDump",
				"delay",
				"teleportToChild",
				"disablePhysics",
				"enablePhysics",
				"sendEvent",
				"luaTask" };
	int current = 0;
	for (int i = 0; i < IM_ARRAYSIZE(types); i++) {
		if (node->type == types[i]) {
			current = i;
			break;
		}
	}
	if (ImGui::Combo("Type", &current, types, IM_ARRAYSIZE(types))) {
		node->type = types[current];
	}

	if (node->isLeaf()) {
		// For luaTask nodes, show a dropdown of registered Lua nodes
		if (node->type == "luaTask") {
			auto names = editScene::getRegisteredLuaNodeNames();
			if (names.empty()) {
				ImGui::TextDisabled("No Lua nodes registered");
			} else {
				int currentIdx = -1;
				for (size_t i = 0; i < names.size(); i++) {
					if (names[i] == node->name) {
						currentIdx = (int)i;
						break;
					}
				}
				std::vector<const char *> cstrs;
				cstrs.reserve(names.size());
				for (const auto &n : names)
					cstrs.push_back(n.c_str());

				if (ImGui::Combo("Lua Node", &currentIdx,
						 cstrs.data(),
						 (int)cstrs.size())) {
					if (currentIdx >= 0 &&
					    currentIdx < (int)names.size())
						node->name = names[currentIdx];
				}
				if (ImGui::IsItemHovered())
					ImGui::SetTooltip(
						"Select a registered Lua node handler");
			}
		} else {
			char buf[256];
			snprintf(buf, sizeof(buf), "%s", node->name.c_str());

			const char *label = "Name";
			const char *hint = nullptr;
			if (node->type == "debugPrint") {
				label = "Message";
				hint = "Text to print when node runs";
			} else if (node->type == "setAnimationState") {
				label = "State (SM/State)";
				hint = "Format: stateMachineName/stateName";
			} else if (node->type == "isAnimationEnded") {
				label = "State Machine";
				hint = "Name of state machine to check";
			} else if (node->type == "setBit") {
				label = "Bit";
				hint = "Bit index or name";
			} else if (node->type == "checkBit") {
				label = "Bit";
				hint = "Bit index or name to check";
			} else if (node->type == "setValue") {
				label = "Key";
				hint = "Variable name";
			} else if (node->type == "checkValue") {
				label = "Key";
				hint = "Variable name to check";
			} else if (node->type == "blackboardDump") {
				label = "Label";
				hint = "Optional label prefix";
			} else if (node->type == "teleportToChild") {
				label = "Child Name";
				hint = "Name of child entity under Smart Object to teleport to";
			} else if (node->type == "sendEvent") {
				label = "Event Name";
				hint = "Name of the event to send via EventBus";
			}

			if (ImGui::InputText(label, buf, sizeof(buf)))
				node->name = buf;
			if (hint && ImGui::IsItemHovered())
				ImGui::SetTooltip("%s", hint);
		}

		// Params input for nodes that need it
		if (node->type == "setBit" || node->type == "setValue" ||
		    node->type == "checkValue" || node->type == "delay" ||
		    node->type == "sendEvent" || node->type == "luaTask") {
			char pbuf[256];
			snprintf(pbuf, sizeof(pbuf), "%s",
				 node->params.c_str());
			const char *phint = nullptr;
			if (node->type == "setBit")
				phint = "1 or 0 (default 1)";
			else if (node->type == "setValue")
				phint = "Value: 5, 3.14, or 1,2,3";
			else if (node->type == "checkValue")
				phint = "Comparison: == 5, > 3.14, != 1,2,3";
			else if (node->type == "delay")
				phint = "Duration in seconds (float, default 1.0)";
			else if (node->type == "sendEvent")
				phint = "Event params: key=val key2=val2 ...";
			else if (node->type == "luaTask")
				phint = "Parameters: key=val,key2=val2 passed to Lua function";
			if (ImGui::InputText("Params", pbuf, sizeof(pbuf)))
				node->params = pbuf;
			if (phint && ImGui::IsItemHovered())
				ImGui::SetTooltip("%s", phint);
		}
	}
}

bool InlineBehaviorTreeEditor::render(BehaviorTreeNode &root)
{
	bool modified = false;
	static BehaviorTreeNode *s_selected = nullptr;
	static BehaviorTreeNode *s_lastRoot = nullptr;

	// Clear selection when switching to a different tree
	if (&root != s_lastRoot) {
		s_selected = nullptr;
		s_lastRoot = &root;
	}

	BehaviorTreeNode *selected = s_selected;
	int id = 0;
	getOps().clear();

	if (root.children.empty() && root.type == "task" && root.name.empty()) {
		ImGui::TextDisabled("(empty - right-click to add nodes)");
		if (ImGui::Button("Add Root Child")) {
			queueOp(EditOp{ .type = EditOp::Add,
					.parent = &root,
					.childIndex = 0,
					.addType = "task" });
			modified = true;
		}
	} else {
		renderNode(root, nullptr, selected, id);
	}

	bool hadEdits = !getOps().empty();
	if (hadEdits) {
		applyOps(root);
		modified = true;
		// Tree structure changed — selected pointer may be invalid
		selected = nullptr;
	}

	s_selected = selected;

	if (selected) {
		ImGui::Separator();
		renderProps(selected);
	}

	return modified;
}
