#ifndef EDITSCENE_DIALOGUESYSTEM_HPP
#define EDITSCENE_DIALOGUESYSTEM_HPP
#pragma once

#include <Ogre.h>
#include <imgui.h>
#include <functional>
#include <string>
#include <vector>

#include "EventBus.hpp"

class EditorApp;

/**
 * Singleton dialogue system.
 *
 * Manages a visual-novel style dialogue box that can be triggered
 * via direct API, Lua scripts, or EventBus events ("dialogue_show",
 * "dialogue_hide").
 *
 * Visual settings are loaded from dialogue.json at startup and can be
 * tweaked from the editor or via the API.
 */
class DialogueSystem {
public:
	struct Settings {
		Ogre::String fontName = "Jupiteroid-Regular.ttf";
		float fontSize = 24.0f;
		float speakerFontSize = 20.0f;
		float backgroundOpacity = 0.85f;
		float boxHeightFraction = 0.25f;
		float boxPositionFraction = 0.75f;

		bool loadFromJson(const std::string &path);
		bool saveToJson(const std::string &path) const;
	};

	static DialogueSystem &getInstance();

	/** Must be called before any font operations. */
	void init(EditorApp *editorApp);

	/** Load visual settings from JSON (defaults used if file missing). */
	bool loadSettings(const std::string &path = "dialogue.json");
	/** Save visual settings to JSON. */
	bool saveSettings(const std::string &path = "dialogue.json") const;

	const Settings &getSettings() const
	{
		return m_settings;
	}
	Settings &getSettingsRef()
	{
		return m_settings;
	}
	void setSettings(const Settings &s)
	{
		m_settings = s;
		m_fontLoaded = false;
	}

	/* --- Runtime API --- */

	/** Show dialogue with narration text and optional choices. */
	void show(const Ogre::String &text,
		  const std::vector<Ogre::String> &choices = {},
		  const Ogre::String &speaker = "");
	/** Hide the dialogue immediately. */
	void hide();
	/** Click-to-progress (no choices mode). */
	void progress();
	/** Select a choice by 1-based index. */
	void selectChoice(int index);
	/** True if dialogue is currently on screen. */
	bool isActive() const;

	/* --- Editor preview --- */

	void setEditorPreviewEnabled(bool enabled);
	bool isEditorPreviewEnabled() const;

	/* --- Rendering --- */

	/** Update and render the dialogue box. Must be inside ImGui frame. */
	void update(float deltaTime);

	/** Pre-load fonts before ImGui NewFrame. */
	void prepareFont();

	/* --- Callbacks --- */

	std::function<void(int)> onChoiceSelected;
	std::function<void()> onDismissed;
	std::function<void()> onShow;

private:
	DialogueSystem();
	~DialogueSystem();

	DialogueSystem(const DialogueSystem &) = delete;
	DialogueSystem &operator=(const DialogueSystem &) = delete;

	void renderDialogueBox();
	void ensureFontLoaded(const Ogre::String &fontName, float fontSize,
			      float speakerFontSize);

	EditorApp *m_editorApp = nullptr;

	enum class State { Idle, Showing, AwaitingChoice };
	State m_state = State::Idle;
	Ogre::String m_text;
	Ogre::String m_speaker;
	std::vector<Ogre::String> m_choices;

	Settings m_settings;
	bool m_editorPreviewEnabled = false;

	bool m_fontLoaded = false;
	Ogre::String m_currentFontName;
	float m_currentFontSize = 0.0f;
	float m_currentSpeakerFontSize = 0.0f;
	ImFont *m_dialogueFont = nullptr;
	ImFont *m_speakerFont = nullptr;

	EventBus::ListenerId m_showListenerId = 0;
	EventBus::ListenerId m_hideListenerId = 0;
};

#endif // EDITSCENE_DIALOGUESYSTEM_HPP
