var Sliding = {


	/**
	 *	Constants and default properties
	 */
	'options':{
		'form': 'quotationForm',
		'section':'.quotation_section',
		'header':'.quotation_section_title',
		'content':'.quotation_section_content',
		'footer':'.quotation_section_footer',
		'handler':'.quotationModifyLink',
		'elements': $A(  ('input,select,textarea').split(',')  ),
		'err_div': 'error_',
		'nextbutton':'nextButton',
		'disableButton':'disableNextButton',
		'activeButton':'activeNextButton',
		'opened':'opened',
		'closed':'quotation_section_close',
		'active' : 'quotation_section_active',
		'hide': 'hidden',
		'timer': 500,
		'effectDuration': 500,
		'stopEventClass': 'js_ajaxAction',
		'noFxClass' : 'js_nofx',
		'noToggleClass' : 'js_noToggle',
		'methodForward':'evaluation',
		'closeLastSection': true,
		'onSubmit': undefined,
		'onSectionOpen': Class.empty,
		'onSectionClose': Class.empty
	},
	
	
	/**
	 *	Initialization of all sections
	 *	Retrieve all sections such as children of 'parent' parameter. Configure all pointers
	 *	and all functions.
	 *	Make a list of sections and list of elements in section
	 */
	'init': function( parent , options  ){
	
		parent = $( parent || document.body );
		
		Sliding.options = $merge( Sliding.options , options );
		
		this.nextButton = parent.getElement( '#' + Sliding.options[ 'nextbutton' ] );
		this.form = $( Sliding.options[ 'form' ] );
		
		/* 	Retrieve all section such as children of 'parent' parameter	*/
		$ES( Sliding.options[ 'section' ] , parent ).each( function(section , index , array ){
		
			if ( index == 0 ) this._first = section;
		
			this.buildSection( section , index );
		
		}, this );
	
		/* Set submit button functions and pointers */
		this.nextButton.form = this.form;
		
		this.nextButton.addEvent( 'click' , this.forward.bind( this ) );

		this.loaded = true;
		this.makeValidationThread();
		
		return this;
	
	},

	'makeValidationThread': function(){
		if ( ! this.loaded ) return;
		$clear( this.timer );
		if ( Sliding.options[ 'timer' ] ){
			new Element( 'input',{
				'type':'hidden',
				'id':'_sliding_check',
				'class':'hidden hide'
			}).injectBefore( this.nextButton );
			this.timer = this.enableNextButton.periodical( Sliding.options[ 'timer' ] , this );
			
		}
	},
	
	'check': function(){
		if ( $('_sliding_check') && $('_sliding_check').getParent() )
			return true;
		return false;
	},
	
	'buildSection': function( section , index ){
	
		/* Store functionally object */
		section.header = section.getElement( Sliding.options[ 'header' ] );			// title of section
		section.content = section.getElement( Sliding.options[ 'content' ] );		// body of section
		section.footer = section.getElement( Sliding.options[ 'footer' ] );			// footer of section
		section.handler = section.getElement( Sliding.options[ 'handler' ] );		// link to open section
		section._nextbutton = this.nextButton;
		section._form = this.form;
	
		/* check the objects */
		if ( !section.header || !section.content || !section.footer || !section.handler )
			return null; // exit
		
		/* build the effect engine */
		if ( ! section.fx ){
			if ( section.hasClass( Sliding.options[ 'noFxClass' ] ) )
				// implement an empty effect
				section.fx = {
					'slideIn': function(){
						return {
							'chain': function(){}
						};
					},
					'slideOut': function(){}
				};
			else
				section.fx = new Fx.Slide( section.content , { 'duration': Sliding.options[ 'effectDuration' ] , 'transition' : Fx.Transitions.Quart.easeOut , 'wait' : false } );
				/** IE8 bug fix:
				 * 		IE8 doesn't retrieve offsetHeight correctly.
				 * 		Then set 'offset' manually in slidOut.chain function ( 'close' method ) and 
				 * 		NOT overwrite that if 'offset' has been set.
				 */
				if ( window.ie8 )
					section.fx.vertical = function(){
						this.margin = "margin-top";
				        this.layout = "height";
				        if ( !$defined( this.offset ) )
				        	this.offset = this.element.offsetHeight;
					};
		}
			
		
		
		var _elementsIndex = 0;
		$ES( '*' , section ).each( function( element , index , a ) {
			
			/* check tag of element. Have to match with required tags */
			if ( ! Sliding.options[ 'elements' ].contains( element.getTag() )   ) return;
			
			//if ( _elementsIndex == 0 ) section._first = element;
			
			this.buildElement( section , element , _elementsIndex++ );
			
		} , this );
		
		
		// Store old pointers
		var _prevSection = this.find( this , index );
		var _nextSection = ( _prevSection ) ? _prevSection.next : null;
		
		/* Make a list of section: set the 'previous' pointer to section */
		section.prev = _prevSection;
		if ( _prevSection )
			_prevSection.next = section;
		
		/* Next pointer of list of sections */
		section.next = _nextSection;
		if ( _nextSection )
			_nextSection.prev = section;
		
		/* Store functions */
		section._validation = this.validateSection.bind( section );
		section.isErrorMessagePresent = this.isErrorMessagePresent.bind( section );
		section.open = this.open.bind( section );
		section.selectElement = this.selectElement.bind( section );
		section.close = this.close.bind( section );
		section.clearErrors = this.clearErrors.bind( section );
		section.resize = this.resize.bind( section );
		section.setCSSClasses = this.setCSSClasses.bind( section );
	
		section.handler.addEvent( 'click' , section.open.bind( section , [ ] ) );
	
		/*section.header.addEvent( 'click' , function(){
			if ( section.hasClass( 'toOpen' ) )
				section.fx.slideOut();
			else
				section.fx.slideIn();
			section.toggleClass( 'toOpen' );
		});*/
	
		
		return section;
	},
	
	
	'buildElement': function( section , element , index ) {
		
		if ( index == undefined || index == null )
			index = 1000; // default: last element
	
		/* Pointer to parent section */
		element.section = section;
		
		element._validation = this.validateElement.bind( element );
		element.clearError = this.clearError.bind( element );
		element.clip = this.clip.bind( element );
		
		/* Pointer to error string container / calculate the name of div */
		var _elementID = '';
		if (window.ie) {
			_elementId = element.name.replace( new RegExp( "\\.", "gi" ), '_').replace( new RegExp( "\\[", "gi" ), '_').replace( new RegExp( "\\]", "gi" ) , '');
			_elementId = _elementId.replace( new RegExp( "\\(", "gi" ), "_");
			_elementId = _elementId.replace( new RegExp( "\\)", "gi" ), "");
		} else {
			_elementId = element.name.replace( '.' , '_' , 'g' ).replace( '[' , '_' , 'g' ).replace( ']' , '' , 'g' );
			_elementId = _elementId.replace( "(" , "_" , "g" );
			_elementId = _elementId.replace( ")" , "" , "g" );
		}
		
		
		element._error = $( Sliding.options[ 'err_div' ] + _elementId );
		
		
		/* chek the tag of element, and set the functions */
		if ( element.getTag() == 'input' && element.getProperty( 'type' ) == 'radio' ){
			element.addEvent( 'click' , this.onLostfocus.bind( element ) );
		} else if ( element.getTag() == 'input' ){
			element.addEvent( 'blur' , this.onLostfocus.bind( element ) );
		} else if ( element.getTag() == 'select' ) {
			element.addEvent( 'change' , this.onLostfocus.bind( element ) );
		} else if ( element.getTag == 'textarea' ) {
			// not used yet
		}
	
		// Store old pointers
		var _prevElement = this.find( section , index ) ;
		var _nextElement = ( _prevElement ) ? _prevElement.next : null;
		
		/* Make a list of section: set the 'previous' pointer to section */
		element.prev = _prevElement;
		if ( _prevElement )
			_prevElement.next = element;
		
		/* Next pointer of list of sections */
		element.next = _nextElement;
		if ( _nextElement )
			_nextElement.prev = element;
		
		
		if ( ! element.prev )
			section._first = element;

		// store the index of the current element
		element._index = this.find( section , element );
		
		
		return element;
	},
	
	/**
	 *	Find an element into a collection ( section / elements )
	 *	returns mixed-in values based on 'index' parameter
	 *	 'index' is a number:
	 *		returns the element at the 'index' position, or last element if 'index' is out of bound
	 *	 'index' is an element:
	 *		returns the index of element, or -1 if element is not present in collection
	 */
	'find': function( collection , index ){
		if ( $type( index ) == 'number' ){
			if ( index < 0 ) return null;
			var el = collection._first;
			while( el && index > 0 ){
				if ( !el.next ) break;
				el = el.next;
				index--;
			}
			return el;
		} else if ( $type( index ) == 'element' ){
			var el = collection._first; var _index = 0;			
			while( el ){
				if(el===index)return _index;
				el = el.next;
				_index++;
			}
			return -1;
		}
	},
	
	/**
	 *	Close all sections if no errors present
	 *	keep open the 'toOpen' sections
	 *	'toOpen' : array of section
	 */
	'closeAll': function( toOpen ){
		var section = this._first;
		while( section ){
			if ( ! toOpen.contains( section ) )
				section.close.apply( section , [ false ] );
			else
				section.setCSSClasses();
			section = section.next;
		}
		return this;
	},
	
	
	/**
	 *	Thread: launch the validation function on all sections
	 *	Submit button is enabled if no errors present
	 */
	'enableNextButton':function(){
		if ( ! this.check() )
			if ( this.timer )
				return $clear( this.timer );
		
		this.errors = 0;
		
		var section = this._first;
		while( section ){
			
			/* validation on all sections */
			var _validation = section._validation( false , true );
			if ( _validation && section.hasClass( Sliding.options[ 'closed' ]  ) )		// show the opener section link
				section.handler.removeClass( Sliding.options[ 'hide' ] );
			this.errors = _validation ? this.errors : this.errors + 1;
			
			section = section.next;
		}
		
		if ( this.errors == 0 ){
			// enable button
			this.nextButton.removeClass( Sliding.options[ 'disableButton' ] );	
			this.nextButton.addClass( Sliding.options[ 'activeButton' ] );
		} else {
			// disable button
			this.nextButton.removeClass( Sliding.options[ 'activeButton' ] );	
			this.nextButton.addClass( Sliding.options[ 'disableButton' ] );
		}
	},
	
	/**
	 *	Forward function: launch submit on form, default parameter is 'method=forward'
	 */
	'forward': function(){
		if (  this.nextButton.hasClass( Sliding.options[ 'disableButton' ] )  )return false;
		// destroy thread
		$clear( this.timer );
		this.submitForm();
	},
	
	'submitForm':function(){
		
		// force check on 'nextButton'
		if (  this.nextButton.hasClass( Sliding.options[ 'disableButton' ] )  ) return false;
		
		/* Modify action of form */
		if ( $type( this.options.onSubmit ) == 'function' )
			return this.options.onSubmit.apply( this , [] );
		
		this.form.action = this.form.action.replace(/(\#.*)?$/, '?method=' + Sliding.options[ 'methodForward' ] + '$1');
		
		myCursor.show();  
		
		this.form.submit();		// submit form
	},
	
	
	/**
	 *	E L E M E N T S
	 */
	
	/* Clear error of element: hide error div */
	'clearError':function(){
		
		if ( this._error ){
			this._error.addClass( Sliding.options[ 'hide' ] );
			this.section.resize();
		}
		
		return this;
	},
	
	/* Validate function of element: if 'show' is true show the error element if present */
	'validateElement': function( show , thread ){
		if ( ! this.form ) return true;
		
		var _validation = false;
		
		if ( this.getProperty( 'type' ) == 'radio' ) {
			
			// search and check other option radio
			var _elements = this.form.elements[ this.name ];
			for ( var i =0; i < _elements.length; i++ )
				if ( _elements[i].checked ){
					_validation = true;
					break;
				}
		} else{
			if(this.value == '')
				_validation=false;
			else if(this.value != '' && this.hasClass('hasDefaultText'))
				_validation=false;
			else 
				_validation=true;
			//_validation = ( this.value != '' || !this.hasClass('hasDefaultText') );	// is value of field empty?
		}
		
		
		if ( $type( this.validation ) == 'function' )
			_validation = this.validation( _validation , !!thread );
		
		
		if ( show && this._error ) {
			if ( _validation )
				this._error.addClass( Sliding.options[ 'hide' ] ); 		// show error element
			else
				this._error/*.setHTML('ERROR')*/.removeClass( Sliding.options[ 'hide' ] ); 		// show error element
			this.section.resize();
		}
		
		return _validation;
		
	},
	
	/* onBlur event: check the field on blur event */
	'onLostfocus': function(){

		if (  this.hasClass( Sliding.options[ 'stopEventClass' ] )  ) return; // Manually managed (on ajax complete)
		
		if (  ! this.section.hasClass( Sliding.options[ 'opened' ] )  ) return;
		
		return this.clip();
	},
	
	
	'clip': function(){
		
		var sectionCorrect = false;
		var elementCorrect = this._validation( true );
		
		if ( elementCorrect )
			sectionCorrect = this.section._validation( false );			// check all field in this section
		
		if ( sectionCorrect ) {
			/* no errors are present */
			if ( this.section.next ){
				/* next section found, open it if its validation fails */
				var _section = this.section,next;
				while( _section && _section._validation() )
					_section = _section.next;
				if ( _section ) 
					_section.open( true , true );
				this.section.close( true );
			}else
				/* close this section */
				this.section.close( true );
		} else {
			
			/* Bug fix: reset the size of content of section in case of 
			 * error messages present in section and not present for this field 
			 */
			//if ( elementCorrect )
				this.section.resize();
		}
		
		return this;
	},
	
	
	
	/**
	 *	S E C T I O N
	 */
	
	/* clear all error in this section */
	'clearErrors':function(){
	
		var element = this._first;
		while( element ){
			element.clearError();		// hide error of element
			element = element.next;
		}
	
		return this;
	},
	
	
	/**
	 *	Validation function: launch validation function on all given elements.
	 *	if 'elements' is missing, 'elements' is 'all elements in section';
	 *	if 'show' is true, show or hide the error message of single element
	 *	'thread' indicates if function was called from 'enableNextButton' thread
	 */
	'validateSection':function( show , thread ){
		this.errors = 0;
		
		var element = this._first;
		while( element ){
			if (  ! element._validation( show , !!thread )  )
				this.errors++;		// an error found
			element = element.next;
		}
		
		var _result = (this.errors == 0);
		
		if ( $type(this.onValidation) == 'function' )
			_result = this.onValidation( _result , !!thread );
		
		return _result;
	},
	
	
	/* Check for error messages shown */
	'isErrorMessagePresent':function(){
		var errors = 0;
		
		var element = this._first;
		while( element ){
			if ( element._error )
				errors = element._error.hasClass( Sliding.options[ 'hide' ] )  ?  errors : errors + 1;
			element = element.next;
		}
		
		return (errors > 0);	// return boolean: are there any errors shown?
	},
	
	
	/* Select a given element in this section */
	'selectElement': function( element ) {
		try{
			element = element || this._first;
			element.focus();
		} catch ( e ){
			// alert( 'errore in focus' );
		}
	},
	
	/**
	 *	Open this section. If 'select' is true, luanch 'focus' event on first element in this section.
	 *	Close all other sections, modify css classes and hide the handler link
	 */
	'open':function( select , skipClose ){
		
		if ( $type(this.onOpen) == 'function' )
			if ( ! this.onOpen.apply( this , [] ) )
				return;
		
		Sliding.options.onSectionOpen.apply( this , [] );
		
		// launch the slide effect, and select an element if 'select' is true
		
//		if ( select )
//			this.fx.slideIn().chain( this.selectElement );	
//		else
			this.fx.slideIn().chain( (function(){
				if (  this.hasClass( Sliding.options[ 'opened' ] ) && this.fx.element  )
					if ( this.fx.element.offsetHeight != this.fx.wrapper.offsetHeight )
						this.fx.wrapper.setStyle( 'height' , this.fx.element.offsetHeight );
			}).bind( this ) );
			
		
		if ( !skipClose ){
			var section = Sliding._first;
			while( section ){
			
				if (  section !== this && ! section.hasClass( Sliding.options[ 'closed' ]  )  )		// this is current section, "I will not close myself!"
					section.close( true );	// close section
				
				section = section.next;
			}
		}
		
		if (this.hasClass(Sliding.options['noToggleClass']))
			return this;
		
		
		this.setCSSClasses();
		
		
		return this;
	},
	
	
	'setCSSClasses': function(){
		/* modify css classes */
		this.addClass( Sliding.options[ 'opened' ] );
		this.removeClass( Sliding.options[ 'closed' ] );
		this.handler.addClass( Sliding.options[ 'hide' ] );	// hide handler link
		this.header.removeClass( Sliding.options[ 'closed' ] );
		this.header.addClass( Sliding.options[ 'active' ] );
		this.footer.removeClass( Sliding.options[ 'hide' ] );
	},
	
	
	/* Resize body of section */
	'resize': function(){
		this.fx.slideIn();		// launch slide function
	},
	
	/* Close section and launch validation if 'validate' is true */
	'close':function( validate ){
		
		if ( $type(this.onClose) == 'function' )
			if ( ! this.onClose.apply( this , [] ) )
				return;
		
		Sliding.options.onSectionClose.apply( this , [] );
		
		if ( validate )
			if ( ! this._validation( true ) )	// launch validation
				return this;	// vlidation failed, "do not close me"
		
		if (  ( this._validation() && !this.next )  &&  !Sliding.options[ 'closeLastSection' ]  )
			this.addClass(Sliding.options['noToggleClass']); // do not close last section
		
		if (this.hasClass(Sliding.options['noToggleClass']))
			return this;
		
		/* Fat - TODO : create a thread to keep open current section */
		(function _close(){
			
			if ( window.ie8 ){
				/** IE8 bug fix:
				 * 		set 'offset' manually because IE8 doesn't retrieve that correctly
				 */
				var _height = this.content.offsetHeight;
				this.fx.slideOut().chain( (function(){
					this.fx.offset = _height;
				}).bind( this ) );
			} else
				this.fx.slideOut();	// slide effect: close
			
			/* modify css classes */
			this.removeClass( Sliding.options[ 'opened' ] );
			this.addClass( Sliding.options[ 'closed' ] );
			
			this.header.removeClass( Sliding.options[ 'active' ] );
			this.header.addClass( Sliding.options[ 'closed' ] );
			this.footer.addClass( Sliding.options[ 'hide' ] );
			return this;
			
		}).delay( 1 , this );
		
		return this;
	}
	
};
