/**
 * formValidator class for MW2
 * @author Grégory Compte
 * @version 0.5
 */

// TODO check for default selected category and don't remove associated subcategory if any

/**
 * @constructor
 * @param {String} selectorId id of the SELECT element used with the pagination (or the form)
 * @param {Object} options
 * @return {formValidator}
 * @type {formValidator}
 */
formValidator = function(selector, options)
{
	var selectorElement = null;
	var formElement = null;
	var blockElement = null;
	
	this._blocks = formValidator.prototype._blocks.clone();
	this._submitElements = this._formSelectOptions.clone();
	this._disabledElements = this._disabledElements.clone();
	this._formSelectOptions = formValidator.prototype._formSelectOptions.clone();
	this._options = Object.extend(Object.clone(this._options), options || {});
	
	formValidator.prototype._checkFormFieldFailElement = new Element('img', {'width': '20', 'height': '20', 'border': '0', 'src': this._options.pictos.cancel, 'alt': 'fail', 'style': 'vertical-align: middle; margin-right: 2px;'});
	formValidator.prototype._checkFormFieldSuccessElement = new Element('img', {'width': '20', 'height': '20', 'border': '0', 'src': this._options.pictos.valid, 'alt': 'success', 'style': 'vertical-align: middle; margin-right: 2px;'});
	formValidator.prototype._checkFormFieldWaitElement = new Element('img', {'width': '16', 'height': '16', 'border': '0', 'src': this._options.pictos.loading, 'alt': 'checking', 'style': 'vertical-align: middle; margin-right: 2px;'});
	formValidator.prototype._checkFormFieldErrorElement = new Element('img', {'width': '16', 'height': '16', 'border': '0', 'src': this._options.pictos.error, 'alt': 'error', 'style': 'vertical-align: middle; margin-right: 2px;'});
	
	if (selector != null && !Object.isElement(selector))
		selector = selector.toString();
	selectorElement = $(selector);
	
	if (this._options.formElement)
		formElement = $(this._options.formElement);
	else
	{
		formElement = $(selectorElement.form);
		if (selectorElement == null || selectorElement.form == null)
			return (null);
	}

	if (formElement == null && selectorElement.tagName.toLowerCase() == 'form')
		formElement = selectorElement;
	if (formElement == null)
		return (null);
	formElement.observe('submit', this._onFormSubmit.bindAsEventListener(this, formElement));
	Element.select(formElement, 'input[type=text], input[type=password], textarea, select').each(function(el){
		// if selectorElement is a select, we use it for block selection, so don't add check event on it.
		if (selectorElement !== null && selectorElement.tagName.toLowerCase() == 'select' && selectorElement == el)
			return;
		// if the element is a select element used in category, register correct event.
		if (el.name != null && el.tagName.toLowerCase() == 'select' && el.name.search(/_category$/i) != -1)
		{
			el.observe('change', this._showSubcategoryOptions.bind(this, el));
			return;
		}
		if (this._options.checkUrl !== null)
		{
			el.observe('change', this._onElementChange.bindAsEventListener(this, el));
			//el.observe('blur', this._onElementChange.bindAsEventListener(this, el));
		}
	}.bind(this));

	// if selectorElement is a select element, we register it as a block swap.
	if (selectorElement !== null && selectorElement.options != null)
	{
		selectorElement.observe('change', this._swapCreateFormBlocks.bind(this, selectorElement));
		$A(selectorElement.options).each(function(optionElement, index){
			blockElement = null;
			if (optionElement.value != null)
				blockElement = $(this._options.blockIdPrefix + optionElement.value);
			if (blockElement != null)
				blockElement.remove();
			this._blocks[index] = blockElement;
		}.bind(this));
		this._currentBlockIndex = selectorElement.selectedIndex;
	}
	else
		this._blocks[0] = null;
	this._swapCreateFormBlocks(selectorElement);
}


/**
 * Default options values.
 * 
 * checkUrl: url called to verify form fields (default: null)
 * onSubmitSuccess: function called with JSON and form object if form submit success and a json object with success = true is return (default: emptyFunction)
 * onSubmitFailure: function called with Ajax.Response and form if form submit success and a json object with success = false is return (default: emptyFunction)
 * blockIdPrefix: string prepend to selector options value to use as block id (default: '')
 * blockContainerId: id of the block containing other blocks (default: 'blocks')
 * resetForm: do we reset form after success or not
 */
