#ifndef ANIMATIONSYSTEM_H
#define ANIMATIONSYSTEM_H

#include <iostream>
#include <Ogre.h>
#include <flecs.h>
#include <tracy/Tracy.hpp>
#include "GameData.h"
#include "EventModule.h"

namespace AnimationSystem
{
struct AnimationTrigger;
struct AnimationTriggerSubscriber {
    virtual void operator()(const AnimationTrigger *trigger) = 0;
};
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
public:
    RootMotionListener()
        : Ogre::NodeAnimationTrack::Listener()
    {
    }
    bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t,
                     const Ogre::TimeIndex &timeIndex,
                     Ogre::KeyFrame *kf) override
    {
#if 0
        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;
        Ogre::Vector3 deltaMotion;
        Ogre::Vector3 prevMotion;
        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;
#endif
        return false;
    }
};
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 {
    struct SetupTracks {
        Ogre::NodeAnimationTrack *mHipsTrack;
        Ogre::NodeAnimationTrack *mRootTrack;
        RootMotionListener *mListener;
        Ogre::Vector3 mRootTranslation;
        SetupTracks(Ogre::Skeleton *skeleton,
                Ogre::Animation *animation)
            : mListener(OGRE_NEW RootMotionListener())
        {
            mHipsTrack = nullptr;
            mRootTrack = nullptr;
            for (const auto &it : animation->_getNodeTrackList()) {
                Ogre::NodeAnimationTrack *track = it.second;
                Ogre::String trackName =
                    track->getAssociatedNode()->getName();
                if (trackName == "mixamorig:Hips") {
                    mHipsTrack = track;
                } else if (trackName == "Root") {
                    mRootTrack = track;
                }
            }
            if (!mRootTrack) {
                Ogre::Bone *bone = skeleton->getBone("Root");
                mRootTrack = animation->createNodeTrack(
                    bone->getHandle(), bone);
                Ogre::TransformKeyFrame *kf =
                    mRootTrack->createNodeKeyFrame(0.0f);
                kf->setTranslate(Ogre::Vector3::ZERO);
                kf->setRotation(Ogre::Quaternion::IDENTITY);
            }
            Ogre::TransformKeyFrame *tkfBeg =
                (Ogre::TransformKeyFrame *)
                    mRootTrack->getKeyFrame(0);
            Ogre::TransformKeyFrame *tkfEnd =
                (Ogre::TransformKeyFrame *)
                    mRootTrack->getKeyFrame(
                        mRootTrack->getNumKeyFrames() -
                        1);
            mRootTranslation =
                tkfEnd->getTranslate() - tkfBeg->getTranslate();
        }
    };
    Ogre::AnimationState *mAnimationState;
    Ogre::Animation *mSkelAnimation;
    SetupTracks *mTracks;
    float m_weight;
    float m_accWeight;
    Ogre::Skeleton *mSkeleton;
    Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
          Ogre::Animation *skelAnimation)
        : mTracks(OGRE_NEW SetupTracks(skeleton, skelAnimation))
        , mAnimationState(animState)
        , mSkelAnimation(skelAnimation)
        , m_weight(0)
        , m_accWeight(0)
        , mSkeleton(skeleton)
    {
    }
    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;
    }
    Ogre::Vector3 rootMotion;
    Ogre::Vector3 getRootMotionDelta()
    {
        if (mAnimationState->getEnabled())
            return rootMotion * mAnimationState->getWeight();
        else
            return Ogre::Vector3(0, 0, 0);
    }
    void getKeyframeIndices(Ogre::Real timePos, unsigned short *pindex)
    {
        Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
        Ogre::KeyFrame *kf1, *kf2;
        mTracks->mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2,
                            pindex);
    }
    bool addTime(float time)
    {
        bool result = mAnimationState->getEnabled();
        if (!result)
            return result;
        unsigned short prev_index, next_index;
        Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
            mAnimationState->getTimePosition());
        getKeyframeIndices(mAnimationState->getTimePosition(),
                   &prev_index);
        unsigned int previous_frame = index.getKeyIndex();
        float lastTime = mAnimationState->getTimePosition();
        mAnimationState->addTime(time);
        float thisTime = mAnimationState->getTimePosition();
        float length = mAnimationState->getLength();
        bool loop = mAnimationState->getLoop();
        int loops =
            loop ? (int)std::round((lastTime + time - thisTime) /
                           length) :
                   0;
        Ogre::TransformKeyFrame tkf(0, 0);
        mTracks->mRootTrack->getInterpolatedKeyFrame(lastTime, &tkf);
        Ogre::Vector3 lastRootPos = tkf.getTranslate();
        mTracks->mRootTrack->getInterpolatedKeyFrame(thisTime, &tkf);
        Ogre::Vector3 thisRootPos = tkf.getTranslate();
        rootMotion = (thisRootPos - lastRootPos) +
                 (loops * mTracks->mRootTranslation);

        getKeyframeIndices(mAnimationState->getTimePosition(),
                   &next_index);
        // updateRootMotion(mAnimationState->getTimePosition());
        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;
    }
};
struct AnimationSystem : AnimationNode {
    bool debug;

#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();
        }
    };

    AnimationSystem(bool debug = false)
        : debug(debug)
        , m_builder(this, debug)
    {
    }
    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;
        }
        /* FIXME: need to remove flecs dependency */
        AnimationSystemBuilder *
        trigger_entity(flecs::entity e, const Ogre::String &name,
                   float time, const Ogre::String &event)
        {
            struct EntityEventSubscriber
                : AnimationTriggerSubscriber {
                Ogre::String event;
                flecs::entity ent;
                void operator()(const AnimationTrigger *trigger)
                {
                    ent.get_mut<ECS::EventData>().add(
                        ent, event, ent, ent);
                }
                EntityEventSubscriber(flecs::entity e,
                        const Ogre::String &event)
                    : event(event)
                    , ent(e)
                {
                }
            };
            OgreAssert(parent, "bad parent");
            AnimationTrigger *trigger =
                new AnimationTrigger(name, time, 0.1f);
            EntityEventSubscriber *sub =
                new EntityEventSubscriber(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;
    Ogre::Vector3 getRootMotionDelta();
    bool addTime(float time);
    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();
    }
};
}

#endif // ANIMATIONSYSTEM_H
