/**
* @fileoverview Venda.Widget.Slider - animated slide functionality.
*
* This widget give us the ability to slide images/divs.
*
* @author Dan Brook <dbrook@venda.com>
* @author Nabi Arshad <narshad@venda.com>
*/

Venda.namespace("Widget.Slider");

/**
 * Sets up the object in preparation for rendering.
 *
 * @constructor
 * @class Transform a list into a slidable view of its items
 * @param Object An object representing the Slider's <tt><a href='#config'>config</a></tt>
 * @requires YAHOO             /venda-support/js/external/yui/build/yahoo/yahoo.js
 * @requires YAHOO.util.Event  /venda-support/js/external/yui/build/event/event.js
 * @requires YAHOO.util.DOM    /venda-support/js/external/yui/build/dom/dom.js
 * @requires YAHOO.util.Motion /venda-support/js/external/yui/build/animation/animation.js
 */
Venda.Widget.Slider = function(config) {
	// Support original calling style.
	if( !( this instanceof Venda.Widget.Slider ) ) {
		return new Venda.Widget.Slider({
			// sliderID,omaxNum,odisplayCount,oslideAmtNum,slideLeft,slideRight,duration
			sliderID:      arguments[0],
			maxNum:        arguments[1],
			displayCount:  arguments[2],
			slideAmtNum:   arguments[3],
			slideLeft:     arguments[4],
			slideRight:    arguments[5],
			duration:      arguments[6],
			slideEnd:      arguments[7],
            slideStart:arguments[8]
		});
	}

	this.config      = config;
	this.scrollCount = 1;
	this.sliding     = false;
};

