#ifndef CHARACTER_ANIMATION_MODULE_H_
#define CHARACTER_ANIMATION_MODULE_H_
#include <Ogre.h>
#include <flecs.h>
#include "GameData.h"
#include "CharacterModule.h"
#include "LuaData.h"
#include "EventModule.h"
namespace ECS
{
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
	Ogre::Vector3 prevTranslation;
	mutable Ogre::Vector3 deltaMotion;
	flecs::entity e;

public:
	RootMotionListener(flecs::entity e)
		: Ogre::NodeAnimationTrack::Listener()
		, e(e)
		, prevTranslation(Ogre::Vector3::ZERO)
		, deltaMotion(Ogre::Vector3::ZERO)
	{
	}
	bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t,
				     const Ogre::TimeIndex &timeIndex,
				     Ogre::KeyFrame *kf) override
	{
		const Ogre::NodeAnimationTrack *nodeTrack =
		    static_cast<const Ogre::NodeAnimationTrack *>(t);
		Ogre::Node *trackNode = nodeTrack->getAssociatedNode();
#if 0
		Ogre::Any entityAny =
		    trackNode->getUserObjectBindings().getUserAny("empty");
		if (entityAny.has_value())
			e = Ogre::any_cast<flecs::entity>(entityAny);
#endif
		Ogre::TransformKeyFrame *vkf =
			static_cast<Ogre::TransformKeyFrame *>(kf);
		Ogre::KeyFrame *kf1, *kf2;
		Ogre::TransformKeyFrame *k1, *k2;
		unsigned short firstKeyIndex;
		float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2,
						 &firstKeyIndex);
		k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
		k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
		Ogre::Vector3 translation;
		Ogre::Quaternion rotation;
		if (tm == 0.0f) {
			rotation = k1->getRotation();
			translation = k1->getTranslate();
			deltaMotion = translation;
		} else {
			rotation = Ogre::Quaternion::nlerp(
				tm, k1->getRotation(), k2->getRotation(), true);
			translation =
				k1->getTranslate() +
				(k2->getTranslate() - k1->getTranslate()) * tm;
			deltaMotion = translation - prevTranslation;
			if (deltaMotion.squaredLength() >
			    translation.squaredLength())
				deltaMotion = translation;
		}
		vkf->setTranslate(deltaMotion);
		vkf->setRotation(rotation);
		vkf->setScale(Ogre::Vector3(1, 1, 1));
		prevTranslation = translation;
		e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
		e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
		e.modified<CharacterBase>();
		return true;
	}
};
struct AnimationTrigger;
struct AnimationTriggerSubscriber {
	virtual void operator()(const AnimationTrigger *trigger) = 0;
};
struct AnimationTrigger {
	Ogre::String name;
	float time;
	float weight;
	std::vector<AnimationTriggerSubscriber *> subscriber_list;
	float getTriggerTime() const
	{
		return time;
	}
	float getMinWeight() const
	{
		return weight;
	}
	const Ogre::String &getName() const
	{
		return name;
	}
	void notify(float weight)
	{
		int i;
		if (weight < this->weight)
			return;
		for (i = 0; i < subscriber_list.size(); i++)
			(*subscriber_list[i])(this);
	}
	void addSubscriber(AnimationTriggerSubscriber *sub)
	{
		if (std::find(subscriber_list.begin(), subscriber_list.end(),
			      sub) != subscriber_list.end())
			return;
		subscriber_list.push_back(sub);
	}
	void removeSubscriber(AnimationTriggerSubscriber *sub)
	{
		auto it = std::find(subscriber_list.begin(),
				    subscriber_list.end(), sub);
		if (it != subscriber_list.end())
			subscriber_list.erase(it);
	}
	void clearSubscribers()
	{
		subscriber_list.clear();
	}
	AnimationTrigger(const Ogre::String name, float time, float weight)
		: name(name)
		, time(time)
		, weight(weight)
	{
	}
};
struct Animation {
	Ogre::AnimationState *mAnimationState;
	Ogre::Animation *mSkelAnimation;
	Ogre::NodeAnimationTrack *mHipsTrack;
	Ogre::NodeAnimationTrack *mRootTrack;
	float m_weight;
	float m_accWeight;
	Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
          Ogre::Animation *skelAnimation)
		: mAnimationState(animState)
		, mSkelAnimation(skelAnimation)
		, m_weight(0)
		, m_accWeight(0)
	{
        // FIXME
		int j;
		mRootTrack = nullptr;
		mHipsTrack = nullptr;
        for (const auto &it : mSkelAnimation->_getNodeTrackList()) {
			Ogre::NodeAnimationTrack *track = it.second;
			Ogre::String trackName =
				track->getAssociatedNode()->getName();
			if (trackName == "mixamorig:Hips") {
				mHipsTrack = track;
			} else if (trackName == "Root") {
				mRootTrack = track;
			}
		}
        OgreAssert(mRootTrack, "no root track");
	}
	Ogre::String getName()
	{
		return mAnimationState->getAnimationName();
	}
	void setLoop(bool loop)
	{
		mAnimationState->setLoop(loop);
	}
	bool getLoop() const
	{
		return mAnimationState->getLoop();
	}
	void setEnabled(bool enabled)
	{
		mAnimationState->setEnabled(enabled);
	}
	bool getEnabled() const
	{
		return mAnimationState->getEnabled();
	}
	void setWeight(float weight)
	{
		bool enabled = weight > 0.001f;
		setEnabled(enabled);
		mAnimationState->setWeight(weight);
		m_weight = weight;
	}
	float getWeight() const
	{
		return m_weight;
	}
	bool addTime(float time)
	{
		bool result = mAnimationState->getEnabled();
		if (!result)
			return result;
		Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
			mAnimationState->getTimePosition());
		Ogre::KeyFrame *kf1, *kf2;
		unsigned short prev_index, next_index;
		mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &prev_index);
		unsigned int previous_frame = index.getKeyIndex();
		mAnimationState->addTime(time);
		index = mSkelAnimation->_getTimeIndex(
			mAnimationState->getTimePosition());
		mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
		return prev_index != next_index;
	}
	void reset()
	{
		mAnimationState->setTimePosition(0);
	}
	void resetAccWeight()
	{
		m_accWeight = 0;
	}
	void increaseAccWeight(float weight)
	{
		m_accWeight += weight;
	}
	float getAccWeight() const
	{
		return m_accWeight;
	}
	float getLength() const
	{
		return mAnimationState->getLength();
		if (getEnabled())
			return mAnimationState->getLength();
		else
			return 0.0f;
	}
	float getTimePosition() const
	{
		return mAnimationState->getTimePosition();
		if (getEnabled())
			return mAnimationState->getTimePosition();
		else
			return 0.0f;
	}
};

