#pragma once

#include "dialogue/DialogueDatabase.h"
#include "external/nlohmann/json.hpp"
#include <functional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace ZL::Dialogue {

class DialogueRuntime {
public:
	using json = nlohmann::json;

	void setDatabase(const DialogueDatabase* value);

	bool startDialogue(const std::string& dialogueId);
	bool startStandaloneCutscene(const std::string& cutsceneId);
	void setOnCutsceneFinished(std::function<void(const std::string&)> cb);
	void stop();

	void update(int deltaMs);

	bool isActive() const { return mode != Mode::Inactive; }
	bool isInChoice() const { return mode == Mode::WaitingForChoice; }
	bool isPlayingCutscene() const { return mode == Mode::PlayingCutscene; }

	void confirmAdvance();
	void moveSelection(int delta);
	void selectChoice(int index);
	bool canSkipCurrentCutscene() const;
	void skipCurrentCutscene();

	const PresentationModel& getPresentation() const { return presentation; }

	void setFlag(const std::string& name, int value);
	int getFlag(const std::string& name) const;

	json buildSaveState() const;
	bool restoreSaveState(const json& state);

private:
	enum class Mode {
		Inactive,
		PresentingLine,
		WaitingForChoice,
		PlayingCutscene
	};

	std::function<void(const std::string&)> onCutsceneFinished;
	std::string activeCutsceneId;

	const DialogueDatabase* database = nullptr;
	const DialogueDefinition* activeDialogue = nullptr;
	const StaticCutsceneDefinition* activeCutscene = nullptr;

	std::unordered_map<std::string, int> flags;
	std::unordered_set<std::string> consumedChoices;

	std::string currentNodeId;
	std::string pendingNodeAfterCutscene;

	std::vector<Choice> visibleChoices;
	PresentationModel presentation;
	Mode mode = Mode::Inactive;

	int selectedChoice = -1;
	float revealCharacters = 0.0f;
	float revealSpeedCharsPerSecond = 52.0f;
	
	int currentCutsceneLine = -1;
	int cutsceneTimerMs = 0;
	int cutsceneElapsedMs = 0;
	int cutsceneTotalDurationMs = 0;
	int cutsceneContentDurationMs = 0;

	std::string currentCutsceneBackground;

	bool evaluateConditions(const std::vector<Condition>& conditions) const;
	void applyEffects(const std::vector<Effect>& effects);

	bool enterNode(const std::string& nodeId);
	void presentLine(const Node& node);
	void presentChoices(const Node& node);
	void startCutscene(const std::string& cutsceneId, const std::string& nextNodeAfterCutscene);
	void finishCutscene();
	void syncCutsceneLineToElapsedTime();

	void advanceCutsceneLine();
	void refreshCutscenePresentation();
	CutsceneCameraBlendState evaluateCutsceneCameraBlend() const;
	std::vector<PresentedCutsceneImage> evaluateCutsceneImages() const;

	static float applyEasing(EasingType easing, float t);
	static int computeFallbackCutsceneDurationMs(const std::string& text);
	static int computeCameraTrackDurationMs(const StaticCutsceneDefinition& cutscene);
};

} // namespace ZL::Dialogue
