#ifndef EDITSCENE_ANIMATIONTREE_HPP
#define EDITSCENE_ANIMATIONTREE_HPP
#pragma once
#include <Ogre.h>
#include <vector>
#include <unordered_map>

/**
 * A node in the animation tree.
 *
 * Node types:
 *   "output"       - Root output node, optional speed multiplier (1 child)
 *   "stateMachine" - Cross-fades between child states, named for lookup
 *   "state"        - A named state within a state machine (1 child)
 *   "speed"        - Playback speed multiplier (1 child)
 *   "animation"    - Leaf referencing an Ogre animation by name
 */
struct AnimationTreeNode {
	Ogre::String type = "animation";
	Ogre::String name;
	Ogre::String animationName;
	float speed = 1.0f;
	float fadeSpeed = 7.5f;
	std::vector<AnimationTreeNode> children;
	/* For stateMachine nodes: auto-transition when animation ends */
	std::unordered_map<Ogre::String, Ogre::String> endTransitions;

	AnimationTreeNode() = default;

	AnimationTreeNode *findChild(const Ogre::String &childName)
	{
		for (auto &child : children) {
			if (child.name == childName)
				return &child;
		}
		return nullptr;
	}

	const AnimationTreeNode *findChild(
		const Ogre::String &childName) const
	{
		for (const auto &child : children) {
			if (child.name == childName)
				return &child;
		}
		return nullptr;
	}

	AnimationTreeNode *findStateMachine(const Ogre::String &smName)
	{
		if (type == "stateMachine" && name == smName)
			return this;
		for (auto &child : children) {
			auto *found = child.findStateMachine(smName);
			if (found)
				return found;
		}
		return nullptr;
	}

	const AnimationTreeNode *findStateMachine(
		const Ogre::String &smName) const
	{
		if (type == "stateMachine" && name == smName)
			return this;
		for (const auto &child : children) {
			auto *found = child.findStateMachine(smName);
			if (found)
				return found;
		}
		return nullptr;
	}

	AnimationTreeNode *findState(const Ogre::String &stateName)
	{
		if (type == "state" && name == stateName)
			return this;
		for (auto &child : children) {
			auto *found = child.findState(stateName);
			if (found)
				return found;
		}
		return nullptr;
	}

	const AnimationTreeNode *findState(
		const Ogre::String &stateName) const
	{
		if (type == "state" && name == stateName)
			return this;
		for (const auto &child : children) {
			auto *found = child.findState(stateName);
			if (found)
				return found;
		}
		return nullptr;
	}

	AnimationTreeNode *findAnimationLeaf()
	{
		if (type == "animation")
			return this;
		for (auto &child : children) {
			auto *found = child.findAnimationLeaf();
			if (found)
				return found;
		}
		return nullptr;
	}

	const AnimationTreeNode *findAnimationLeaf() const
	{
		if (type == "animation")
			return this;
		for (const auto &child : children) {
			auto *found = child.findAnimationLeaf();
			if (found)
				return found;
		}
		return nullptr;
	}

	void collectStateMachines(std::vector<AnimationTreeNode *> &out)
	{
		if (type == "stateMachine")
			out.push_back(this);
		for (auto &child : children)
			child.collectStateMachines(out);
	}

	void collectStateMachines(
		std::vector<const AnimationTreeNode *> &out) const
	{
		if (type == "stateMachine")
			out.push_back(this);
		for (const auto &child : children)
			child.collectStateMachines(out);
	}
};

/**
 * Animation tree component for hierarchical state-machine-based animation.
 *
 * The tree is evaluated each frame by AnimationTreeSystem to determine
 * which Ogre AnimationStates are active and their blend weights.
 */
struct AnimationTreeComponent {
	AnimationTreeNode root;
	bool enabled = true;
	bool useRootMotion = false;
	bool dirty = true;

	/* If set, the tree root is copied from the named template */
	Ogre::String templateName;

	/* Runtime: last copied template version (not serialized) */
	uint64_t templateVersion = 0;

	/* Runtime: current state of each state machine (not serialized) */
	std::unordered_map<Ogre::String, Ogre::String> currentStates;

	AnimationTreeComponent();
};

#endif // EDITSCENE_ANIMATIONTREE_HPP