formValidator.prototype._options = {
	checkUrl: null,
	onSubmitSuccess: Prototype.emptyFunction,
	onSubmitFailure: Prototype.emptyFunction,
	blockIdPrefix: '',
	blockContainerId: 'blocks',
	'resetForm': true,
	pictos : {"cancel" : "img/cancel.png",
			  "valid" : "img/valid.png",
			  "loading" : "img/loading.gif",
			  "error" : "img/error.png"}
};

formValidator.prototype._checkFormFieldFailElement = new Element('img', {'width': '20', 'height': '20', 'border': '0', 'src': formValidator.prototype._options.pictos.cancel, 'alt': 'fail', 'style': 'vertical-align: middle; margin-right: 2px;'});

formValidator.prototype._checkFormFieldSuccessElement = new Element('img', {'width': '20', 'height': '20', 'border': '0', 'src': formValidator.prototype._options.pictos.valid, 'alt': 'success', 'style': 'vertical-align: middle; margin-right: 2px;'});

formValidator.prototype._checkFormFieldWaitElement = new Element('img', {'width': '16', 'height': '16', 'border': '0', 'src': formValidator.prototype._options.pictos.loading, 'alt': 'checking', 'style': 'vertical-align: middle; margin-right: 2px;'});

formValidator.prototype._checkFormFieldErrorElement = new Element('img', {'width': '16', 'height': '16', 'border': '0', 'src': formValidator.prototype._options.pictos.error, 'alt': 'error', 'style': 'vertical-align: middle; margin-right: 2px;'});

formValidator.prototype._reloadElements = function(selectorElement, formElement)
{
	Element.select(formElement, 'input[type=text], input[type=password], textarea, select').each(function(el){
		//el.stopObserving("change");
		//el.stopObserving("blur");
		// if selectorElement is a select, we use it for block selection, so don't add check event on it.
		if (selectorElement.tagName.toLowerCase() == 'select' && selectorElement == el)
			return;
		// if the element is a select element used in category, register correct event.
		if (el.name != null && el.tagName.toLowerCase() == 'select' && el.name.search(/_category$/i) != -1)
		{
			el.observe('change', this._showSubcategoryOptions.bind(this, el));
			return;
		}
		if (this._options.checkUrl !== null)
		{
			el.observe('change', this._onElementChange.bindAsEventListener(this, el));
			//el.observe('blur', this._onElementChange.bindAsEventListener(this, el));
		}
	}.bind(this));
}

formValidator.prototype._blocks  = new Array();
formValidator.prototype._disabledElements  = new Array();

/**
 * submit elements removed from the form
 * @type {Array} 
 */
formValidator.prototype._submitElements = new Array();

/**
 * Contain category => subcategory select's options for each block
 * _formSelectOptions[blockIndex][selectCategoryName][optionCategoryValue] = Array;
 */
formValidator.prototype._formSelectOptions = new Array();

formValidator.prototype._currentBlockIndex = 0;

formValidator.prototype._swapCreateFormBlocks = function(/** Element */ selectElement)
{
	var selectSubcategoryElement = null;
	var targetOptionsArray = null;
	var targetBlock = null;
	var targetForm = null;
	var targetSelect = null;
	var targetCategory = null;

	selectElement = $(selectElement);
	if (selectElement == null || selectElement.selectedIndex == null)
		return;//newBlockIndex = 0;
	else
		newBlockIndex = selectElement.selectedIndex;
	if (newBlockIndex != this._currentBlockIndex)
	{
		if (this._blocks[this._currentBlockIndex] != null)
			this._blocks[this._currentBlockIndex].remove();
		this._currentBlockIndex = newBlockIndex;
	}
	
	if ($(this._options.blockContainerId) != null)
		$(this._options.blockContainerId).insert(this._blocks[newBlockIndex]);

	if (this._formSelectOptions[this._currentBlockIndex] == null)
		this._formSelectOptions[this._currentBlockIndex] = {};
	targetBlock = this._formSelectOptions[this._currentBlockIndex];
	
	targetForm = $(selectElement.form);
	if (targetForm == null)
		return;

	// if a checkUrl was given while creating object. 
	if (this._options.checkUrl !== null)
		this._checkCreateFormElementsValue(targetForm);
	Element.select(targetForm, 'select[name$=_category]').each(function(selectElement, index){

		if (selectElement.options == null || selectElement.name == null)
			return;

		selectSubcategoryElement = selectElement.next('select');
		if (selectSubcategoryElement == null)
			return;

		if (targetBlock[selectElement.name] == null)
			targetBlock[selectElement.name] = {};
		targetSelect = targetBlock[selectElement.name];
		
		$A(selectElement.options).each(function(optionElement, index){
			if (optionElement.value == null || optionElement.value == 0)
				return;
			
			if (targetSelect[optionElement.value] != null)
				return;
			
			targetCategory = targetSelect[optionElement.value] = new Array();
			Element.select(selectSubcategoryElement, 'option.subcategory_'+ optionElement.value).each(function(subcategoryOptionElement, index){
				targetCategory.push(subcategoryOptionElement);
				if (optionElement.value != selectElement.value)
					subcategoryOptionElement.remove();
			});
		}.bind(this));
	}.bind(this));
	if (window.showStep1)
		window.showStep1();
	this.resizeFormIframe();
	
};