struct AnimationNode {
	std::vector<AnimationNode *> children;
	float m_weight;
	Ogre::String m_name;
	std::multimap<float, AnimationTrigger *> trigger_list;
	AnimationNode()
		: m_weight(0)
	{
	}
	virtual bool addTime(float time) = 0;
	virtual void setWeight(float weight) = 0;
	virtual void reset() = 0;
	virtual float getLength() const = 0;
	virtual float getTimePosition() const = 0;
	float getWeight()
	{
		return m_weight;
	}
	const Ogre::String &getName()
	{
		return m_name;
	}
	void setName(const Ogre::String &name)
	{
		m_name = name;
	}
	virtual float getTime() const
	{
		float l = getLength();
		if (l > 0.0f)
			return getTimePosition() / l;
		return 0.0f;
	}
	void addTrigger(AnimationTrigger *trigger)
	{
		trigger_list.insert(std::pair<float, AnimationTrigger *>(
			trigger->getTriggerTime(), trigger));
	}
	void clearTriggers()
	{
		auto it = trigger_list.begin();
		while (it != trigger_list.end()) {
			delete it->second;
			it++;
		}
		trigger_list.clear();
	}
	float mpreUpdateTime;
	void preUpdateTriggers()
	{
		mpreUpdateTime = getTime();
	}
	void postUpdateTriggers(float delta)
	{
		float postUpdateTime = getTime();
		bool positive = delta >= 0.0f;
		if (positive)
			updateTriggers(mpreUpdateTime, postUpdateTime);
		else
			updateTriggers(postUpdateTime, mpreUpdateTime);
	}
	void updateTriggers(float currentTime, float nextTime)
	{
		int i;
		float weight = getWeight();
		if (currentTime <= nextTime) {
			auto it = trigger_list.lower_bound(currentTime);
			while (it != trigger_list.end()) {
				if (nextTime <=
				    it->second->getTriggerTime()) // in future, sorrted by time
					return;
				it->second->notify(weight);
				it++;
			}
		} else {
			updateTriggers(currentTime, 1);
			updateTriggers(0, nextTime);
		}
	}
};

