var BROWSER_PREFIXES = ['-webkit-', '-ms-', '-moz-', '-o-', ''];
var CROSS_BROWSER_PREFIX = '-css3-';

Animation = Class.create({
  initialize: function(options) {
    this.options = {
      container:          '.mainAnimation',               // The css selector to the container.
      repeat:             true,                          // false if the animation must be executed only once.
      frames:             $$('.frame'),                   // The array containing all elements.
      delayDuration:      1,                              // The time between to show one frame to another one. [s]
      effectTransitions:  []                              // An array containing all effects (transition). See Animation.Transition for more info.
    };
    Object.extend(this.options, options || {});
    this.options.frames.each(function(frame, index) {
      this.createEffects.bind(this).delay(this.options.delayDuration * index, frame, index);
    }.bind(this));
  },
  applyCrossBrowserEffect: function(effectArg, element) {
    var fx = new Animation.Transition(effectArg);
    if (fx.getProperty().startsWith(CROSS_BROWSER_PREFIX)) {
      BROWSER_PREFIXES.each(function(prefix) {
        var fxAux = new Animation.Transition(effectArg);
        fxAux.setProperty(fx.getProperty().sub(CROSS_BROWSER_PREFIX, prefix));
        element.apply(fxAux);
      });
    } else {
      element.apply(fx);
    }
  },
  createEffects: function(frame, frameNumber) {
    if (frame) {
      var frameElm = (!!frame.frame) ? $(frame.frame) : $(frame);
      var frameDelay = (!!frame.delay) ? frame.delay : 0;
      var frameEffects = (!!frame.effects) ? frame.effects : [];
    } else {
      throw 'Invalid frame argument';
    }
    if ($(frameElm)) {
      var element = new Animation.Element($(frameElm), {id: frameNumber });
      var effects = this.options.effectTransitions.concat(frameEffects);
      effects.each(function(effectArg) {
        this.applyCrossBrowserEffect.bind(this).delay(frameDelay, effectArg, element);
      }.bind(this) );
      if (this.options.repeat) {
        this.createEffects.bind(this).delay(this.options.delayDuration * this.options.frames.length, frame, frameNumber);
      }
    }
  }
});

Animation.Element = Class.create({
  initialize: function(element, options) {
    this.options = {
        id: $(element).id
    };
    Object.extend(this.options, options || {});
    this.frameSets = 20;
    this.effects = [];
    if (!!$(element)) {
      this.element = $(element);
    } else {
      throw 'Invalid element initialized';
    }
  },
  apply: function(effect) {
    this.execute.bind(this).delay(effect.getDelay(), effect);
  },
  execute: function(effect) {
    var obj = {};
    obj[effect.getProperty().camelize()] = effect.getInitialValue();
    $(this.element).setStyle(obj); // IMPORTANT: This is not cross-browser
    if (!!$$('.csstransitions')[0]) {
      var obj = {};
      obj[effect.getProperty().camelize()] = effect.getFinalValue();
      this.effects.push(effect);
      $(this.element).setStyle(this.getTransitionStyles());
      $(this.element).setStyle(obj);
      this.remove.bind(this).delay(effect.getDuration(), effect);
    } else {
      this.transitWithJS(effect);
    }
  },
  getTransitionStyles: function() {
    var value = "";
    var size = this.effects.length;
    for (var i = 0; i < size; i++) {
      value += this.effects[i].getStyleValue();
      value += (i == size - 1) ? '' : ', ';
    }
    var obj = {};
    BROWSER_PREFIXES.each(function(prefix){
      obj[(prefix + 'transition').camelize()] = value;
    }.bind(this));
    return obj;
  },
  remove: function(effect) {
    for (var i = 0; i < this.effects.length; i++) {
      if (this.effects[i] === effect) {
        this.effects.splice(i, 1);
      }
    }
  },
  transitWithJS: function(effect) {
    switch(effect.getProperty().toLowerCase()) {
      case 'opacity':
        new Effect.Opacity($(this.element), { from: effect.getInitialValue(), to: effect.getFinalValue(), duration: effect.getDuration(), queue: { position: 'end', scope: 'opacityscope' + this.options.id } });
        break;
      case 'transform':
        var values = effect.getFinalValue().split(" ");
        values.each(function(value) {
          var arg = getValuesOfTransform(value);
          if (value.include('translate')) {
            new Effect.Move($(this.element), { x: parseInt(arg[0]), y: parseInt(arg[1]), duration: effect.getDuration(), queue: { position: 'end', scope: 'translatescope'  + this.options.id } });
          }
          if (value.include('scale')) {
            var iniVals = getValuesOfTransform(effect.getInitialValue());
            var p = parseFloat(arg[0]) / parseFloat(iniVals[0]);
            new Effect.Scale($(this.element), p * 100, {scaleFromCenter: true, duration: effect.getDuration(), queue: { position: 'end', scope: 'scalescope' + this.options.id } });
          }
        }.bind(this));
        break;
      default:
        var obj = {};
        obj[effect.getProperty().camelize()] = effect.getFinalValue();
        new Effect.Morph($(this.element), {
          style: obj, 
          duration: effect.getDuration()
        });
    }
  }
});

