#include "CharacterClassDatabaseEditor.hpp"
#include "../components/CharacterClassDatabase.hpp"
#include <OgreLogManager.h>
#include <cstring>

void CharacterClassDatabaseEditor::render(bool *open)
{
	ImGui::SetNextWindowPos(ImVec2(300, 100), ImGuiCond_FirstUseEver);
	ImGui::SetNextWindowSize(ImVec2(600, 600),
				 ImGuiCond_FirstUseEver);

	if (!ImGui::Begin("Character Class Database", open)) {
		ImGui::End();
		return;
	}

	if (ImGui::Button("Save to character_class.json")) {
		if (CharacterClassDatabase::saveToJson(
			    "character_class.json")) {
			Ogre::LogManager::getSingleton().logMessage(
				"Character class database saved.");
		}
	}
	ImGui::SameLine();
	if (ImGui::Button("Load from character_class.json")) {
		if (CharacterClassDatabase::loadFromJson(
			    "character_class.json")) {
			Ogre::LogManager::getSingleton().logMessage(
				"Character class database loaded.");
		}
	}

	ImGui::Separator();

	ImGui::TextDisabled("(?) Hover over fields for help.");
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Formulas support: level, current, base, value");
		ImGui::Text("Operators: + - * / ^");
		ImGui::Text("Functions: floor, ceil, min, max, clamp, sqrt, abs, round");
		ImGui::EndTooltip();
	}

	if (ImGui::BeginTabBar("CCTabs")) {
		if (ImGui::BeginTabItem("Stats")) {
			renderStatsTab();
			ImGui::EndTabItem();
		}
		if (ImGui::BeginTabItem("Skills")) {
			renderSkillsTab();
			ImGui::EndTabItem();
		}
		if (ImGui::BeginTabItem("Needs")) {
			renderNeedsTab();
			ImGui::EndTabItem();
		}
		if (ImGui::BeginTabItem("Classes")) {
			renderClassesTab();
			ImGui::EndTabItem();
		}
		ImGui::EndTabBar();
	}

	ImGui::End();
}

void CharacterClassDatabaseEditor::renderStatsTab()
{
	auto &db = CharacterClassDatabase::getSingleton();

	ImGui::Text("Add Stat");
	ImGui::InputText("Name", m_nameBuf, sizeof(m_nameBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Internal identifier, e.g. 'strength'");
		ImGui::EndTooltip();
	}
	ImGui::InputText("Display Name", m_displayBuf,
			 sizeof(m_displayBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Human-readable name shown in UI, e.g. 'Strength'");
		ImGui::EndTooltip();
	}
	const char *kindLabels[] = { "Attribute", "ResourcePool" };
	ImGui::Combo("Kind", &m_statKindIdx, kindLabels,
		     IM_ARRAYSIZE(kindLabels));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Attribute: permanent value (str, agi, dex)");
		ImGui::Text("ResourcePool: depletable current/max (hp, stamina, mana)");
		ImGui::EndTooltip();
	}
	ImGui::InputInt("Min", &m_intBuf);
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Minimum value (attribute) or pool max (resource pool)");
		ImGui::EndTooltip();
	}
	ImGui::InputInt("Max", &m_maxVal);
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Maximum value (attribute) or pool max cap (resource pool)");
		ImGui::EndTooltip();
	}
	if (ImGui::Button("Add Stat")) {
		CharacterClassDatabase::StatDef def;
		def.name = m_nameBuf;
		def.displayName = m_displayBuf;
		def.kind = (m_statKindIdx == 1) ?
				   CharacterClassDatabase::StatKind::ResourcePool :
				   CharacterClassDatabase::StatKind::Attribute;
		def.minValue = m_intBuf;
		def.maxValue = m_maxVal;
		if (!def.name.empty())
			db.addOrReplaceStat(def);
	}

	ImGui::Separator();
	ImGui::Text("Defined Stats");
	for (const auto &name : db.getStatNames()) {
		auto *def = db.findStat(name);
		if (!def)
			continue;
		ImGui::PushID(name.c_str());
		ImGui::Text("%s [%s]  min:%d max:%d", name.c_str(),
			    def->kind == CharacterClassDatabase::StatKind::ResourcePool ?
					    "Pool" : "Attr",
			    def->minValue, def->maxValue);
		ImGui::SameLine();
		if (ImGui::Button("Remove"))
			db.removeStat(name);

		if (ImGui::TreeNode("Edit")) {
			char buf[128];
			strncpy(buf, def->displayName.c_str(), sizeof(buf) - 1);
			buf[sizeof(buf) - 1] = '\0';
			if (ImGui::InputText("Display Name", buf,
					     sizeof(buf)))
				def->displayName = buf;
			int kindEdit = (def->kind ==
					CharacterClassDatabase::StatKind::ResourcePool) ?
						       1 :
						       0;
			if (ImGui::Combo("Kind", &kindEdit, kindLabels,
					 IM_ARRAYSIZE(kindLabels)))
				def->kind = (kindEdit == 1) ?
						    CharacterClassDatabase::StatKind::ResourcePool :
						    CharacterClassDatabase::StatKind::Attribute;
			if (ImGui::InputInt("Min", &def->minValue))
				;
			if (ImGui::InputInt("Max", &def->maxValue))
				;
			ImGui::TreePop();
		}
		ImGui::PopID();
	}
}

