var ProgressiveForm = Class.create();
ProgressiveForm.prototype = {
	container: null,
	indicatorUL: null,
	steps: null,
	stepLinks_array: Array(),
	step_current: 0,
	step_next: null,
	step_cancel: 1,
	ajaxUrl: null,
	step_limit: null,
	steps_validated: Array(),
	cheatCode: '',
	validateForms: true,
	animator: null,
	clickFunc: null, //holds the bound event listener to enable Event.stopObserving();

	// Constructor
	initialize: function(preferences) {
		this.container = preferences['container'];
		this.ajaxUrl = preferences['ajaxUrl'];
		this.step_current = preferences['defaultStep'] ? preferences['defaultStep'] : 1;
		this.steps = new Array();
		this.indicatorUL = DOM.getChildrenByTagName(this.container, 'ul')[0];
		this.animator = new kAccordion(
		{
			elRoot: this.container,
			selHeading: '',
			selContent: 'div.step',
			canCloseAll: false
		});
		this.animator.recalculateHeights();
		this.animator.setNewFold();
		this.observeStepLinks();
	},

	// Method
	observeStepLinks: function () {
		this.clickFunc = this.clickStepLink.bindAsEventListener(this);
		$A(this.indicatorUL.getElementsByTagName('a')).each(function (el, index)
			{
				var step_id = el.href.getUrlArgument();
				this.stepLinks_array[index] = step_id;
				Event.observe(el, 'click', this.clickFunc);
				if ($(step_id) != null && !step_id.isNumeric()){
					this.steps[step_id] = $(step_id);
					this.findNextButton(step_id);
				}
				if (index == 0 || Element.hasClassName(el.parentNode, 'selected'))
					this.step_current = step_id;
			}.bind(this)
		);
	},
	stopObservingStepLinks: function (terminalStep) {
		var step = this.stepLinks_array.indexOf(terminalStep);
		$A(this.indicatorUL.getElementsByTagName('a')).each(function (el, index)
			{
				if (index <= step) {
					Event.stopObserving(el, 'click', this.clickFunc);
					Element.addClassName(el, 'disabled');
				}
			}.bind(this)
		);
	},
	clickStepLink: function (e) {
		var el = e.currentTarget || Event.findElement(e, 'a');
		var step = el.href.getUrlArgument();
		this.changeStep(step);
	},
	clickNext: function (e) {
		this.loadStep(this.step_current, true);
	},
	clickPrev: function (e) {
		var el = e.currentTarget || Event.findElement(e, 'input');
		var step = el.name;
		this.changeStep(step);
	},
	clickCancel: function(e) {
		var el = e.currentTarget || Event.findElement(e, 'input');
		var step = el.name;
		this.changeStep(step);
	},
	clickSubmit: function(e) {
		this.submitData();
	},
	submitData: function() {
		this.loadStep(this.step_current, true);
	},
	changeStep: function (step) {
		//shared code for clickStepLink and clickPrev
		if (this.stepLinks_array.indexOf(step) > this.stepLinks_array.indexOf(this.step_current)) {
			// If the user is trying to skip to the next step...
			var nextPage = this.minInvalid();
			if (this.step_current == nextPage)
				nextPage++;
			// Set the next step
			this.step_next = Math.min(step, nextPage);
			// Verify that this step is valid first.
			this.loadStep(this.step_current, true);

		// If the user is trying to go to previous step
		} else {
			// Let him.
			this.loadStep(step);
		}
	},
	loadStep: function (step, check) {

		if (typeof(this.steps[step]) != 'undefined' && !check) {
			//select existing step
			this.selectStep(step);
		}
		else if (!check) {
			//request a step from the backend
			this.getForm = true;
			this.step_next = step;
			this.AjaxOut();
		}
		else if (typeof(this.steps[step]) != 'undefined' && check) {
			//request a step from the backend AND verify this step
			this.checkForm = true;
			this.AjaxOut();
		}
	},
	selectStep: function (step_id) {
		//select the proper LI
		var stepIndex = this.stepLinks_array.indexOf(step_id);
		
		$A(this.indicatorUL.getElementsByTagName('li')).each(function (el, index)
			{
				if (index == stepIndex && !Element.hasClassName(el, 'selected')) {
					Element.addClassName(el, 'selected');
				}
				else if (index != stepIndex && Element.hasClassName(el, 'selected'))
					Element.removeClassName(el, 'selected');
			}.bind(this)
		);

		var current_exists = typeof(this.steps[this.step_current]) != 'undefined' ? true : false;
		var requested_exists = typeof(this.steps[step_id]) != 'undefined' ? true : false;

		//make sure the step links are visible before animation begins (IE needs this)
		if (!Element.visible(this.container)) {
			Element.show(this.container);
		}

		//Animate it
		this.animator.headingClick(null, stepIndex);

		//set current step to chosen step
		this.step_current = step_id;
	},
	injectError: function (name, message) {
		var elements = document.getElementsByName(name);
		$A(elements).each(function(el, index)
			{
				new Insertion.Before(el, message);
			}.bind(this)
		);
	},
	destroyErrors: function (step) {
		if (typeof(this.steps[step]) != 'undefined') {
			var spans = this.steps[step].getElementsByTagName('span');
			$A(spans).each(function(el, index)
				{
					if (Element.hasClassName(el, 'error'))
						Element.remove(el);
				}.bind(this)
			);
		}
	},
	AjaxOut: function() {
		var vars = '';
		this.destroyErrors(this.step_current);
		if (this.getForm) {
			vars = 'step_next=' + escape(this.step_next);
			this.step_next = null;
			this.getForm = null;
		}
		else if (this.checkForm) {
			//initialize terminal process
			if (typeof(this.steps[this.step_current]) != 'undefined' && Element.hasClassName(this.steps[this.step_current], 'terminal'))
				this.terminateOnSuccess = this.step_current;
			else
				this.terminateOnSuccess = null;

			//get the steps from the form
			var form = this.steps[this.step_current].getElementsByTagName('form')[0];
			
			if (this.step_next) {
				var oldNextStep = form.elements['step_next'].value;
				form.elements['step_next'].value = this.step_next;
			}
			vars = Form.serialize(form);
			if (this.step_next) {
				form.elements['step_next'].value = oldNextStep;
				this.step_next = null;
			}
			this.checkForm = null;
		}

		vars = (vars ? vars+'&' : '')+this.getAllForms();

		/*if (this.step_current == 14 || this.step_current == 15)
		{
			this.ajax = new Ajax.Request('ajax/saveToDB.php',
			 	{
					method: 'post',
					parameters: vars,
					onSuccess: this.AjaxIn.bind(this)
				}
			);
		}
		else
		{*/
			this.ajax = new Ajax.Request(this.ajaxUrl,
				{
					parameters: vars,
					onSuccess: this.AjaxIn.bind(this)
				}
			);
		//}
	},
	AjaxIn : function(req) {
		
		var steps  = req.responseXML.getElementsByTagName('step');
		var errors = req.responseXML.getElementsByTagName('error');
		var invalids = req.responseXML.getElementsByTagName('invalidated');
		//capture "Invalids"
		if (invalids.length) {
			var step_id = invalids[0].getAttribute('step');
			var stepIndex = this.stepLinks_array.indexOf(step_id);
			var invalid = null;
			for(var x=0; x<invalids.length; x++) {
				invalid = invalids[x].getAttribute('step');
				this.steps_validated[invalid] = false;
			}
		}

		//No errors
		if (!errors.length && steps.length) {
			// if last step was terminal, kill all previous links.
			if (this.terminateOnSuccess) {
				this.stopObservingStepLinks(this.terminateOnSuccess);
				this.terminateOnSuccess = null;
			}

			//get values for the new step
			var step_id = steps[0].getAttribute('value');
			
			var stepIndex = this.stepLinks_array.indexOf(step_id);
			var validated = steps[0].getAttribute('validated');
			this.steps_validated[validated] = true;

			//try to refresh steps that want refreshed
			if (typeof(this.steps[step_id]) != 'undefined' && Element.hasClassName(this.steps[step_id], 'refresh'))
				var refreshDiv = true;

			//create new div if necessary
			if((typeof(this.steps[step_id]) == 'undefined' || refreshDiv) && step_id.isNumeric()) {
				if (!refreshDiv) {
					//setup new div
					this.steps[step_id] = document.createElement('div');
					this.steps[step_id].style.display = 'none';
					Element.addClassName(this.steps[step_id], 'step');
					if (this.checkRefresh(step_id))
						Element.addClassName(this.steps[step_id], 'refresh');
					if (this.checkTerminal(step_id))
						Element.addClassName(this.steps[step_id], 'terminal');

					//insert in the correct place in the DOM
					var stepIndex = this.stepLinks_array.indexOf(step_id);
					var nextStep = this.stepLinks_array[stepIndex+1];
					if (typeof(this.steps[nextStep]) != 'undefined') {
						this.container.insertBefore(this.steps[step_id], this.steps[nextStep]);
					} else {
						this.container.appendChild(this.steps[step_id]);
					}
					//this.animator.createContent(el);
					//alert(this.steps[step_id]);
					this.animator.createContent(this.steps[step_id]);
					this.animator.measureHeight(stepIndex);
				}

				//populate div contents
				this.steps[step_id].innerHTML = DOM.getXMLData(req.responseXML, 'step');
				this.animator.measureHeight(stepIndex);

				//make sure buttons all have evens attached
				EventSelectors.apply();
				this.findPrevLink(step_id);
				this.findNextButton(step_id);
				this.findPrevButton(step_id);
				this.findCancelButton(step_id);
				this.findSubmitButton(step_id);
			}

			//setup pre-existing steps (not numeric implies a DIV that existed on page load)
			else if ($(step_id) != null && !step_id.isNumeric()) {
				this.steps[step_id] = $(step_id);
				this.findNextButton(step_id);
				this.findPrevButton(step_id);
				this.findCancelButton(step_id);
				this.findSubmitButton(step_id);
			}

			//Select the new step
			this.selectStep(step_id);
		}

		//Capture Errors
		else if (errors.length) {
			this.terminateOnSuccess = null;
			$A(errors).each(function(el, index)
				{
					var error_message = DOM.getElementText(el);
					var error_name = el.getAttribute('name');
					this.injectError(error_name, error_message);
				}.bind(this)
			);
			//alert(stepIndex);
			this.animator.measureHeight(stepIndex);
		}
	},
	findNextButton: function (step) {
		if ($('next_'+step) != null) {
			Event.observe($('next_'+step), 'click', this.clickNext.bind(this));
		}
	},
	findPrevButton: function (step) {
		if ($('prev_'+step) != null) {
			Event.observe($('prev_'+step), 'click', this.clickPrev.bind(this));
		}
	},
	findCancelButton: function (step) {
		if ($('cancel_'+step) != null) {
			Event.observe($('cancel_'+step), 'click', this.clickCancel.bind(this));
		}
	},
	findSubmitButton: function (step) {
		if ($('submit_app') != null) {
			Event.observe($('submit_app'), 'click', this.clickSubmit.bind(this));
		}
	},
	findPrevLink: function (step) {
		if (typeof(this.steps[step]) != 'undefined') {
			
			var prevs = document.getElementsByClassName('prev', this.steps[step]);
			$A(prevs).each(function (el, index)
				{
					if (el.tagName.toLowerCase() == 'a')
						Event.observe(el, 'click', this.clickStepLink.bind(this));
				}.bind(this)
			);
		}
	},
	minInvalid: function() {
		// Returns the lowest step that is still invalid
		var lowest = null;
		for (var step_id=0; step_id<this.steps_validated.length; step_id++) {
			if (this.steps_validated[this.stepLinks_array[step_id]] != true && (step_id < lowest || lowest == null))
				lowest = step_id;
		}
		if (lowest == null)
			lowest = step_id;
		if (lowest < 0)
			lowest = 0;
		if (!this.validateForms)
			lowest = this.stepLinks_array.length - 1;
		return this.stepLinks_array[lowest];
	},
	getAllForms: function () {
		var vars = '';
		var varsArray = '';
		var queryString = '';
		var form = null;
		if (this.steps.length > 0) {
			for (key in this.steps) {
				if (typeof(this.steps[key]) != 'function') {
					form = this.steps[key].getElementsByTagName('form');
					form = form.length > 0 ? form[0] : null;
					if (form) {
						vars = Form.serialize(form);
						varsArray = vars.toQueryParams();
						for (key2 in varsArray) {
							queryString = (queryString ? queryString+'&' : '')+'step['+key+']['+this.fixKeyArray(key2)+']='+varsArray[key2];
						}
					}
				}
			}
		}
		return queryString;
	},
	checkRefresh: function (step) {
		return this.checkStepsForClass(step, 'refresh');
	},
	checkTerminal: function (step) {
		return this.checkStepsForClass(step, 'terminal');
	},
	checkStepsForClass: function (step, className) {
		var liArray = $A(this.indicatorUL.getElementsByTagName('li'));
		var index = this.stepLinks_array.indexOf(step);
		if (liArray.length && index >= 0 && Element.hasClassName(liArray[index], className))
			return true;
		else
			return false;

	},
	fixKeyArray: function (key) {
		key = key.replace('%5B','[');
		key = key.replace('%5D',']');
		var index = key.indexOf('[');
		var index2 = key.lastIndexOf(']');
		if (index == -1)
			return key;
		else {
			key = key.substring(0,index) + ']'+ key.substring(index, index2);
			return key;
		}
	}
};

var progForm = null;
EventSelectors.register({
	'#application' : function(el){
		progForm = new ProgressiveForm({ajaxUrl: 'ajax/', container: el, defaultStep: 1});
	}
});