formValidator.prototype._checkCreateFormElementsValue = function(/** Element */ form)
{
	var elements = null;
	var infoElement = null;
	
	if (this._options.checkUrl === null)
		return;
	form = $(form);
	if (form == null)
		return;

	// only run check on elements *REALLY* send
	elements = form.serialize(true);
	for (element in elements)
	{
		elementObj = $(element);
		if (elementObj === null)
			continue;
		infoElement = this._getInfoElement(elementObj);
		if (infoElement != null)
			infoElement.update(this._checkFormFieldWaitElement.cloneNode(false));
	}
	
	var datas = form.serialize();
	new Ajax.Request(this._options.checkUrl, {
		'asynchronous': true,
		'parameters': datas,
		'onSuccess': this._onCheckCreateFormElementValueSuccess.bind(this, form),
		'onFailure': this._onCheckCreateFormElementValueException.bind(this),
		'onException': this._onCheckCreateFormElementValueException.bind(this)
	});	
};

formValidator.prototype._checkCreateFormElementValue = function(/** Element */ formElement)
{
	var infoElement = null;

	if (this._options.checkUrl === null)
		return;
	formElement = $(formElement);
	if (formElement == null || formElement.name == null)
		return;
		
	infoElement = this._getInfoElement(formElement);
	if (infoElement != null)
		infoElement.update(this._checkFormFieldWaitElement.cloneNode(false))
	var datas = formElement.serialize();
	new Ajax.Request(this._options.checkUrl, {
		'asynchronous': true,
		'parameters': datas,
		'onSuccess': this._onCheckCreateFormElementValueSuccess.bind(this, formElement.form),
		'onFailure': this._onCheckCreateFormElementValueException.bind(this),
		'onException': this._onCheckCreateFormElementValueException.bind(this)
	});
};

formValidator.prototype._onCheckCreateFormElementValueException = function(/** Ajax.Request */ xhr, /** Exception */ ex)
{	
	var waitElements = null;
	
	waitElements = $$('img[alt=checking]');
	for (temp = 0; temp < waitElements.length; ++temp)
	{
		waitElements[temp].up().update(this._checkFormFieldErrorElement.cloneNode(false)).addText('Exception asking this URL:'+ this._options.checkUrl);
	}
	this.resizeFormIframe();
};

formValidator.prototype._onCheckCreateFormElementValueSuccess = function(/** Element */ form, /** Ajax.Response */ xhr)
{
	var containerElement = null;
	var resultElement = null;
	var formElement = null;

	if (xhr == null || xhr.responseJSON == null)
		return;

	for (elementId in xhr.responseJSON)
	{
		formElement = Element.down(form, '[name="'+ elementId +'"]');
		if (formElement == null)
			continue;
		containerElement = formElement.up('td');
		if (containerElement == null)
			continue;
		resultElement = containerElement.next('td');
		if (resultElement == null)
			continue;
		value = xhr.responseJSON[elementId];
		if (value === null)
			resultElement.update();
		else if (value === true)
			resultElement.update(this._checkFormFieldSuccessElement.cloneNode(false));
		else
		{
			var temp = this._checkFormFieldFailElement.cloneNode(false);
			if (value !== false)
				temp.writeAttribute('title', value);
			resultElement.update(temp);
			/*var styleSpanElement = new Element('span');
			styleSpanElement.setStyle(
			{
				"color" : "#FF0000",
				"fontFamily" : "Tahoma"
			}
			);
			resultElement.insert(styleSpanElement.update(value));*/
		}
	}
	this.resizeFormIframe();
};