void CharacterClassDatabaseEditor::renderSkillsTab()
{
	auto &db = CharacterClassDatabase::getSingleton();

	ImGui::Text("Add Skill");
	ImGui::InputText("Name", m_nameBuf, sizeof(m_nameBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Internal identifier, e.g. 'smithing'");
		ImGui::EndTooltip();
	}
	ImGui::InputText("Display Name", m_displayBuf,
			 sizeof(m_displayBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Human-readable name shown in UI, e.g. 'Smithing'");
		ImGui::EndTooltip();
	}
	if (ImGui::Button("Add Skill")) {
		CharacterClassDatabase::SkillDef def;
		def.name = m_nameBuf;
		def.displayName = m_displayBuf;
		def.maxValue = 100;
		if (!def.name.empty())
			db.addOrReplaceSkill(def);
	}

	ImGui::Separator();
	ImGui::Text("Defined Skills");
	for (const auto &name : db.getSkillNames()) {
		auto *def = db.findSkill(name);
		if (!def)
			continue;
		ImGui::PushID(name.c_str());
		ImGui::Text("%s  max:%d", name.c_str(), def->maxValue);
		ImGui::SameLine();
		if (ImGui::Button("Remove"))
			db.removeSkill(name);

		if (ImGui::TreeNode("Edit")) {
			char buf[128];
			strncpy(buf, def->displayName.c_str(), sizeof(buf) - 1);
			buf[sizeof(buf) - 1] = '\0';
			if (ImGui::InputText("Display Name", buf,
					     sizeof(buf)))
				def->displayName = buf;
			ImGui::TreePop();
		}
		ImGui::PopID();
	}
}

void CharacterClassDatabaseEditor::renderNeedsTab()
{
	auto &db = CharacterClassDatabase::getSingleton();

	ImGui::Text("Add Need");
	ImGui::InputText("Name", m_nameBuf, sizeof(m_nameBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Internal identifier, e.g. 'hunger'");
		ImGui::EndTooltip();
	}
	ImGui::InputText("Display Name", m_displayBuf,
			 sizeof(m_displayBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Human-readable name shown in UI, e.g. 'Hunger'");
		ImGui::EndTooltip();
	}
	ImGui::InputFloat("Accumulation Rate", &m_floatBuf);
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Points added per second (range 0-1000)");
		ImGui::EndTooltip();
	}
	ImGui::InputInt("Low Threshold", &m_lowThreshold);
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Need value at or below which the bit is CLEARED");
		ImGui::Text("(need is satisfied / low priority)");
		ImGui::EndTooltip();
	}
	ImGui::InputInt("High Threshold", &m_highThreshold);
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Need value at or above which the bit is SET");
		ImGui::Text("(need is critical / action required)");
		ImGui::EndTooltip();
	}
	char bitName[128] = {};
	ImGui::InputText("Bit Name", bitName, sizeof(bitName));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("GOAP blackboard bit managed by this need");
		ImGui::Text("Set when need >= high threshold");
		ImGui::Text("Cleared when need <= low threshold");
		ImGui::Text("Between thresholds the bit keeps its current state (hysteresis)");
		ImGui::EndTooltip();
	}
	if (ImGui::Button("Add Need")) {
		CharacterClassDatabase::NeedDef def;
		def.name = m_nameBuf;
		def.displayName = m_displayBuf;
		def.accumulationRate = m_floatBuf;
		def.lowThreshold = m_lowThreshold;
		def.highThreshold = m_highThreshold;
		def.bitName = bitName;
		if (!def.name.empty())
			db.addOrReplaceNeed(def);
	}

	ImGui::Separator();
	ImGui::Text("Defined Needs");
	for (const auto &name : db.getNeedNames()) {
		auto *def = db.findNeed(name);
		if (!def)
			continue;
		ImGui::PushID(name.c_str());
		ImGui::Text("%s  rate:%.1f  low:%d  high:%d  bit:%s",
			    name.c_str(), def->accumulationRate,
			    def->lowThreshold, def->highThreshold,
			    def->bitName.empty() ? "-" :
						   def->bitName.c_str());
		ImGui::SameLine();
		if (ImGui::Button("Remove"))
			db.removeNeed(name);

		if (ImGui::TreeNode("Edit")) {
			char buf[128];
			strncpy(buf, def->displayName.c_str(), sizeof(buf) - 1);
			buf[sizeof(buf) - 1] = '\0';
			if (ImGui::InputText("Display Name", buf,
					     sizeof(buf)))
				def->displayName = buf;
			if (ImGui::InputFloat("Accumulation Rate",
					      &def->accumulationRate))
				;
			if (ImGui::InputInt("Low Threshold",
					    &def->lowThreshold))
				;
			if (ImGui::InputInt("High Threshold",
					    &def->highThreshold))
				;
			strncpy(buf, def->bitName.c_str(), sizeof(buf) - 1);
			buf[sizeof(buf) - 1] = '\0';
			if (ImGui::InputText("Bit Name", buf, sizeof(buf)))
				def->bitName = buf;
			ImGui::TreePop();
		}
		ImGui::PopID();
	}
}

