// from http://www.ogre3d.org/wiki/index.php/AnimationBlender

#include "AnimationBlender.h"
#include "OgreAnimationState.h"


using namespace Ogre;


void AnimationBlender::init(const String &animation)
{
    AnimationStateSet *set = mEntity->getAllAnimationStates();
    AnimationStateIterator it = set->getAnimationStateIterator();
    while(it.hasMoreElements())
    {
        AnimationState *anim = it.getNext();
        anim->setEnabled(false);
        anim->setWeight(0);
        anim->setTimePosition(0);
    }
    mSource = mEntity->getAnimationState( animation );
    mSource->setEnabled(true);
    mSource->setWeight(1);
    mTimeleft = 0;
    mDuration = 1;
    mTarget = 0;
    complete=false;
} 
void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
{
    loop=l;
    if( transition == AnimationBlender::BlendSwitch )
    {
        if( mSource != 0 )
            mSource->setEnabled(false);
        mSource = mEntity->getAnimationState( animation );
        mSource->setEnabled(true);
        mSource->setWeight(1);
        mSource->setTimePosition(0);
        mTimeleft = 0;
    } 
    else 
    { 
        AnimationState *newTarget = mEntity->getAnimationState( animation );
        if( mTimeleft > 0 )
        {
            // oops, weren't finished yet
            if( newTarget == mTarget )
            {
                // nothing to do! (ignoring duration here)
            }
            else if( newTarget == mSource )
            {
                // going back to the source state, so let's switch
                mSource = mTarget;
                mTarget = newTarget;
                mTimeleft = mDuration - mTimeleft; // i'm ignoring the new duration here
            }
            else
            {
                // ok, newTarget is really new, so either we simply replace the target with this one, or
                // we make the target the new source
                if( mTimeleft < mDuration * 0.5 )
                {
                    // simply replace the target with this one
                    mTarget->setEnabled(false);
                    mTarget->setWeight(0);
                }
                else
                {
                    // old target becomes new source
                    mSource->setEnabled(false);
                    mSource->setWeight(0);
                    mSource = mTarget;
                } 
                mTarget = newTarget;
                mTarget->setEnabled(true);
                mTarget->setWeight( 1.0 - mTimeleft / mDuration );
                mTarget->setTimePosition(0);
            }
        }
        else
        {
            if (mSource == newTarget)
            {
                mTimeleft = 0;
                return;
            }
            // assert( target == 0, "target should be 0 when not blending" )
            // mSource->setEnabled(true);
            // mSource->setWeight(1);
            mTransition = transition;
            mTimeleft = mDuration = duration;
            mTarget = newTarget;
            mTarget->setEnabled(true);
            mTarget->setWeight(0);
            mTarget->setTimePosition(0);
        }
    }
}
void AnimationBlender::addTime( Real time )
{
    if( mSource != 0 )
    {
        if( mTimeleft > 0 )
        {
            mTimeleft -= time;
            if( mTimeleft < 0 )
            {
                // finish blending
                mSource->setEnabled(false);
                mSource->setWeight(0);
                mSource = mTarget;
                mSource->setEnabled(true);
                mSource->setWeight(1);
                mTarget = 0;
            }
            else
            {
                // still blending, advance weights
                mSource->setWeight(mTimeleft / mDuration);
                mTarget->setWeight(1.0 - mTimeleft / mDuration);
                if(mTransition == AnimationBlender::BlendWhileAnimating)
                    mTarget->addTime(time);
            }
        }
        if (mSource->getTimePosition() >= mSource->getLength())
        {
            complete=true;
        }
        else
        {
            complete=false;
        }
        mSource->addTime(time);
        mSource->setLoop(loop);
    }
}
AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity) {}