struct AnimationNodeAnimation : AnimationNode {
	Animation *mAnimation;
	bool enabled;
	AnimationNodeAnimation(Animation *animation)
		: AnimationNode()
		, mAnimation(animation)
	{
	}
	bool addTime(float time)
	{
		bool ret;
		preUpdateTriggers();
		ret = mAnimation->addTime(time);
		postUpdateTriggers(time);
		return ret;
	}
	void setWeight(float weight)
	{
		m_weight = weight;
		enabled = weight > 0.001f;
	}
	void reset()
	{
		mAnimation->reset();
	}
	float getLength() const
	{
		return mAnimation->getLength();
		if (enabled)
			return mAnimation->getLength();
		else
			return 0.0f;
	}
	float getTimePosition() const
	{
		return mAnimation->getTimePosition();
		if (enabled)
			return mAnimation->getTimePosition();
		else
			return 0.0f;
	}
};
struct AnimationNodeStateMachineState : AnimationNode {
	AnimationNodeStateMachineState()
		: AnimationNode()
	{
	}
	bool addTime(float time)
	{
		bool ret;
		preUpdateTriggers();
		ret = children[0]->addTime(time);
		postUpdateTriggers(time);
		return ret;
	}
	void setWeight(float weight)
	{
		m_weight = weight;
		bool enabled = weight > 0.001f;
		children[0]->setWeight(weight);
	}
	void reset()
	{
		children[0]->reset();
	}
	float getLength() const
	{
		return children[0]->getLength();
	}
	float getTimePosition() const
	{
		return children[0]->getTimePosition();
	}
};
struct AnimationNodeSpeed : AnimationNode {
	float m_speed;
	bool enabled;
	AnimationNodeSpeed(float speed)
		: AnimationNode()
		, m_speed(speed)
		, enabled(false)
	{
	}
	bool addTime(float time)
	{
		bool ret;
		preUpdateTriggers();
		ret = children[0]->addTime(time * m_speed);
		postUpdateTriggers(time);
		return ret;
	}
	void setWeight(float weight)
	{
		m_weight = weight;
		children[0]->setWeight(weight);
	}
	void reset()
	{
		children[0]->reset();
	}
	float getLength() const
	{
		if (m_speed > 0.0f || m_speed < 0.0f)
			return children[0]->getLength() / m_speed;
		return 0.0f;
	}
	float getTimePosition() const
	{
		if (m_speed > 0.0f || m_speed < 0.0f)
			return children[0]->getTimePosition() / m_speed;
		return 0.0f;
	}
	float getTime() const override
	{
		float l = children[0]->getLength();
		if (l > 0.0f)
			return children[0]->getTimePosition() / l;
		return 0.0f;
	}
};
struct AnimationNodeStateMachine : AnimationNode {
	std::map<Ogre::String, AnimationNode *> stateMap;
	std::set<AnimationNode *> fade_in, fade_out;
	AnimationNode *currentAnim, *nextAnim;
	float fade_speed;
	Ogre::String mCurrentStateName;
	bool configured;
	bool debug;
	AnimationNodeStateMachine(float fade_speed, bool debug = false)
		: AnimationNode()
		, currentAnim(nullptr)
		, nextAnim(nullptr)
		, fade_speed(fade_speed)
		, mCurrentStateName("")
		, configured(false)
		, debug(debug)
	{
		m_weight = 1.0f;
	}
	bool addTime(float time)
	{
		int i;
		preUpdateTriggers();
		if (!configured) {
			configure();
			configured = true;
		}
#ifdef VDEBUG
		if (debug) {
			std::cout << "state machine addTime" << std::endl;
			std::cout
				<< "state machine children: " << children.size()
				<< std::endl;
		}
#endif
		for (i = 0; i < children.size(); i++) {
#ifdef VDEBUG
			if (debug)
				std::cout << "child weight: " << i << " "
					  << children[i]->getWeight()
					  << std::endl;
#endif
			AnimationNode *child = children[i];
			if (fade_in.find(child) != fade_in.end()) {
				Ogre::Real newWeight =
					child->getWeight() + time * fade_speed;
				child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
					newWeight * m_weight, 0, m_weight));
#ifdef VDEBUG
				if (debug) {
					std::cout << "fade in: " << newWeight
						  << std::endl;
					std::cout << "m_weight: " << m_weight
						  << std::endl;
				}
#endif
				if (newWeight >= 1)
					fade_in.erase(child);
			}
			if (fade_out.find(child) != fade_out.end()) {
				Ogre::Real newWeight =
					child->getWeight() - time * fade_speed;
				child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
					newWeight * m_weight, 0, 1));
				if (newWeight <= 0)
					fade_out.erase(child);
			}
		}
		OgreAssert(currentAnim, "bad current anim");
		bool ret = false;
		if (currentAnim)
			ret = currentAnim->addTime(time);
		postUpdateTriggers(time);
		return ret;
	}
	void setWeight(float weight)
	{
		int i;
		if (weight > m_weight && currentAnim)
			fade_in.insert(currentAnim);
		if (weight < m_weight && currentAnim &&
		    currentAnim->getWeight() > weight)
			currentAnim->setWeight(weight);
		m_weight = weight;
		bool enabled = weight > 0.001f;
		/* do not update child state yet */
	}
	void addState(AnimationNode *state)
	{
		const Ogre::String &name = state->getName();
		stateMap[name] = state;
		state->setWeight(0);
		fade_in.erase(state);
		fade_out.erase(state);
		std::cout << "registered state: " << name << std::endl;
	}
	void configure()
	{
		int i;
		if (debug)
			std::cout << "children: " << children.size()
				  << std::endl;
		for (i = 0; i < children.size(); i++)
			addState(children[i]);
		if (debug)
			std::cout << "configure called" << std::endl;
	}
	void reset()
	{
		int i;
		for (i = 0; i < children.size(); i++)
			children[i]->reset();
	}
	void setAnimation(const Ogre::String &anim_state, bool reset = false)
	{
		if (!configured) {
			configure();
			configured = true;
		}
		OgreAssert(stateMap.find(anim_state) != stateMap.end(),
			   "Bad animation state: " + anim_state);
		nextAnim = stateMap[anim_state];
		if (nextAnim == currentAnim)
			return;
		if (currentAnim != nullptr) {
			fade_out.insert(currentAnim);
			fade_in.erase(currentAnim);
		}
		fade_out.erase(nextAnim);
		fade_in.insert(nextAnim);
		nextAnim->setWeight(0);
		if (reset)
			nextAnim->reset();
		currentAnim = nextAnim;
		mCurrentStateName = anim_state;
	}
	const Ogre::String &getCurrentState() const
	{
		return mCurrentStateName;
	}
	float getLength() const
	{
		if (currentAnim)
			return currentAnim->getLength();
		else
			return 0.0f;
	}
	float getTimePosition() const
	{
		if (currentAnim)
			return currentAnim->getTimePosition();
		else
			return 0.0f;
	}
};

