/* 
 * Based upon (but heavily modified, and updated to work with most recent Prototype) 
 * work by Sébastien Gruhier (http://xilinus.com, http://itseb.com)
 * 
 * this modified version is redistributed under the same license as the source work
// 
// 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.
//
 * VERSION 0.26
 */

var Carousel = Class.create();
Carousel.prototype = {
  // Constructor
	initialize: function(carouselElemID) {
	this.carouselElemID = carouselElemID;
	this.options = Object.extend({
			numVisible:           4,
			scrollInc:            3,
			animParameters:      {},
			buttonStateHandler:  null,
			animHandler:         null,
			ajaxHandler:         null,
			initDoneHandler:     null,
			queue:               "carousel",
			size:                0,
			prevElementID:       "prev-arrow",
			nextElementID:       "next-arrow",
			carouselListClass:   "pcarousel-list",
			hasTabs:	     true, //does each carousel item have tabs inside it that should be part of the flow/paging
			ajaxParameters:      null,
			url:                 null
		}, arguments[1] || {});

		this.initDone = false;
		this.animRunning = "none";
		this.requestIsRunning = false;
		this.tabIndex = 0;

		// add afterFinish options to animParameters (store old function)
		this.animAfterFinish = this.options.animParameters.afterFinish;
		Object.extend(this.options.animParameters, {afterFinish:  this._animDone.bind(this), queue: { position:'end', scope: this.options.queue }});
		
		// Event bindings
		this.prevScroll = this._prevScroll.bindAsEventListener(this);
		this.nextScroll = this._nextScroll.bindAsEventListener(this);
		this.onComplete = this._onComplete.bindAsEventListener(this);
		this.onFailure  = this._onFailure.bindAsEventListener(this);

		Event.observe(this.options.prevElementID, "click", this.prevScroll);
		Event.observe(this.options.nextElementID, "click", this.nextScroll);

		// Get DOM UL element
// 		var carouselListClass = ;
		this.carouselList = $(this.carouselElemID).down('ul.'+this.options.carouselListClass); 
		//document.getElementsByClassName(carouselListClass, $(carouselElemID))[0]

		this.options.size =  $A(this.carouselList.childElements()).length;

		//lastly, attach the Carousel object to the parentElement

		$(this.carouselElemID)._CAROUSEL = this;

		// Init data
		this._init();


	},
  // Destructor
 	destroy: function() {
		Event.stopObserving(this.options.prevElementID, "click", this.prevScroll);
		Event.stopObserving(this.options.nextElementID, "click", this.nextScroll);
	},
	scrollTo: function(newStart) {
		var old_inc = this.options.scrollInc;
		this.ignoreNoMoreImages = true;
		if(newStart > this.currentIndex) {
			this.options.scrollInc = newStart - this.currentIndex;
			this._nextScroll(this);
		} else {
			this.options.scrollInc = this.currentIndex - newStart;
			this._prevScroll(this);
		}
    		this.options.scrollInc = old_inc;
	},
	/* "Private" functions */
	_init: function() {
		this.currentIndex = 0;
		// Ajax content
		if (this.options.url)
			this._request(this.currentIndex, this.options.numVisible);
		// Static content
		else {
			this._getLiElementSize('tabs');
			if (this.options.initDoneHandler) 
				this.options.initDoneHandler(this);
			this._updateButtonStateHandler(this.options.prevElementID, false);
			this._updateButtonStateHandler(this.options.nextElementID, this.options.size > this.options.numVisible);
		}

	},
	_prevScroll: function(event) {
		//reget list size
		this._getLiElementSize('tabs');
		//console.log(this.animRunning+' - '+this.tabIndex);
		if (this.animRunning != "none" || (! this.options.hasTabs && this.currentIndex == 0  ) || ( this.currentIndex == 0 && (this.options.hasTabs && this.tabIndex == 0 ) ) )
			return;


		if ( this.options.hasTabs && this.tabIndex % 2 ) {     //full carusel rotation
			var curTabContent = $( this.carouselList.childElements()[this.currentIndex] );
			var type = curTabContent.down('div.rowlist').readAttribute('id');       
			type = type.replace(/^ptc_/, '');
			var nTab = curTabContent.down('ul.tablist').down( 'a.refc_'+type );   // the first one will be the non-current one... .next( 'li' ).down('a');
			//console.log(nTab);
			var nTabType = nTab.readAttribute('class');
			nTabType = nTabType.replace(/^.*refc_/,'').replace(/ .+$/,'');
			this._toggleTabs(nTab, nTabType);
		
			this._updateButtonStateHandler(this.options.prevElementID, this.currentIndex != 0 || ( this.options.hasTabs && this.tabIndex != 0 ), this);
			this._updateButtonStateHandler(this.options.nextElementID, (this.currentIndex + this.options.numVisible < this.options.size), this);

			return false;
		} else if ( this.options.hasTabs ) {
			this.tabIndex--;
			if ( this.tabIndex < 0 )
				this.tabIndex = 0;
		//console.log( this.tabIndex );
		}



		var inc = this.options.scrollInc;

		if (this.currentIndex - inc < 0)
		inc = this.currentIndex;




		this._scroll(inc)		  
			return false;
	}, 
	_nextScroll: function(event) {    
		//reget list size
		this._getLiElementSize();
// console.log('.!!.');
		if (this.animRunning != "none" || (!this.options.hasTabs && this.currentIndex >= this.options.size - 1 ) || ( this.options.hasTabs && this.tabIndex+1 >= this.tabSize ) )
		return false;
// console.log();
// console.dirxml(Event.element(event));
//  		var thar = Event.element(event) ? $( Event.element(event) ) : null ); //event may just be the item itself?
// 
 
		// console.log( 'tin: '+this.tabIndex);
		if ( this.options.hasTabs && ( ! this.tabIndex || ! (this.tabIndex % 2) ) ) //need to eventually code in # of tabs in each cell instead of assuming 2
		{    
			var curTabContent = $( this.carouselList.childElements()[this.currentIndex] );
			//console.log(curTabContent);
			var type = curTabContent.down('div.rowlist').readAttribute('id');       
			type = type.replace(/^ptc_/, '');

			//this will also allow us to target the TAB for the current CONTENT
			var nTab = curTabContent.down('ul.tablist').down( 'a.refc_'+type ).up('li').next( 'li' ).down('a');
			var nTabType = nTab.readAttribute('class');
			nTabType = nTabType.replace(/^.*refc_/,'').replace(/ .+$/,'');
			//console.log(nTabType);
			//var nTabType = nTab.
			this._toggleTabs(nTab, nTabType);
			//console.log( this.tabIndex );
			//this._updateButtonStateHandler(this.options.prevElementID, this.currentIndex != 0, this);
			this._updateButtonStateHandler(this.options.prevElementID, this.currentIndex != 0 || ( this.options.hasTabs && this.tabIndex != 0 ), this);
			this._updateButtonStateHandler(this.options.nextElementID, (this.currentIndex + this.options.numVisible < this.options.size), this);

			return false;
		} else if (this.options.hasTabs) {
			this.tabIndex++;
			if ( this.tabIndex >= this.tabSize )
				this.tabIndex = this.tabSize;
			//console.log( this.tabIndex );
		}       
		// Check if there are enough elements in cache
		if (this.currentIndex + this.options.numVisible + this.options.scrollInc <= this.options.size) 
			this._scroll(-this.options.scrollInc);
		else {
			// Compute how many are in the cache
			this.nbInCache = this.options.size - (this.currentIndex + this.options.numVisible);
			if (this.options.url && this.noMoreImages == false) 
				this._request(this.currentIndex + this.options.numVisible + this.nbInCache, this.options.scrollInc - this.nbInCache);
			else  {
				if (this.nbInCache > 0)
					this._scroll(-this.nbInCache);
			}
		}
		return false;
	},
	//if the inner carousel items have tabs
	_toggleTabs: function(thar,type) {
		var theCarousel = this;

		thar = $(thar);
		thar.blur();

		if ( thar.up('li').hasClassName('selected') ) { return; } 

		var onDeck = $('ptc_'+type);
		var curTabCont = onDeck.previous('div');
		var curTab = thar.up('li').previous('li');
		if ( ! curTabCont ) { // go next
			theCarousel.tabIndex--;
			if ( theCarousel.tabIndex < 0 )
			theCarousel.tabIndex = 0;

			curTabCont = onDeck.next('div');
			curTab = thar.up('li').next('li');
		} else {


			theCarousel.tabIndex++;
			if ( theCarousel.tabIndex >= ( 2 * theCarousel.options.size ) )
			theCarousel.tabIndex = ( ( 2 * theCarousel.options.size ) - 1 );

			//we hit the second tab, so necessarily the global back button should be enabled
			$(theCarousel.options.prevElementID).removeClassName( 'prev-disabled' ).removeClassName( 'prev-disabled-horizontal' );
		}

		var obj = this;
		new Effect.Move(thar, {
			queue: { position:'end', scope:obj.options.queue },
			duration:0.1,
			transition: Effect.Transitions.none,
			afterFinish:function() { 

				new Effect.Fade( thar.up('li'), {  
					duration: 0.25, 
					from:1.0, 
					to:0.5,  
					beforeStart:function(){
						new Effect.Morph(thar.up('li'), {
						style: 'z-index:15;', //'background:#FFF; border-bottom: 2px solid #146489;',
						duration: 0.25,
						afterFinish:function(){ thar.up('li').addClassName('selected'); }
						});
					},
					afterFinish:function(){ Effect.Appear( thar.up('li'), { from:0.5, to:1.0, duration:0.25 }); }
				});

				new Effect.Fade( curTab, {  
					duration: 0.25, 
					from:1.0, 
					to:0.5,  
					beforeStart:function(){
						new Effect.Morph(curTab, {
						style: 'z-index:0;', //'background:#FFF; border-bottom: 2px solid #146489;',
						duration: 0.25,
						afterFinish:function(){ curTab.removeClassName('selected'); }
						});
					},
					afterFinish:function(){ Effect.Appear( curTab, { from:0.5, to:1.0, duration:0.25 }); }
				});

				new Effect.Fade( curTabCont, { duration:0.5 });
				new Effect.Appear( onDeck, { duration:0.5 });

			}
		});
	},
	_request: function(start, nb) {
		if (this.options.url && ! this.requestIsRunning) {
			this.requestIsRunning = true;
			
			if (this.options.ajaxHandler)
				this.options.ajaxHandler(this, "before");
			
			var params = "start=" + start + "&nb=" + nb;
			if (this.options.ajaxParameters != null)
				params += "&" + this.options.ajaxParameters
		
			new Ajax.Request(this.options.url, {parameters: params, onComplete: this.onComplete, onFailure: this.onFailure});
		}
	},
	_onComplete: function(originalRequest){
		this.requestIsRunning = false;
		this.carouselList.innerHTML += originalRequest.responseText;
		// Compute how many new elements we have
		var size = this.options.size;
		this.options.size = this.carouselList.getElementsByTagName("li").length;
		var inc = this.options.size - size;
		
		// First run, compute li size
		if (this.initDone == false) {
			this._getLiElementSize()
			this.currentIndex = 0;
			this.initDone = true;
			if (this.options.initDoneHandler) 
				this.options.initDoneHandler(this);
			
			// Update button states
			this._updateButtonStateHandler(this.options.prevElementID, false);
			this._updateButtonStateHandler(this.options.nextElementID, this.options.size == this.options.numVisible);
			this.noMoreImages = this.options.size < this.options.numVisible
		}
		// Add images
		else {
			if (!this.ignoreNoMoreImages)
				this.noMoreImages = inc != this.options.scrollInc;
			else
				this.ignoreNoMoreImages = false;
			// Add images
			if (inc > 0) {
				this._scroll(-inc, this.noMoreImages)
			}
			// No more images, disable next button
			else {
				if (this.nbInCache >0)
					this._scroll(-this.nbInCache, true);
				
				this._updateButtonStateHandler(this.options.nextElementID, false);
			}
		}
			
		if (this.options.ajaxHandler)
			this.options.ajaxHandler(this, "after");
	},	
	_onFailure: function(originalRequest){    
		this.requestIsRunning = false;
	},
	_animDone: function(event){   
		if (this.options.animHandler)
			this.options.animHandler(this.carouselElemID, "after", this.animRunning);
		
		this.animRunning = "none";
		// Call animAfterFinish if exists
		if (this.animAfterFinish)
			this.animAfterFinish(event);
	},
	_updateButtonStateHandler: function(button, state) {
			if (this.options.buttonStateHandler) 
			this.options.buttonStateHandler(button, state, this);
	},
	_scroll: function(delta, forceDisableNext) {      

		this.animRunning = delta > 0 ? "prev" : "next";

		if (this.options.animHandler)
			this.options.animHandler(this.carouselElemID, "before", this.animRunning);

		this.currentIndex -= delta;

		this._updateButtonStateHandler(this.options.prevElementID, this.currentIndex != 0 );
		
		if (this.options.url && this.noMoreImages == false)
			enable = true;
		else
			enable = (this.currentIndex + this.options.numVisible < this.options.size);

		this._updateButtonStateHandler(this.options.nextElementID, (forceDisableNext ? false : enable) );

		new Effect.MoveBy(this.carouselList, 0, delta * this.elementSize, this.options.animParameters);
	},
	
	_getLiElementSize: function(lim) {

    if ( ! lim ) { //sometimes we clal get element size, just for the current tabs, not to recalc the whole dealie
		  var li = $(this.carouselList.getElementsByTagName("li")[0]);
		  this.elementSize = li.getDimensions().width + parseFloat(li.getStyle("margin-left")) + parseFloat(li.getStyle("margin-right"));
    }


		if ( this.options.hasTabs ) 
			this.tabSize = $( this.carouselList ).select("ul.tablist li").length; 
		//[this.currentIndex] ).down('ul.tablist').select('li').length;
	}
}
	