formValidator.prototype._getInfoElement = function(/** Element */ formElement)
{
	var containerElement = null;
	var infoElement = null;
	
	formElement = $(formElement)
	if (formElement == null)
		return (null);
	containerElement = formElement.up('td');
	if (containerElement == null)
		return (null);
	infoElement = containerElement.next('td');
	return (infoElement);
};

formValidator.prototype._onElementChange = function(/** Event */ event, /** Element */ formElement)
{
	this._checkCreateFormElementValue(formElement);
};

/**
 * handle call when the form is submit
 * @param {Event} event
 * @param {Element} form
 */
formValidator.prototype._onFormSubmit = function(event, form)
{
	form = $(form);
	try {
	Event.stop(event);
	if (form == null)
		return;
	this.enableDisabledElements(form);
	form.request({
		'asynchronous': true,
		'onSuccess': this._onFormSubmitSuccess.bind(this, form),
		'onFailure': this._onFormSubmitException.bind(this, null, form),
		'onException': this._onFormSubmitException.bind(this, form)
	});
	this._removeSubmitElements(form);
	this.disableForm(form);
	} catch (e)
	{
		alert(e.message);
	}
};

formValidator.prototype._onFormSubmitSuccess = function(/** Element */ form, /** Ajax.Response */ xhr)
{
	var formElement = null;
	var infoElement = null;
	//form.enable(form);
	this.enableForm(form);
	
	if (xhr == null || xhr.responseJSON == null)
	{
		if (Object.isFunction(this._options.onSubmitFailure))
			this._options.onSubmitFailure(xhr, form);
		return;
	}
	// remove all error infos
	$A(form.select('img[alt="'+ this._checkFormFieldFailElement.alt +'"]')).each(function(el){
		el.up().update();
	}.bind(this));
	if (xhr.responseJSON.success === true)
	{
		if (Object.isFunction(this._options.onSubmitSuccess))
			this._options.onSubmitSuccess(xhr.responseJSON, form);
		if (this._options.resetForm === true)
		{
			form.reset();
			this._swapCreateFormBlocks(null);
		}
		var msg = 'Traitement en cours';
		if (xhr.responseJSON.msg)
			msg = xhr.responseJSON.msg;
		this._restoreSubmitElements(form, true, msg);
		return;
	}
	if (this._options.checkUrl !== null && xhr.responseJSON.errors != null)
	{
		for (elementName in xhr.responseJSON.errors)
		{
			formElement = Element.down(form, '[name="'+ elementName +'"]');
			if (formElement == null)
				continue;
			infoElement = this._getInfoElement(formElement);
			if (infoElement == null)
				continue;
			value = xhr.responseJSON.errors[elementName];
			if (value === null)
				infoElement.update();
			else if (value === true)
				infoElement.update(this._checkFormFieldSuccessElement.cloneNode(false));
			else
			{
				if (value === false)
					value = '';
				/*var styleSpanElement = new Element('span');
				styleSpanElement.setStyle(
				{
					"color" : "#FF0000",
					"fontFamily" : "Tahoma"
				}
				);*/
				infoElement.update($(this._checkFormFieldFailElement.cloneNode(false)).writeAttribute('title', value));
				//infoElement.insert(styleSpanElement.update(value));
			}
		}
	}
	if (Object.isFunction(this._options.onSubmitFailure))
		this._options.onSubmitFailure(xhr, form);
	var msg = 'Champs invalides';
	if (xhr.responseJSON.msg)
		msg = xhr.responseJSON.msg;
	this._restoreSubmitElements(form, false, msg);
	this.resizeFormIframe();
};

formValidator.prototype._onFormSubmitException = function(/** Element */ form, /** Ajax.Request */ xhr, /** Exception */ ex)
{
	form.enable();
	this._restoreSubmitElements(form, false, "Incorrect");
	if (Object.isFunction(this._options.onSubmitFailure))
		this._options.onSubmitFailure(xhr, form);
};