Venda.Widget.Slider.prototype = {
	/**
	 * The configuration object as passed into the constructor.
	 * <p/>
	 * It <strong>must</strong> have following properties:
	 * <dl>
	 *  <dt>sliderID</dt>
	 *  <dd>An element id (or object) representing the list of items in the Slider</dd>
	 *  <dt>maxNum</dt>
	 *  <dd>This will be the value for maxNum</dd>
	 *  <dt>displayCount</dt>
	 *  <dd>How many items to display at a time</dd>
	 *  <dt>slideAmtNum</dt>
	 *  <dd>The amount of scroll items</dd>
	 *  <dt>slideLeft</dt>
	 *  <dd>An element id (or object) representing the button to slide left</dd>
	 *  <dt>slideRight</dt>
	 *  <dd>An element id (or object) representing the button to slide right</dd>
	 *  <dt>duration</dt>
	 *  <dd>The length of time of the slide animation e.g the smaller the number the quicker the animation</dd>
	 * </dl>
	 * <em>NB - A <code>Count</code> is a dynamic number updated for each slide. An <code>Amt</code> is a static number.</em>
	 */

	config:        {},
	/**
	 * The current scoll position.
	 * @private
	 * @type Number
	 */
	scrollCount:   null,
	/**
	 * Indicate whether the sliding animation is taking place
	 * @private
	 * @type Boolean
	 */
	sliding:       null,

	/**
	 * The element containing the slider.
	 * @private
	 * @type HTMLElement
	 */
	slider: null,

	/**
	 * Set up the slider functionality.
	 */
	init: function() {
		this.slider = YAHOO.util.Dom.get(this.config.sliderID);
		YAHOO.util.Event.on(this.config.slideLeft,  'click', this.getSlideHandler('slideLeft'),  {}, this);
		YAHOO.util.Event.on(this.config.slideRight, 'click', this.getSlideHandler('slideRight'), {}, this);
		YAHOO.util.Event.on(this.config.slideEnd, 'click', this.getSlideHandler('slideEnd'), {}, this);
		YAHOO.util.Event.on(this.config.slideStart, 'click', this.getSlideHandler('slideStart'), {}, this);

		if (this.itemCount() > this.config.displayCount) {
			YAHOO.util.Dom.get(this.config.slideRight).style.visibility = 'visible';
		} else {
			YAHOO.util.Dom.get(this.config.slideRight).style.visibility = 'hidden';
		}
	},

	
	/**
	 * Provide the appropriate slide handler given a direction
	 * @param method A string which can either be slideLeft or slideRight.
	 * @returns A Function.
	 * @private
	 */
	getSlideHandler: function(method) {
		var self = this;
		return function(evt) {
			self.doSlide(evt, method);
		};
	},
	
	/**
	 * Calculate the amount of items to slide by.
	 * @returns The amount to slide by.
	 * @private
	 */
	slideAmt: function(slideAmtNum) {
		var remainder	    = this.itemCount() % this.config.displayCount;
		var amountToSlideBy = remainder == 0 ? this.config.displayCount: remainder;
		return this.sliderItems()[0].clientWidth * (slideAmtNum || amountToSlideBy);
	},

	/**
	 * Calculate whether the slide view is at the last element
	 * @returns A Boolean.
	 * @private
	 */
	atStart: function() {
		return this.scrollCount <= 1;
	},
	/**
	 * Calculate whether the slide view is at the last element
	 * @returns A Boolean.
	 * @private
	 */
	atEnd: function() {
		var stepAmount = this.config.slideAmtNum
					   ? this.scrollCount * this.config.slideAmtNum + this.config.displayCount
					   : this.scrollCount * this.config.displayCount;
		return stepAmount > this.itemCount()
		    || stepAmount > this.config.maxNum;
	},
	
	/**
	 * If the slide view can move do so.
	 * @private
	 */
	doSlide: function(evt, method) {
		if(this.sliding)
			return;
		

		var attrs = this[method]();
		
		
		if(attrs) {
			this.slide(evt, attrs);
		}
	},
	
	/**
	 * Provide necessary information to slide left. If the view can slide to
	 * the left then an Object is returned otherwise False is returned.
	 * @returns False or an Object
	 * @private
	 */
	slideLeft: function(atStart, atEnd) {
		if(this.atStart()) {
			return;
		} else {
			this.scrollCount--;
			return { points: { by: [this.slideAmt(this.slideAmtNum), 0] } };
		}
	},
	
	/**
	 * Provide necessary information to slide right. If the view can slide to
	 * the right then an Object is returned otherwise False is returned.
	 * @returns False or an Object
	 * @private
	 */
	slideRight: function(atStart, atEnd) {
		if(this.atEnd()) {
			return;
		} else {
			this.scrollCount++;
			return { points: { by: [-this.slideAmt(this.slideAmtNum), 0] } };
		}
	},

    slideEnd: function(atStart, atEnd) {
		if(this.atEnd()) {
			return;
		} else {
            var activeSlider = this.scrollCount;
			this.scrollCount =this.itemCount();
			return { points: { by: [-this.slideAmt(this.itemCount() - activeSlider), 0] } };
		}
	},
    slideStart: function(atStart, atEnd) {
		if(this.atStart()) {
			return;
		} else {
            var activeSlider = this.scrollCount;
			this.scrollCount = 1;
			return { points: { by: [this.slideAmt(activeSlider-1), 0] } };
		}
	},
	/**
	 * Perform the sliding animation.
	 * @private
	 */
	slide: function(evt, attributes) {
		YAHOO.util.Event.stopEvent(evt);
		//control for scroll speed
		var anim = new YAHOO.util.Motion(this.slider, attributes, this.config.duration, YAHOO.util.Easing.easeOut);
		anim.onComplete.subscribe(this.slideComplete, {}, this);
		anim.animate();
		
		this.sliding = true;
	},
	
	/**
	 * Update the slider's buttons as appropriate e.g don't show the slide
	 * left button if the view is at the first item.
	 * @private
	 */
	slideComplete: function() {
		YAHOO.util.Dom.setStyle(this.config.slideLeft,  'visibility', this.atStart() ? 'hidden' : '');
		YAHOO.util.Dom.setStyle(this.config.slideRight, 'visibility', this.atEnd()   ? 'hidden' : '');
		YAHOO.util.Dom.setStyle(this.config.slideStart, 'visibility', this.atStart()   ? 'hidden' : '');
		YAHOO.util.Dom.setStyle(this.config.slideEnd, 'visibility', this.atEnd()   ? 'hidden' : '');
		this.sliding = false;
	},

	/**
	 * Return slider element items as a HTMLCollection.
	 * @returns A HTMLCollection
	 * @private
	 */
	sliderItems: function() {
		return this.slider.getElementsByTagName('li');
	},

	/**
	 * Return the number of items in the slider.
	 * @returns A Number
	 * @private
	 */
	itemCount: function() {
		return this.sliderItems().length;
	}
};
