#include "ActionDatabaseEditor.hpp"
#include "GoapBlackboardEditor.hpp"
#include "InlineBehaviorTreeEditor.hpp"
#include <imgui.h>

bool ActionDatabaseEditor::renderComponent(flecs::entity entity,
					   ActionDatabaseComponent &db)
{
	bool modified = false;
	(void)entity;
	ImGui::PushID("ActionDatabase");

	if (ImGui::CollapsingHeader("Bit Names",
				    ImGuiTreeNodeFlags_DefaultOpen))
		modified |= renderBitNames();

	if (ImGui::CollapsingHeader("Actions", ImGuiTreeNodeFlags_DefaultOpen))
		renderActions(db);

	if (ImGui::CollapsingHeader("Goals", ImGuiTreeNodeFlags_DefaultOpen))
		renderGoals(db);

	ImGui::PopID();

	// Sync to singleton if modified
	if (modified)
		db.syncToSingleton();

	return modified;
}

bool ActionDatabaseEditor::renderBitNames()
{
	bool changed = false;
	ImGui::Indent();
	ImGui::Text("Name bits for use in preconditions/effects:");
	ImGui::TextDisabled("(empty slots show as numbers in blackboards)");

	for (int i = 0; i < 64; i++) {
		const char *name = GoapBlackboard::getBitName(i);
		char buf[64];
		snprintf(buf, sizeof(buf), "%s", name ? name : "");

		ImGui::PushID(i);
		char label[16];
		snprintf(label, sizeof(label), "%2d", i);
		ImGui::SetNextItemWidth(-FLT_MIN);
		if (ImGui::InputText(label, buf, sizeof(buf))) {
			GoapBlackboard::setBitName(i, buf);
			changed = true;
		}
		ImGui::PopID();
	}

	ImGui::Unindent();
	return changed;
}

void ActionDatabaseEditor::renderActions(ActionDatabaseComponent &db)
{
	ImGui::Indent();

	for (size_t i = 0; i < db.actions.size(); i++) {
		ImGui::PushID((int)i);
		bool isSelected = (m_selectedAction == (int)i);

		char label[256];
		snprintf(label, sizeof(label), "%s (cost: %d)",
			 db.actions[i].name.c_str(), db.actions[i].cost);

		if (ImGui::Selectable(label, isSelected))
			m_selectedAction = isSelected ? -1 : (int)i;

		ImGui::SameLine();
		if (ImGui::SmallButton("X")) {
			db.actions.erase(db.actions.begin() + i);
			db.syncToSingleton();
			if (m_selectedAction == (int)i)
				m_selectedAction = -1;
			else if (m_selectedAction > (int)i)
				m_selectedAction--;
			ImGui::PopID();
			break;
		}
		ImGui::PopID();
	}

	if (ImGui::Button("Add Action")) {
		char newName[64];
		snprintf(newName, sizeof(newName), "Action_%zu",
			 db.actions.size());
		db.actions.emplace_back(newName);
		db.syncToSingleton();
		m_selectedAction = (int)db.actions.size() - 1;
	}

	if (m_selectedAction >= 0 &&
	    m_selectedAction < (int)db.actions.size()) {
		renderActionEditor(db.actions[m_selectedAction]);
		db.syncToSingleton();
	}

	ImGui::Unindent();
}

void ActionDatabaseEditor::renderGoals(ActionDatabaseComponent &db)
{
	ImGui::Indent();

	for (size_t i = 0; i < db.goals.size(); i++) {
		ImGui::PushID((int)i + 10000);
		bool isSelected = (m_selectedGoal == (int)i);

		char label[256];
		snprintf(label, sizeof(label), "%s (priority: %d)",
			 db.goals[i].name.c_str(), db.goals[i].priority);

		if (ImGui::Selectable(label, isSelected))
			m_selectedGoal = isSelected ? -1 : (int)i;

		ImGui::SameLine();
		if (ImGui::SmallButton("X")) {
			db.goals.erase(db.goals.begin() + i);
			db.syncToSingleton();
			if (m_selectedGoal == (int)i)
				m_selectedGoal = -1;
			else if (m_selectedGoal > (int)i)
				m_selectedGoal--;
			ImGui::PopID();
			break;
		}
		ImGui::PopID();
	}

	if (ImGui::Button("Add Goal")) {
		char newName[64];
		snprintf(newName, sizeof(newName), "Goal_%zu", db.goals.size());
		db.goals.emplace_back(newName);
		db.syncToSingleton();
		m_selectedGoal = (int)db.goals.size() - 1;
	}

	if (m_selectedGoal >= 0 && m_selectedGoal < (int)db.goals.size()) {
		renderGoalEditor(db.goals[m_selectedGoal]);
		db.syncToSingleton();
	}

	ImGui::Unindent();
}