formValidator.prototype._showSubcategoryOptions = function(/** Element */ selectElement)
{
	var targetBlock = null;
	var targetForm = null;
	var targetSelect = null;
	var targetCategory = null;

	var selectSubcategoryElement = null;
	var optionElements = null;
	
	selectElement = $(selectElement);
	if (selectElement == null || Object.isUndefined(selectElement.getValue))
		return;
	selectSubcategoryElement = selectElement.next('select');
	if (selectSubcategoryElement == null)
		return;
	if (selectSubcategoryElement.selectedIndex != 0)
	{
		selectSubcategoryElement.selectedIndex = 0;
		this._onElementChange(null, selectSubcategoryElement);
	}
	optionElements = Element.select(selectSubcategoryElement, 'option[value!=0]');
	optionElements.each(function(option, index){
		 //option.remove();
	});

	if (this._formSelectOptions[this._currentBlockIndex] == null)
		return;
	targetBlock = this._formSelectOptions[this._currentBlockIndex];

	if (targetBlock[selectElement.name] == null)
		return;
	
	targetSelect = targetBlock[selectElement.name];
	if (targetSelect[selectElement.value] == null)
		return;
	targetCategory = targetSelect[selectElement.value];
	$A(targetCategory).each(function(optionElement, index){
		selectSubcategoryElement.insert(optionElement);
	});
};

formValidator.prototype.enableDisabledElements = function(/** Element */ form)
{
	elements = form.elements;
	for (var i = 0; i != elements.length; i++)
	{
		if (elements[i].nodeName == "INPUT")
			if (elements[i].disabled == true)
			{
				this._disabledElements.push(elements[i]);
				elements[i].disabled = false;
			}
	}
};

/**
 * remove submit elements of form and replace them by a wait icon
 * @param {Element} form
 * @return {void}
 * @type {Function}
 */
formValidator.prototype._removeSubmitElements = function(form)	
{
	var submitElements;
	
	form = $(form);
	if (!Object.isElement(form))
		return;
	submitElements = form.select('input[type=submit]');
	submitElements.each(function(el){
		var elPos = this._submitElements.push(el) - 1;
		var divEl = new Element('div', {'id': 'submitElement_'+ elPos});
		divEl.setStyle({
			"width": el.getWidth() +'px'
			, 'height': el.getHeight() +'px'
			, 'textAlign': 'center'
		});
		divEl.update($(this._checkFormFieldWaitElement.cloneNode(false)).writeAttribute('alt', 'processing request'));
		el.insert({"before": divEl});
		el.remove();
	}.bind(this));
}

/**
 * restore submit elements of form and
 * @param {Object} form
 * @param {Object} isSuccess
 * @param {Object} msg
 */
formValidator.prototype._restoreSubmitElements = function(form, isSuccess, msg)
{
	form = $(form);
	if (!Object.isElement(form))
		return;
	var color = '#CC0000';
	if (isSuccess === true)
		color = "#00CC00";
	$A(this._submitElements).each(function(submitEl, index){
		var divEl = form.down("#submitElement_"+ index);
		if (Object.isUndefined(divEl))
			return;
		divEl.updateText(msg);
		divEl.setStyle({"backgroundColor" : color, "fontWeight" : "bold", "color" : "#FFFFFF"});
//		new Effect.Highlight(divEl, {
//			'startcolor': color
//			, 'duration': 2.5
//			, 'afterFinish': function(divEl, submitEl){
//				divEl.insert({"before": submitEl});
//				divEl.remove();
//			}.bind(this,divEl, submitEl)
//		});
		new Effect.Opacity(divEl, {
			'from': 1.0
			, 'to': 0.0
			, 'duration': 2.5
			, 'afterFinish': function(divEl, submitEl){
				divEl.insert({"before": submitEl});
				divEl.remove();
				if (Object.isFunction(this._options.onRestoreSubmitElement))
					this._options.onRestoreSubmitElement(divEl, submitEl);
			}.bind(this,divEl, submitEl)
		});
	}.bind(this));
	this._submitElements.clear();
}

formValidator.prototype.disableForm = function(/** Element */ form)
{
	form.disable();
};

formValidator.prototype.enableForm = function(/** Element */ form)
{
	form.enable();
	elements = this._disabledElements;
	for (var i = 0; i != elements.length; i++)
		elements[i].disabled = true;
};

formValidator.prototype.resizeFormIframe = function()
{
//	if (window.frameElement !== null && top.tabs !== null)
//		top.tabs.resizeIframe(window.frameElement.id, window);
};
