var com = com||{};
com.wo2online = com.wo2online||{};

com.wo2online.Animator = function() {
  var FRAMERATE = 20;
  var ANIMATION_LENGTH = 1;
  var EASE_IN_FACTOR = 1;
  var EASE_OUT_FACTOR = 1;
  
  /**
   * The Animator class, animates things in a linear, bezier or custom way. 
   *
   * @param {Object} options Optional options parameter. This may contain:
   *        {Number} frameratesilent (defaults to 20 frames per second)
   *        {Number} animationLength (defaults to 1 second)
   *        {Number} easeFactor (defaults to 1)
   * @namespace com.wo2online
   * @class Animator
   * @constructor
   */
  var Animator = function(onAnimate,onAnimationEnd,options) {
    this.onAnimate = onAnimate;
    this.onAnimationEnd = onAnimationEnd;
    this.options = options||{};
    this.reset();
  };
  
  /**
   * @method _clearAnimationTimer
   * @private
   */
  function _clearAnimationTimer() {
    if (this.animationTimer) {
      window.clearInterval(this.animationTimer);
      this.animationTimer = false;
    }
  }
  
  /**
    * creates a bezier animator, [0,1] -> [0,1]
    * @param c1 ease in parameter, 1 is no easing, less slows down, negative bounces, more speeds up
    * @param c2 ease out parameter, 1 is no easing
    * @method _bezier
    * @private
    * @returns function
    */
  function _bezier(c1,c2) {
    var p1 = 0;
    var p2 = 1;
    c1 = p1+c1/3;
    c2 = p2-c2/3;
    return function(s) {
      //this is bezier easing
      return (p2+3*c1-3*c2-p1)*(s*s*s) + (3*c2-6*c1+3*p1)*(s*s) + ((3*c1-3*p1)*s) + p1;
    }
  }
 
  /**
   * (re)starts animation
   * @method play
   */
  Animator.prototype.play = function() {
    if (this.playing) {return}
    this.al = this.options.animationLength||ANIMATION_LENGTH;
    this.fr = this.options.framerate||FRAMERATE;
    this.delay = Math.round(1000/this.fr);
    this.steps = Math.round(1000*this.al/this.delay);
    this.c1 = (this.options.easeInFactor||EASE_IN_FACTOR);
    this.c2 = (this.options.easeOutFactor||EASE_OUT_FACTOR);
    this.easeFunction = (this.options.easeFunction||_bezier(this.c1,this.c2));
    var self = this;
    this.animationTimer = window.setInterval(function() {
      self.frame.call(self);
    },this.delay);
    this.playing = true;
  };
  
  /**
   * resets animation
   * @method reset
   */
  Animator.prototype.reset = function() {
    _clearAnimationTimer.call(this);
    this.step = 0;
    this.playing = false;
  };
  
  /**
   * stops animation
   * @method stop
   */
  Animator.prototype.stop = function() {
    _clearAnimationTimer.call(this);
    if (this.onAnimationEnd) {this.onAnimationEnd.call(this);}
    this.step = 0;
    this.playing = false;
  };
  
  /**
   * pauses animation
   * @method pause
   */
  Animator.prototype.pause = function() {
    _clearAnimationTimer.call(this);
    this.playing = false;
  };
 
  /**
   * plays one animation frame
   * @method frame
   */
  Animator.prototype.frame = function() {
    //normalize step to the interval [0,1]
    var normStep = (this.step/this.steps);  
    //calculate value on interval [0,1]
    var normValue = this.easeFunction(normStep);
    this.value = normValue;
    this.onAnimate.call(this,normValue);
    this.step++;
    
    if (this.step>this.steps) {
      this.stop();
    }
  };

  return Animator;
}();