void ActionDatabaseEditor::renderActionEditor(GoapAction &action)
{
	ImGui::Separator();
	ImGui::Text("Action: %s", action.name.c_str());

	char nameBuf[256];
	snprintf(nameBuf, sizeof(nameBuf), "%s", action.name.c_str());
	if (ImGui::InputText("Name", nameBuf, sizeof(nameBuf)))
		action.name = nameBuf;

	if (ImGui::InputInt("Cost", &action.cost))
		action.cost = action.cost < 0 ? 0 : action.cost;

	char btNameBuf[256];
	snprintf(btNameBuf, sizeof(btNameBuf), "%s",
		 action.behaviorTreeName.c_str());
	if (ImGui::InputText("Behavior Tree Name", btNameBuf,
			     sizeof(btNameBuf)))
		action.behaviorTreeName = btNameBuf;

	if (ImGui::TreeNode("Behavior Tree")) {
		if (InlineBehaviorTreeEditor::render(action.behaviorTree))
			/* modified */;
		ImGui::TreePop();
	}

	if (ImGui::TreeNode("Preconditions")) {
		GoapBlackboardEditor::render(action.preconditions, "precond");
		ImGui::TreePop();
	}

	// Precondition mask editor
	ImGui::Separator();
	ImGui::Text("Precondition Mask (bits to check)");
	for (int i = 0; i < 64; i += 8) {
		for (int j = 0; j < 8 && (i + j) < 64; j++) {
			int bit = i + j;
			bool enabled = (action.preconditionMask >> bit) & 1ULL;
			char label[8];
			snprintf(label, sizeof(label), "%d", bit);
			if (j > 0)
				ImGui::SameLine();
			if (ImGui::Checkbox(label, &enabled)) {
				if (enabled)
					action.preconditionMask |=
						(1ULL << bit);
				else
					action.preconditionMask &=
						~(1ULL << bit);
			}
		}
	}
	ImGui::Text("Mask: 0x%016llX",
		    (unsigned long long)action.preconditionMask);
	ImGui::SameLine();
	if (ImGui::SmallButton("Set All##mask")) {
		action.preconditionMask = ~0ULL;
	}
	ImGui::SameLine();
	if (ImGui::SmallButton("Clear All##mask")) {
		action.preconditionMask = 0ULL;
	}

	if (ImGui::TreeNode("Effects")) {
		GoapBlackboardEditor::render(action.effects, "effects");
		ImGui::TreePop();
	}
}

void ActionDatabaseEditor::renderGoalEditor(GoapGoal &goal)
{
	ImGui::Separator();
	ImGui::Text("Goal: %s", goal.name.c_str());

	char nameBuf[256];
	snprintf(nameBuf, sizeof(nameBuf), "%s", goal.name.c_str());
	if (ImGui::InputText("Name", nameBuf, sizeof(nameBuf)))
		goal.name = nameBuf;

	if (ImGui::InputInt("Priority", &goal.priority))
		goal.priority = goal.priority < 0 ? 0 : goal.priority;

	char condBuf[512];
	snprintf(condBuf, sizeof(condBuf), "%s", goal.condition.c_str());
	if (ImGui::InputText("Condition", condBuf, sizeof(condBuf)))
		goal.condition = condBuf;
	if (ImGui::IsItemHovered()) {
		ImGui::SetTooltip(
			"Optional expression: health > 20 && hunger < 50");
	}

	if (ImGui::TreeNode("Target Blackboard")) {
		GoapBlackboardEditor::render(goal.target, "target");
		ImGui::TreePop();
	}
}
