#ifndef EDITSCENE_ANIMATIONTREEREGISTRY_HPP
#define EDITSCENE_ANIMATIONTREEREGISTRY_HPP
#pragma once

#include "../components/AnimationTree.hpp"
#include <Ogre.h>
#include <nlohmann/json.hpp>
#include <string>
#include <unordered_map>
#include <vector>

/**
 * Global animation tree registry.
 *
 * Holds named animation tree definitions.  Each tree is referenced by name
 * from AnimationTreeComponent.  Trees are edited in the Animation Tree
 * Registry window (Tools menu) and persisted to animation_tree.json.
 */
class AnimationTreeRegistry {
public:
	static AnimationTreeRegistry &getSingleton();
	static AnimationTreeRegistry *getSingletonPtr();

private:
	static AnimationTreeRegistry *ms_singleton;

public:
	struct AnimationTreeDefinition {
		Ogre::String name;
		Ogre::String skeletonSource;
		AnimationTreeNode root;
		uint64_t version = 1;
	};

	AnimationTreeRegistry();
	~AnimationTreeRegistry() = default;

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

	/* ------------------------------------------------------------------ */
	/*  Life-cycle                                                        */
	/* ------------------------------------------------------------------ */
	void initialize();

	/* ------------------------------------------------------------------ */
	/*  Definitions                                                       */
	/* ------------------------------------------------------------------ */
	bool registerTree(const AnimationTreeDefinition &def);
	bool removeTree(const Ogre::String &name);

	AnimationTreeDefinition *findTree(const Ogre::String &name);
	const AnimationTreeDefinition *findTree(const Ogre::String &name) const;

	const std::unordered_map<Ogre::String, AnimationTreeDefinition> &
	getTrees() const
	{
		return m_trees;
	}

	std::vector<Ogre::String> getTreeNames() const;

	bool hasTree(const Ogre::String &name) const
	{
		return m_trees.find(name) != m_trees.end();
	}

	/* Generate a unique name from a base prefix. */
	Ogre::String generateUniqueName(const Ogre::String &base) const;

	/* Bump version and auto-save. */
	void markModified(const Ogre::String &name);

	/* ------------------------------------------------------------------ */
	/*  Skeleton source helpers                                           */
	/* ------------------------------------------------------------------ */
	struct SkeletonSource {
		Ogre::String skeletonName;
		Ogre::String meshName;
	};

	std::vector<SkeletonSource> getAvailableSkeletonSources() const;
	std::vector<Ogre::String> getAnimationsForSource(
		const Ogre::String &source) const;

	/* Return the skeleton resource name for a given mesh, or empty. */
	Ogre::String getSkeletonNameForMesh(const Ogre::String &meshName) const;

	/* ------------------------------------------------------------------ */
	/*  Persistence                                                       */
	/* ------------------------------------------------------------------ */
	nlohmann::json serialize() const;
	void deserialize(const nlohmann::json &j);

	bool saveToFile(const std::string &filepath);
	bool loadFromFile(const std::string &filepath);
	const std::string &getLastError() const
	{
		return m_lastError;
	}

	void autoSave();

	/* Default file path used by initialize()/autoSave(). */
	static const char *defaultPath()
	{
		return "animation_tree.json";
	}

private:
	std::unordered_map<Ogre::String, AnimationTreeDefinition> m_trees;
	std::string m_autoSavePath;
	std::string m_lastError;
};

#endif // EDITSCENE_ANIMATIONTREEREGISTRY_HPP