Animation.Transition = Class.create({
  initialize: function(options) {
    this.options = {
      transition: '', // A shorthand property for setting the four transition properties into a single property. Based on css3 transition properties. See: http://www.w3schools.com/cssref/css3_pr_transition.asp
      transitionProperty: 'all',         // Based on transition-property.
      transitionDuration: '0',           // Based on transition-duration. 
      transitionTimingFunction: 'ease',  // Based on transition-timing-function.
      transitionDelay: '0',              // Based on transition-delay.
      initialValue: '',
      finalValue: ''                    // e.g.: if transitionPropery is 'opacity', final Value must be a number between 0 and 1.
    };
    Object.extend(this.options, options || {});
    this.setTransition();
  },
  getDelay: function() {
    return this.options.transitionDelay;
  },
  getDuration: function() {
    return this.options.transitionDuration;
  },
  getInitialValue: function() {
    return this.options.initialValue;
  },
  getFinalValue: function() {
    return this.options.finalValue;
  },
  getProperty: function() {
    return this.options.transitionProperty;
  },
  getStyleValue: function() {
    return this.options.transition;
  },
  getTimingFunction: function() {
    return this.options.transitionTimingFunction;
  },
  setProperty: function(property) {
    this.options.transitionProperty = property;
    this.options.transition = this.options.transitionProperty + ' ' + this.options.transitionDuration.toString() + 's ' + this.options.transitionTimingFunction + ' 0s';
  },
  setTransition: function() {
    this.transitionObj = this.options;
    if (this.options.transition !== '') {
      var array = this.options.transition.split(' ');
      this.options.transitionProperty       = ((array.length > 0) && (!!array[0])) ? array[0] : this.options.transitionProperty;
      this.options.transitionDuration       = ((array.length > 1) && (!!array[1])) ? array[1] : this.options.transitionDuration;
      this.options.transitionTimingFunction = ((array.length > 2) && (!!array[2])) ? array[2] : this.options.transitionTimingFunction;
      this.options.transitionDelay          = ((array.length > 3) && (!!array[3])) ? array[3] : this.options.transitionDelay;
    }
    this.options.transitionDuration = this.toSeconds(this.options.transitionDuration);
    this.options.transitionDelay = this.toSeconds(this.options.transitionDelay);
    this.options.transition = this.options.transitionProperty + ' ' + this.options.transitionDuration.toString() + 's ' + this.options.transitionTimingFunction + ' 0s';
  },
  // Returns a decimal number of passed value. e.g.: 1.234, '1.234', '1234ms' or '1.234s' will be 1.234
  toSeconds: function(time) {
    var timeNumber;
    time = time.toString();
    if (time.endsWith('ms')) {
      timeNumber = parseFloat(time.sub('ms', '')) / 1000;
    } else {
      timeNumber = parseFloat(time.sub('s', ''));
    }
    return timeNumber;
  }
});


// e.g.: getValuesOfTransform('translate(50px,100px)') -> ['50px', '100px']
function getValuesOfTransform(property) {
  var i = property.indexOf('(');
  var f = property.indexOf(')');
  var s = property.substring(i + 1, f);
  return s.split(',');
}