void CharacterClassDatabaseEditor::renderClassesTab()
{
	auto &db = CharacterClassDatabase::getSingleton();

	ImGui::Text("Add Class");
	ImGui::InputText("Name", m_nameBuf, sizeof(m_nameBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Internal identifier, e.g. 'warrior'");
		ImGui::EndTooltip();
	}
	ImGui::InputTextMultiline("Description", m_descBuf,
				 sizeof(m_descBuf), ImVec2(0, 40));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Short description shown in UI tooltips");
		ImGui::EndTooltip();
	}
	ImGui::InputText("XP Formula", m_formulaBuf,
			 sizeof(m_formulaBuf));
	if (ImGui::IsItemHovered()) {
		ImGui::BeginTooltip();
		ImGui::Text("Variables: level, current, base, value");
		ImGui::Text("Ops: + - * / ^  |  Functions: floor, ceil, min, max, clamp, sqrt, abs, round");
		ImGui::Text("Example: '100 * level ^ 1.5' or 'floor(50 * sqrt(level))'");
		ImGui::EndTooltip();
	}
	if (ImGui::Button("Add Class")) {
		CharacterClassDatabase::ClassDef def;
		def.name = m_nameBuf;
		def.description = m_descBuf;
		def.xpForLevel = Formula(m_formulaBuf);
		if (!def.name.empty())
			db.addOrReplaceClass(def);
	}

	ImGui::Separator();
	ImGui::Text("Defined Classes");
	for (const auto &name : db.getClassNames()) {
		auto *cls = db.findClass(name);
		if (!cls)
			continue;
		ImGui::PushID(name.c_str());
		ImGui::Text("%s - %s", name.c_str(),
			    cls->description.c_str());
		ImGui::SameLine();
		if (ImGui::Button("Remove"))
			db.removeClass(name);

		if (ImGui::TreeNode("Edit")) {
			char buf[256];
			strncpy(buf, cls->description.c_str(), sizeof(buf) - 1);
			buf[sizeof(buf) - 1] = '\0';
			if (ImGui::InputTextMultiline("Description", buf,
						      sizeof(buf),
						      ImVec2(0, 40)))
				cls->description = buf;

			char formulaBuf[256];
			strncpy(formulaBuf,
				cls->xpForLevel.getExpression().c_str(),
				sizeof(formulaBuf) - 1);
			formulaBuf[sizeof(formulaBuf) - 1] = '\0';
			if (ImGui::InputText("XP Formula", formulaBuf,
					     sizeof(formulaBuf)))
				cls->xpForLevel = Formula(formulaBuf);

			ImGui::TreePop();
		}

		if (ImGui::TreeNode("Edit Base Stats")) {
			for (const auto &sname : db.getStatNames()) {
				int val = cls->baseStats.count(sname) ?
						  cls->baseStats.at(sname) :
						  0;
				if (ImGui::InputInt(sname.c_str(), &val))
					cls->baseStats[sname] = val;
			}
			ImGui::TreePop();
		}

		if (ImGui::TreeNode("Edit Base Skills")) {
			for (const auto &sname : db.getSkillNames()) {
				int val = cls->baseSkills.count(sname) ?
						  cls->baseSkills.at(sname) :
						  0;
				if (ImGui::InputInt(sname.c_str(), &val))
					cls->baseSkills[sname] = val;
			}
			ImGui::TreePop();
		}

		ImGui::PopID();
	}
}
