// <pre>

package actor;

import render.Matrix;

/**
 * Base implentation assumes single KeyFrameAnimation
 */
public class AnimationManager
{
  private KeyFrameAnimation fromAnimation, toAnimation;  
  private double prevTime = 0.0, transition = 1.0;
  private double beat = 0.0, elapsed = 0.0;
  
  public AnimationManager() {}

  public AnimationManager(KeyFrameAnimation animation)
  {
    super();
    this.toAnimation = animation;
  }
  
  /*
   * Needs to setAnimationTravel() on Actor
   */
  public Point3D[] getAnimatedVertices(WorldState world, Actor actor)
  {        
    Point3D[] newAnimationPts = getAnimatedVerticesOffsets(world, actor);
    for(int i = 0; i < newAnimationPts.length; i++)
      newAnimationPts[i].add(actor.getModel().getBaseShape()[i]);
    return newAnimationPts;
  }
  
  
  /*
	 * Needs to setAnimationTravel() on Actor
	 */
  public Point3D[] getAnimatedVerticesOffsets(WorldState world, Actor actor)
  {        
    double time = world.getTime();
    if(prevTime == 0.0) prevTime = time;
    elapsed = time - prevTime;    
    prevTime = time;
    Point3D[] newAnimationPts;

    transition = Math.min(1, transition + elapsed);      
    
    if(transition >= 1.0)
    {  
      actor.setAnimationTravel(
        elapsed * toAnimation.getTravel() * toAnimation.getRate());

      beat = (beat + elapsed * toAnimation.getRate())
        % toAnimation.getFrameCount();
      
      newAnimationPts = toAnimation.getVertexOffsets(elapsed, beat);
    }
    else 
    {        
      actor.setAnimationTravel(elapsed
          * lerp(transition, fromAnimation.getTravel(), toAnimation.getTravel())
          * lerp(transition, fromAnimation.getRate(), toAnimation.getRate()));

      beat = (beat + elapsed * lerp(transition, fromAnimation.getRate(), 
         toAnimation.getRate())) % fromAnimation.getFrameCount();

      KeyFrame kfFrom = new KeyFrame
        (fromAnimation.getVertexOffsets(elapsed, beat));
      
      KeyFrame kfTo = new KeyFrame
        (toAnimation.getVertexOffsets(elapsed, beat));
      
      newAnimationPts = KeyFrame.keyFrameLerp(transition, kfFrom, kfTo);      
    }
    return newAnimationPts;
  }
  
  public void changeAnimation(KeyFrameAnimation kFA)
  {
    if(fromAnimation == null)
      initiateTransition(kFA, kFA, 1.0);
    else if (toAnimation != kFA)
      initiateTransition(toAnimation, kFA, 0.0);
  }
  
  public void initiateTransition(
    KeyFrameAnimation from,
    KeyFrameAnimation to,
    double transition)
  {
    this.fromAnimation = from;
    this.toAnimation = to;
    this.transition = transition;
  }  
  
  public static Point3D[] orientAnimationVertices(MotionChange mC, Point3D[] p)
  {
    Point3D[] returnPts = new Point3D[p.length];

    
    if (mC.newDirection != null)
    {
      Matrix rotationMatrix = new Matrix();
      rotationMatrix.rotateY(mC.newDirection.getYRotation());
      for (int i = 0; i < returnPts.length; i++)
        returnPts[i] = Point3D.matrixTransform(rotationMatrix, p[i]);
    }
    else
    {
      for (int i = 0; i < returnPts.length; i++)
        returnPts[i] = p[i];
    }        
    
    return returnPts;
    
  }

  /**
	 * @return Returns the transition.
	 */
  public double getTransition()
  {
    return transition;
  }

  /**
	 * @param transition
	 *          The transition toAnimation set.
	 */
  public void setTransition(double transition)
  {
    this.transition = transition;
  }

  /**
	 * @return Returns the beat.
	 */
  public double getBeat()
  {
    return beat;
  }

  /**
	 * @param beat
	 *          The beat toAnimation set.
	 */
  public void setBeat(double beat)
  {
    this.beat = beat;
  }

  /**
	 * @return Returns the fromAnimation.
	 */
  public KeyFrameAnimation getFromAnimation()
  {
    return fromAnimation;
  }

  /**
	 * @param from
	 */
  public void setFromAnimation(KeyFrameAnimation from)
  {
    this.fromAnimation = from;
  }

  /**
	 * @return Returns the prevTime.
	 */
  public double getPrevTime()
  {
    return prevTime;
  }

  /**
	 * @param prevTime
	 */
  public void setPrevTime(double prevTime)
  {
    this.prevTime = prevTime;
  }

  /**
	 * @return Returns the toAnimation.
	 */
  public KeyFrameAnimation getToAnimation()
  {
    return toAnimation;
  }

  /**
	 * @param toAnimation
	 *          The toAnimation toAnimation set.
	 */
  public void setToAnimation(KeyFrameAnimation toAnimation)
  {
    this.toAnimation = toAnimation;
  }
  
  /**
   * @return Returns the elapsed.
   */
  public double getElapsed()
  {
    return elapsed;
  }

  /**
   * @param elapsed The elapsed to set.
   */
  public void setElapsed(double elapsed)
  {
    this.elapsed = elapsed;
  }  

  public static double lerp(double t, double a, double b)
  {
    return a + t * (b - a);
  }
  
  public void onBehaviorListChange(BehaviorListChangedEvent bce)
  {
    if (bce.getType() == BehaviorListChangedEvent.CHANGED) { 
      AbstractBehavior to = bce.getBehavior();
    }    
  }
  
} // end