#include "dialogue/DialogueDatabase.h"

#include "utils/Utils.h"
#include <iostream>

namespace ZL
{
	extern const char* CONST_ZIP_FILE;
}

namespace ZL::Dialogue {

NodeType DialogueDatabase::parseNodeType(const std::string& value) {
	if (value == "Choice") return NodeType::Choice;
	if (value == "Condition") return NodeType::Condition;
	if (value == "SetFlag") return NodeType::SetFlag;
	if (value == "Jump") return NodeType::Jump;
	if (value == "End") return NodeType::End;
	if (value == "CutsceneStart") return NodeType::CutsceneStart;
	return NodeType::Line;
}

ChoiceKind DialogueDatabase::parseChoiceKind(const std::string& value) {
	if (value == "Optional") return ChoiceKind::Optional;
	if (value == "Exit") return ChoiceKind::Exit;
	return ChoiceKind::Main;
}

ComparisonOp DialogueDatabase::parseComparisonOp(const std::string& value) {
	if (value == "==" || value == "Equals") return ComparisonOp::Equals;
	if (value == "!=" || value == "NotEquals") return ComparisonOp::NotEquals;
	if (value == ">=" || value == "GreaterOrEqual") return ComparisonOp::GreaterOrEqual;
	if (value == "<=" || value == "LessOrEqual") return ComparisonOp::LessOrEqual;
	return ComparisonOp::Exists;
}

EasingType DialogueDatabase::parseEasingType(const std::string& value) {
	if (value == "EaseInSine") return EasingType::EaseInSine;
	if (value == "EaseOutSine") return EasingType::EaseOutSine;
	if (value == "EaseInOutSine") return EasingType::EaseInOutSine;
	if (value == "EaseInQuad") return EasingType::EaseInQuad;
	if (value == "EaseOutQuad") return EasingType::EaseOutQuad;
	if (value == "EaseInOutQuad") return EasingType::EaseInOutQuad;
	if (value == "EaseInCubic") return EasingType::EaseInCubic;
	if (value == "EaseOutCubic") return EasingType::EaseOutCubic;
	if (value == "EaseInOutCubic") return EasingType::EaseInOutCubic;
	return EasingType::Linear;
}

CutsceneAnchor DialogueDatabase::parseCutsceneAnchor(const std::string& value) {
	if (value == "TopLeft") return CutsceneAnchor::TopLeft;
	if (value == "TopRight") return CutsceneAnchor::TopRight;
	if (value == "BottomRight") return CutsceneAnchor::BottomRight;
	if (value == "BottomLeft") return CutsceneAnchor::BottomLeft;
	if (value == "Custom") return CutsceneAnchor::Custom;
	return CutsceneAnchor::Center;
}

Condition DialogueDatabase::parseCondition(const json& j) {
	Condition c;
	c.flag = j.value("flag", "");
	c.op = parseComparisonOp(j.value("op", "Exists"));
	c.value = j.value("value", 1);
	return c;
}

Effect DialogueDatabase::parseEffect(const json& j) {
	Effect e;
	e.flag = j.value("flag", "");
	e.value = j.value("value", 1);
	e.relative = j.value("relative", false);
	return e;
}

Choice DialogueDatabase::parseChoice(const json& j) {
	Choice c;
	c.id = j.value("id", "");
	c.text = j.value("text", "");
	c.next = j.value("next", "");
	c.kind = parseChoiceKind(j.value("kind", "Main"));
	c.consumeOnce = j.value("consumeOnce", false);

	if (j.contains("conditions") && j["conditions"].is_array()) {
		for (const auto& item : j["conditions"]) {
			c.conditions.push_back(parseCondition(item));
		}
	}
	if (j.contains("effects") && j["effects"].is_array()) {
		for (const auto& item : j["effects"]) {
			c.effects.push_back(parseEffect(item));
		}
	}
	return c;
}

Node DialogueDatabase::parseNode(const json& j) {
	Node node;
	node.id = j.value("id", "");
	node.type = parseNodeType(j.value("type", "Line"));
	node.speaker = j.value("speaker", "");
	node.text = j.value("text", "");
	node.portrait = j.value("portrait", "");
	node.next = j.value("next", "");
	node.trueNext = j.value("trueNext", "");
	node.falseNext = j.value("falseNext", "");
	node.cutsceneId = j.value("cutsceneId", "");

	if (j.contains("conditions") && j["conditions"].is_array()) {
		for (const auto& item : j["conditions"]) {
			node.conditions.push_back(parseCondition(item));
		}
	}
	if (j.contains("effects") && j["effects"].is_array()) {
		for (const auto& item : j["effects"]) {
			node.effects.push_back(parseEffect(item));
		}
	}
	if (j.contains("choices") && j["choices"].is_array()) {
		for (const auto& item : j["choices"]) {
			node.choices.push_back(parseChoice(item));
		}
	}

	return node;
}

DialogueDefinition DialogueDatabase::parseDialogue(const json& j) {
	DialogueDefinition result;
	result.id = j.value("id", "");
	result.displayName = j.value("displayName", result.id);
	result.startNode = j.value("start", "");

	if (j.contains("nodes") && j["nodes"].is_array()) {
		for (const auto& item : j["nodes"]) {
			Node node = parseNode(item);
			if (!node.id.empty()) {
				result.nodes[node.id] = std::move(node);
			}
		}
	}

	return result;
}

CutsceneLine DialogueDatabase::parseCutsceneLine(const json& j) {
	CutsceneLine line;
	line.speaker = j.value("speaker", "");
	line.text = j.value("text", "");
	line.portrait = j.value("portrait", "");
	line.sfx = j.value("sfx", "");
	line.background = j.value("background", "");
	line.backgroundWidth = j.value("backgroundWidth", 0);
	line.backgroundHeight = j.value("backgroundHeight", 0);
	line.durationMs = j.value("durationMs", 0);
	line.waitForConfirm = j.value("waitForConfirm", false);
	return line;
}

CutsceneCameraPose DialogueDatabase::parseCutsceneCameraPose(const json& j) {
	CutsceneCameraPose pose;
	pose.anchor = parseCutsceneAnchor(j.value("anchor", "Center"));
	pose.centerX = j.value("centerX", 0.5f);
	pose.centerY = j.value("centerY", 0.5f);
	pose.zoom = j.value("zoom", 1.0f);
	pose.rotationDeg = j.value("rotationDeg", 0.0f);
	return pose;
}

CutsceneCameraSegment DialogueDatabase::parseCutsceneCameraSegment(const json& j) {
	CutsceneCameraSegment segment;
	segment.durationMs = j.value("durationMs", 0);
	segment.easing = parseEasingType(j.value("easing", "EaseInOutSine"));
	if (j.contains("from") && j["from"].is_object()) {
		segment.from = parseCutsceneCameraPose(j["from"]);
	}
	if (j.contains("to") && j["to"].is_object()) {
		segment.to = parseCutsceneCameraPose(j["to"]);
	}
	else {
		segment.to = segment.from;
	}
	return segment;
}

CutsceneImageCue DialogueDatabase::parseCutsceneImageCue(const json& j) {
	CutsceneImageCue cue;
	cue.path = j.value("path", "");
	cue.startMs = j.value("startMs", 0);
	cue.endMs = j.value("endMs", 0);
	cue.fadeInMs = j.value("fadeInMs", 0);
	cue.fadeOutMs = j.value("fadeOutMs", 0);
	return cue;
}

StaticCutsceneDefinition DialogueDatabase::parseCutscene(const json& j) {
	StaticCutsceneDefinition cutscene;
	cutscene.id = j.value("id", "");
	cutscene.background = j.value("background", "");
	cutscene.backgroundWidth = j.value("backgroundWidth", 1280);
	cutscene.backgroundHeight = j.value("backgroundHeight", 720);
	cutscene.music = j.value("music", "");
	cutscene.skippable = j.value("skippable", true);
	cutscene.durationMs = j.value("durationMs", 0);
	cutscene.fadeOutMs = j.value("fadeOutMs", 0);
	cutscene.fadeInMs = j.value("fadeInMs", 0);
	cutscene.endFadeOutMs = j.value("endFadeOutMs", 0);
	cutscene.endFadeInMs = j.value("endFadeInMs", 0);

	if (j.contains("cameraTrack") && j["cameraTrack"].is_array()) {
		for (const auto& item : j["cameraTrack"]) {
			cutscene.cameraTrack.push_back(parseCutsceneCameraSegment(item));
		}
	}

	if (j.contains("images") && j["images"].is_array()) {
		for (const auto& item : j["images"]) {
			cutscene.images.push_back(parseCutsceneImageCue(item));
		}
	}

	if (j.contains("lines") && j["lines"].is_array()) {
		for (const auto& item : j["lines"]) {
			cutscene.lines.push_back(parseCutsceneLine(item));
		}
	}

	return cutscene;
}

bool DialogueDatabase::loadFromFile(const std::string& path) {
	dialogues.clear();
	cutscenes.clear();

	std::string raw;
	try {
		if (strlen(CONST_ZIP_FILE) == 0) {
			raw = readTextFile(path);
		}
		else {
			auto buf = readFileFromZIP(path, CONST_ZIP_FILE);
			if (buf.empty()) {
				std::cerr << "UiManager: failed to read " << path << " from zip " << CONST_ZIP_FILE << std::endl;
				throw std::runtime_error("Failed to load UI file: " + path);
			}
			raw.assign(buf.begin(), buf.end());
		}
	}
	catch (const std::exception& e) {
		std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl;
		throw std::runtime_error("Failed to load UI file: " + path);
	}

	json root;
	try {
		root = json::parse(raw);
	}
	catch (const std::exception& e) {
		std::cerr << "[dialogue] JSON parse error in " << path << ": " << e.what() << "\n";
		return false;
	}

	if (root.contains("dialogues") && root["dialogues"].is_array()) {
		for (const auto& item : root["dialogues"]) {
			DialogueDefinition dialogue = parseDialogue(item);
			if (!dialogue.id.empty()) {
				dialogues[dialogue.id] = std::move(dialogue);
			}
		}
	}

	if (root.contains("cutscenes") && root["cutscenes"].is_array()) {
		for (const auto& item : root["cutscenes"]) {
			StaticCutsceneDefinition cutscene = parseCutscene(item);
			if (!cutscene.id.empty()) {
				cutscenes[cutscene.id] = std::move(cutscene);
			}
		}
	}

	return !dialogues.empty();
}

const DialogueDefinition* DialogueDatabase::findDialogue(const std::string& id) const {
	auto it = dialogues.find(id);
	return (it != dialogues.end()) ? &it->second : nullptr;
}

const StaticCutsceneDefinition* DialogueDatabase::findCutscene(const std::string& id) const {
	auto it = cutscenes.find(id);
	return (it != cutscenes.end()) ? &it->second : nullptr;
}

} // namespace ZL::Dialogue
