/*
Copyright (c) 2009 Victor Stanciu - http://www.victorstanciu.ro

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

Carousel = Class.create(Abstract, {
	initialize: function (scroller, slides, controls, options) {
		this.scrolling	= false;
		this.scroller	= $(scroller);
		this.slides		= slides;
    this.controlName = controls;
		this.controls	= $$(controls);

		this.options    = Object.extend({
            duration:           1,
            auto:               false,
            frequency:          3,
            visibleSlides:      1,
            moveSteps:          1,
            controlClassName:   'carousel-control',
            jumperClassName:    'carousel-jumper',
            disabledClassName:  'carousel-disabled',
            selectedClassName:  'carousel-selected',
            circular:           false,
            wheel:              false,
            effect:             'scroll',
            phaseDuration:      0.5,
            phaseOpacity:       0.3,
            transition:         'sinoidal'
        }, options || {});
        
        if (this.options.effect == 'fade') {
            // this.options.circular = true;
        }
		this.slides.each(function(slide, index) {
			slide._index = index;
        });
            
		if (this.controls) {
            this.controls.invoke('observe', 'click', this.click.bind(this));
            this.killPrev();
    }
        
    if (this.options.wheel) {            
        this.scroller.observe('mousewheel', this.wheel.bindAsEventListener(this)).observe('DOMMouseScroll', this.wheel.bindAsEventListener(this));;
    }

    if (this.options.auto) {
        this.start();
    }
		if (this.options.initial) {
			if (this.options.initial == 'last') {
				var initialIndex = this.slides.length - 1;
			      } else {
				var initialIndex = this.slides.indexOf($(this.options.initial));
			      }
			if (initialIndex > (this.options.visibleSlides - 1) && this.options.visibleSlides > 1) {               
				if (initialIndex > this.slides.length - (this.options.visibleSlides + 1)) {
					initialIndex = this.slides.length - this.options.visibleSlides;
				}
			}
            this.moveTo(this.slides[initialIndex]);
		}else{
			this.moveTo(this.slides[0]);
		}
	},

  click: function (event) {
    this.stop();

		var element = event.currentTarget;
        if( typeof(element) == 'undefined' ){//fallback for ie 7/8
            //element = event.findElement('.'+this.jumperClassName);
            element = event.findElement('a');
            if( typeof(element) == 'undefined' ){
                //element = event.findElement('.'+this.jumperClassName);
                element = event.findElement('li');
            }
        }

		if (!element.hasClassName(this.options.disabledClassName)) {
			if (element.hasClassName(this.options.controlClassName)) {
				eval("this." + element.getAttribute('rel') + "()");
            } else if (element.hasClassName(this.options.jumperClassName)) {
            	if( !isNaN(element.getAttribute('rel')) ){
            		this.moveTo(this.slides[element.getAttribute('rel')]);
            	}else{
                this.moveTo(element.getAttribute('rel'));
            	}
              if (this.options.selectedClassName) {
                  this.controls.invoke('removeClassName', this.options.selectedClassName);
                  element.addClassName(this.options.selectedClassName);
              }
            }
        }
    
    if (element.rel != 'pause' && element.rel != 'resume') {
      this.deactivateControls();
    }

		event.stop();
    },

  toggleJumpers: function (slide) {

    buttons = this.controls;
    
    selectedClassName = this.options.selectedClassName;
    
    buttons.each(function (button) {
      if (button.rel == slide.id) {
        button.addClassName(selectedClassName);
      } else {
        button.removeClassName(selectedClassName);
      }
    });
    
  },

	moveTo: function (element) {
    if (this.options.beforeMove && (typeof this.options.beforeMove == 'function')) {
			this.options.beforeMove();
    }

	this.toggleJumpers(element);

		this.previous = this.current ? this.current : this.slides[0];
		this.current  = $(element);
		
		this.previous.addClassName('outbound');
		this.current.removeClassName('outbound');

		var scrollerOffset = this.scroller.cumulativeOffset();
		var elementOffset  = this.current.cumulativeOffset();

		if (this.scrolling) {
			this.scrolling.cancel();
		}
		
		var transition;
    switch (this.options.transition) {
        case 'spring':
            transition = Effect.Transitions.spring;
            break;
        case 'sinoidal':
        default:
            transition = Effect.Transitions.sinoidal;
            break;
    }
    
    switch (this.options.effect) {
      case 'fade':               
          this.scrolling = new Effect.Opacity(this.scroller, {
              from:   1.0,
              to:     0,
              duration: this.options.duration,
              afterFinish: (function () {
                  this.scroller.scrollLeft = elementOffset[0] - scrollerOffset[0];
                  this.scroller.scrollTop  = elementOffset[1] - scrollerOffset[1];

                  new Effect.Opacity(this.scroller, {
                      from: 0,
                      to: 1.0,
                      duration: this.options.duration,
                      afterFinish: (function () {
                          if (this.controls) {
                              this.activateControls();
                          }
                          if (this.options.afterMove && (typeof this.options.afterMove == 'function')) {
                              this.options.afterMove();
                          }
                      }).bind(this)
                  });
              }
          ).bind(this)});
      break;
      case 'phase':      
        this.scrolling = new Effect.Opacity(this.scroller, {
          from: 1.0,
          to: this.options.phaseOpacity,
          duration: this.options.phaseDuration,
          afterFinish: (function () {
            new Effect.SmoothScroll(this.scroller, {
              duration: this.options.duration,
              x: (elementOffset[0] - scrollerOffset[0]),
              y: (elementOffset[1] - scrollerOffset[1]),
              transition: transition,
              afterFinish: (function () {
                new Effect.Opacity(this.scroller, {
                  from: this.options.phaseOpacity,
                  to: 1.0,
                  duration: this.options.phaseDuration,
                  afterFinish: (function () {
                    if (this.controls) {
                        this.activateControls();
                    }
                    if (this.options.afterMove && (typeof this.options.afterMove == 'function')) {
                        this.options.afterMove();
                    }
                  }).bind(this)
                });
              }).bind(this)
            });
          }).bind(this)
        });
      break;
      case 'scroll':
      default:
          this.scrolling = new Effect.SmoothScroll(this.scroller, {
              duration: this.options.duration,
              x: (elementOffset[0] - scrollerOffset[0]),
              y: (elementOffset[1] - scrollerOffset[1]),
              transition: transition,
              afterFinish: (function () {
                  if (this.controls) {
                      this.activateControls();
                  }
                  if (this.options.afterMove && (typeof this.options.afterMove == 'function')) {
                      this.options.afterMove();
                  }                        
                  this.scrolling = false;
              }).bind(this)});
      break;            
    }

    this.updateControlsState();
		return false;
	},

	prev: function () {
		if (this.current) {
			var currentIndex = this.current._index;
			var prevIndex = (currentIndex == 0) ? (this.options.circular ? this.slides.length - 1 : 0) : currentIndex - this.options.moveSteps;
        } else {
            var prevIndex = (this.options.circular ? this.slides.length - 1 : 0);
        }

		if (prevIndex == (this.slides.length - 1) && this.options.circular && this.options.effect != 'fade') {
			this.scroller.scrollLeft =  (this.slides.length - 1) * this.slides.first().getWidth();
			this.scroller.scrollTop =  (this.slides.length - 1) * this.slides.first().getHeight();
			prevIndex = this.slides.length - 2;
        }
    
    this.updateControlsState();
    
		this.moveTo(this.slides[prevIndex]);
	},

	next: function () {
		if (this.current) {
			var currentIndex = this.current._index;
			var nextIndex = (this.slides.length - 0 < currentIndex + this.options.moveSteps) ? (this.options.circular ? 0 : currentIndex) : currentIndex + this.options.moveSteps;
        } else {
            var nextIndex = 1;
        }

		if (nextIndex == 0 && this.options.circular && this.options.effect != 'fade') {
			this.scroller.scrollLeft = 0;
			this.scroller.scrollTop  = 0;
			nextIndex = 1;
        }

//		if (nextIndex > this.slides.length - (this.options.visibleSlides + 1)) {
//			nextIndex = this.slides.length - this.options.visibleSlides;
//		}
		
    this.updateControlsState();
    
    if( typeof(this.slides[nextIndex]) == 'undefined'){
		  this.moveTo(this.slides[0]);
    }else{
		  this.moveTo(this.slides[nextIndex]);
    }
	},

	first: function () {
		this.moveTo(this.slides[0]);
    },

	last: function () {
		this.moveTo(this.slides[this.slides.length - 1]);
    },

	toggle: function () {
		if (this.previous) {
			this.moveTo(this.slides[this.previous._index]);
        } else {
            return false;
        }
    },

	stop: function () {
		if (this.timer) {
			clearTimeout(this.timer);
		}
	},

	start: function () { 
        this.periodicallyUpdate();
    },

	pause: function () {
		this.stop();
		this.activateControls();
    },

	resume: function (event) {
		if (event) {
			var related = event.relatedTarget || event.toElement;
			if (!related || (!this.slides.include(related) && !this.slides.any(function (slide) { return related.descendantOf(slide); }))) {
				this.start();
            }
        } else {
            this.start();
        }
    },

	periodicallyUpdate: function () {
		if (this.timer != null) {
			clearTimeout(this.timer);
			this.next();
        }
		this.timer = setTimeout(this.periodicallyUpdate.bind(this), this.options.frequency * 1000);
    },
    
    wheel: function (event) {
        event.cancelBubble = true;
        event.stop();
        
		var delta = 0;
		if (!event) {
            event = window.event;
        }
		if (event.wheelDelta) {
			delta = event.wheelDelta / 120; 
		} else if (event.detail) { 
            delta = -event.detail / 3;	
        }        
       
        if (!this.scrolling) {
            this.deactivateControls();
            if (delta > 0) {
                this.prev();
            } else {
                this.next();
            }            
        }
        
		return Math.round(delta); //Safari Round
    },

	deactivateControls: function () {
    this.controls.invoke('addClassName', this.options.disabledClassName);
    },
  killNext: function () {
    $$(this.controlName+'[rel="next"]').invoke('addClassName', 'control-dead');
  },
  killPrev: function () {
    $$(this.controlName+'[rel="prev"]').invoke('addClassName', 'control-dead');
  },
  restoreNext: function () {
    $$(this.controlName+'[rel="next"]').invoke('removeClassName', 'control-dead');
  },
  restorePrev: function () {
    $$(this.controlName+'[rel="prev"]').invoke('removeClassName', 'control-dead');
  },
	activateControls: function () {
		this.controls.invoke('removeClassName', this.options.disabledClassName);
	},
	updateControlsState: function() {
		
		if( this.current._index + this.options.moveSteps > this.slides.length - 1 ){
			this.killNext();
		}else{
			this.restoreNext();
		}
		
		if( this.current._index - this.options.moveSteps < 0 ){
			this.killPrev();
		}else{
			this.restorePrev();
		}
		
		if (this.options.selectedClassName) {
			this.controls.invoke('removeClassName', this.options.selectedClassName);
			var self = this
			this.controls.each(function( value, index){
				if( value.getAttribute('rel') == self.current._index || value.getAttribute('rel') == self.current.id){
					value.addClassName(self.options.selectedClassName);
				}
			});
		}
		
	}
});


Effect.SmoothScroll = Class.create();
Object.extend(Object.extend(Effect.SmoothScroll.prototype, Effect.Base.prototype), {
	initialize: function (element) {
		this.element = $(element);
		var options = Object.extend({ x: 0, y: 0, mode: 'absolute' } , arguments[1] || {});
		this.start(options);
    },

	setup: function () {
		if (this.options.continuous && !this.element._ext) {
			this.element.cleanWhitespace();
			this.element._ext = true;
			this.element.appendChild(this.element.firstChild);
        }

		this.originalLeft = this.element.scrollLeft;
		this.originalTop  = this.element.scrollTop;

		if (this.options.mode == 'absolute') {
			this.options.x -= this.originalLeft;
			this.options.y -= this.originalTop;
        }
    },

	update: function (position) {
		this.element.scrollLeft = this.options.x * position + this.originalLeft;
		this.element.scrollTop  = this.options.y * position + this.originalTop;
    }
});