#define ANIM_FADE_SPEED \
	7.5f // animation crossfade speed in % of full weight per second

struct AnimationNodeOutput : AnimationNode {
	float m_weight;
	float m_speed;
	AnimationNodeOutput()
		: AnimationNode()
		, m_weight(1.0f)
		, m_speed(1.0f)
	{
	}
	bool addTime(float time)
	{
		bool ret;
		preUpdateTriggers();
		ret = children[0]->addTime(time * m_speed);
		postUpdateTriggers(time);
		return ret;
	}
	void setWeight(float weight)
	{
		m_weight = weight;
		bool enabled = weight > 0.001f;
		children[0]->setWeight(weight);
	}
	void reset()
	{
		children[0]->reset();
	}
	float getLength() const
	{
		return children[0]->getLength();
	}
	float getTimePosition() const
	{
		return children[0]->getTimePosition();
	}
};

struct AnimationSystem : AnimationNode {
    RootMotionListener *mListener;
    bool debug;
    AnimationSystem(Ogre::Entity *ent, flecs::entity e, bool debug = false)
        : mListener(OGRE_NEW RootMotionListener(e))
        , debug(debug)
		, m_builder(this, debug)
	{
        static std::set<Ogre::Skeleton *> skeletons;
        Ogre::AnimationStateSet *animSet = ent->getAllAnimationStates();
        Ogre::Skeleton *skeleton = ent->getSkeleton();
        const Ogre::AnimationStateMap &states =
            animSet->getAnimationStates();
        for (auto it = states.begin(); it != states.end(); it++) {
            Ogre::Animation *skeletonAnimation =
                skeleton->getAnimation(it->first);

            Ogre::NodeAnimationTrack *rootTrack = nullptr,
                         *hipsTrack = nullptr;
            for (const auto &it :
                 skeletonAnimation->_getNodeTrackList()) {
                Ogre::NodeAnimationTrack *track = it.second;
                Ogre::String trackName =
                    track->getAssociatedNode()->getName();
                if (trackName == "mixamorig:Hips")
                    hipsTrack = track;
                else if (trackName == "Root")
                    rootTrack = track;
            }
            if (true || skeletons.find(skeleton) == skeletons.end()) {
                if (!rootTrack) {
                    Ogre::Bone *bone =
                        skeleton->getBone("Root");
                    rootTrack =
                        skeletonAnimation
                            ->createNodeTrack(
                                bone->getHandle(),
                                bone);
                    Ogre::TransformKeyFrame *kf =
                        rootTrack->createNodeKeyFrame(
                            0.0f);
                    kf->setTranslate(Ogre::Vector3::ZERO);
                    kf->setRotation(
                        Ogre::Quaternion::IDENTITY);
                }
                rootTrack->setListener(mListener);
            }
            OgreAssert(rootTrack, "no root track");
            Ogre::Node *controlNode =
                rootTrack->getAssociatedNode();
            if (controlNode) {
                Ogre::Any listenerAny =
                    controlNode->getUserObjectBindings()
                        .getUserAny("entity");
                if (!listenerAny.has_value()) {
                    controlNode->getUserObjectBindings()
                        .setUserAny("entity", e);
                }
            }

            Animation *animation = new Animation(
                skeleton, it->second, skeletonAnimation);
#ifdef VDEBUG
            std::cout << "animation: " << animNames[i] << std::endl;
#endif
            animation->setLoop(true);
            add_animation(it->first, animation);
        }
        skeletons.insert(skeleton);
    }
    std::unordered_map<Ogre::String, Animation *> animation_list;
	std::vector<Animation *> vanimation_list;
	void add_animation(const Ogre::String &name, Animation *animation)
	{
		OgreAssert(animation, "No animation " + name);
		animation_list[name] = animation;
		vanimation_list.push_back(animation);
	}
	void clear_animations()
	{
		animation_list.clear();
		vanimation_list.clear();
	}
	struct AnimationSystemBuilder {
		AnimationSystem *mAnimationSystem;
		std::vector<AnimationNode *> animation_nodes;
		AnimationNode *parent;
		std::list<AnimationNode *> parent_stack;
		std::unordered_map<Ogre::String, AnimationNode *> nodeMap;
		std::vector<AnimationNodeAnimation *> animationNodeList;
		bool debug;
		AnimationSystemBuilder(AnimationSystem *animationSystem,
				       bool debug = false)
			: mAnimationSystem(animationSystem)
			, debug(debug)
		{
		}
		AnimationSystemBuilder *output()
		{
			AnimationNodeOutput *onode = new AnimationNodeOutput();
			animation_nodes.push_back(onode);
			parent = onode;
			return this;
		}
		AnimationSystemBuilder *
		animation(const Ogre::String &animation_name)
		{
			OgreAssert(parent, "bad parent");
			Animation *animation =
				mAnimationSystem->animation_list[animation_name];
			OgreAssert(animation,
				   "bad animation " + animation_name);
			AnimationNodeAnimation *onode =
				new AnimationNodeAnimation(animation);
			OgreAssert(onode, "bad animation");
			OgreAssert(onode->mAnimation, "bad animation");
			animation_nodes.push_back(onode);
			parent->children.push_back(onode);
			animationNodeList.push_back(onode);
			return this;
		}
		AnimationSystemBuilder *trigger(flecs::entity e,
						const Ogre::String &name,
						float time,
						const Ogre::String &event)
		{
			struct EventSubscriber : AnimationTriggerSubscriber {
				flecs::entity ent;
				Ogre::String event;
				void operator()(const AnimationTrigger *trigger)
				{
					ent.get_mut<EventData>().add(ent, event,
								     ent, ent);
				}
				EventSubscriber(flecs::entity e,
						const Ogre::String &event)
					: ent(e)
					, event(event)
				{
				}
			};
			OgreAssert(parent, "bad parent");
			AnimationTrigger *trigger =
				new AnimationTrigger(name, time, 0.1f);
			EventSubscriber *sub = new EventSubscriber(e, event);
			trigger->addSubscriber(sub);
			parent->addTrigger(trigger);
			return this;
		} // leaf too...
		AnimationSystemBuilder *
		transition_end(const Ogre::String &state_from,
			       const Ogre::String &state_to)
		{
			struct EndTransitionSubscriber
				: AnimationTriggerSubscriber {
				AnimationNodeStateMachine *sm;
				Ogre::String next_state;
				bool reset;
				void operator()(const AnimationTrigger *trigger)
				{
					sm->setAnimation(next_state, reset);
				}
				EndTransitionSubscriber(
					AnimationNodeStateMachine *sm,
					const Ogre::String &next_state,
					bool reset = true)
					: sm(sm)
					, next_state(next_state)
					, reset(reset)
				{
				}
			};
			OgreAssert(parent, "no parent");
			AnimationNodeStateMachine *sm =
				static_cast<AnimationNodeStateMachine *>(
					parent);
			OgreAssert(sm, "no state machine");
			AnimationTrigger *trigger = new AnimationTrigger(
				"transition:" + state_from + "_" + state_to,
				0.99f, 0.1f);
			EndTransitionSubscriber *sub =
				new EndTransitionSubscriber(sm, state_to);
			int i;
			bool ok = false;
			for (i = 0; i < sm->children.size(); i++) {
				if (sm->children[i]->getName() == state_from) {
					trigger->addSubscriber(sub);
					sm->children[i]->addTrigger(trigger);
					ok = true;
					break;
				}
			}
			OgreAssert(ok, "Failed to set transition");
			return this;
		}
		AnimationSystemBuilder *speed(float speed,
					      const Ogre::String &anchor = "")
		{
			OgreAssert(parent, "bad parent");
			AnimationNodeSpeed *onode =
				new AnimationNodeSpeed(speed);
			animation_nodes.push_back(onode);
			parent->children.push_back(onode);
			parent_stack.push_back(parent);
			parent = onode;
			if (anchor.length() > 0)
				nodeMap[anchor] = onode;
			return this;
		}
		AnimationSystemBuilder *
		state_machine(float fade_time = ANIM_FADE_SPEED,
			      const Ogre::String &anchor = "")
		{
			OgreAssert(parent, "bad parent");
			AnimationNodeStateMachine *onode =
				new AnimationNodeStateMachine(fade_time, debug);
			animation_nodes.push_back(onode);
			parent->children.push_back(onode);
			parent_stack.push_back(parent);
			parent = onode;
			if (anchor.length() > 0)
				nodeMap[anchor] = onode;
			return this;
		}
		AnimationSystemBuilder *state(const Ogre::String &state_name)
		{
			OgreAssert(parent, "bad parent");
			AnimationNodeStateMachineState *onode =
				new AnimationNodeStateMachineState;
			animation_nodes.push_back(onode);
			parent->children.push_back(onode);
			parent_stack.push_back(parent);
			parent = onode;
			onode->setName(state_name);
			return this;
		}
		AnimationSystemBuilder *end()
		{
			parent = parent_stack.back();
			parent_stack.pop_back();
			return this;
		}
	};
	AnimationSystemBuilder m_builder;
	bool addTime(float time)
	{
		int i;
		preUpdateTriggers();
		bool ret = m_builder.animation_nodes[0]->addTime(time);
		for (i = 0; i < m_builder.animationNodeList.size(); i++) {
			AnimationNodeAnimation *anim =
				m_builder.animationNodeList[i];
			OgreAssert(anim->mAnimation, "No animation");
			float weight = anim->getWeight();
			anim->mAnimation->increaseAccWeight(weight);
#ifdef VDEBUG
			if (debug)
				std::cout << i << " node: "
					  << anim->mAnimation->getName() << " "
					  << weight << std::endl;
#endif
		}
		for (i = 0; i < vanimation_list.size(); i++) {
			float weight = vanimation_list[i]->getAccWeight();
			vanimation_list[i]->setWeight(weight);
			vanimation_list[i]->resetAccWeight();
#define VDEBUG
#ifdef VDEBUG
			if (debug && vanimation_list[i]->getEnabled())
				std::cout << i << " animation: "
					  << vanimation_list[i]->getName()
					  << " " << weight << std::endl;
#endif
#undef VDEBUG
		}
		postUpdateTriggers(time);
		return ret;
	}
	void setWeight(float weight)
	{
		m_builder.animation_nodes[0]->setWeight(weight);
	}
	void reset()
	{
		m_builder.animation_nodes[0]->reset();
	}
	AnimationSystemBuilder *builder()
	{
		m_builder.animation_nodes.reserve(8);
		m_builder.parent = nullptr;
		return &m_builder;
	}
	template <class T> T *get(const Ogre::String &name)
	{
		return static_cast<T *>(m_builder.nodeMap[name]);
	}
	float getLength() const
	{
		return m_builder.animation_nodes[0]->getLength();
	}
	float getTimePosition() const
	{
		return m_builder.animation_nodes[0]->getTimePosition();
	}
};

struct AnimationControl {
	bool configured;
	AnimationSystem *mAnimationSystem;
};
struct DefaultAnimation {
	std::vector<std::pair<std::string, std::string> > animations;
};
struct CharacterAnimationModule {
	CharacterAnimationModule(flecs::world &ecs);
};
}
#endif
