/**
 * eprimo Forms
 *
 * Javascript related to forms
 * 
 * stable
 * 
 */

/*
 * TOC
 *
 * eprimo form handler
 */



/* event handlers */
var eprimoFormHandler = {

	defaults: {},
	keyupTimeoutPointer:null,
	jsFormProxy: null,
	jsMyEprimoProxy: null,

	init: function(options) {
		var $this = this;
		$this.options =  $.extend({}, $this.defaults, options); // set $this.options by merging given options with defaults

		$.jsonRPC.setup({
			endPoint: '/rpc/form'
		});

		this.jsFormProxy = new JSONRpcClient("/rpc/form");
		this.jsMyEprimoProxy = new JSONRpcClient("/rpc/myeprimo");
		jsFormProxy = this.jsFormProxy
		jsMyEprimoProxy = this.jsMyEprimoProxy

		// find forms
		$('form').each( function() {
			//debug.log('### assigning handler ###')
			var form = $(this)
			$this.setUp(form)
		})		
	},
	/**
 	* macht einen einfachen rpc call damit der cookie der http session erneuert wird (eg. einen neunen Zeitstempel bekommt)
 	*/
	refreshCookie: function() {
		jsFormProxy.getCurrentAPIVersion( function(result, e) {
		});
	},
	getText: function(formname, textname,targets) {
		var $this=this;
		jsFormProxy.getText( function(result, e) {
			for  (target in targets) {
				targets[target].html(result);
			}
		},formname,textname);
	},
	setUp: function(form, options) {

		//debug.log('### setUp ###')

		var $this = this;
		var formDefinition = form.data('definition')
		if(typeof formDefinition == 'undefined'){
			return
		}
		formDefinition.name = form.attr('name')
		formDefinition.initMethod = 'getDefinition'
		formDefinition.updateMethod = 'getCurrentValues'
		formDefinition.defaultErrorBehaviour = true;
		formDefinition.validateMethod = 'validate'
		formDefinition.saveMethod = 'saveForm'
		formDefinition.params = [form.attr("name")]
		
		$this.getFormDefinition(form)

		form.find('input').live('change', function() {

			if(!$(this).parents('.editor').exists()) {
				var elementName = $(this).attr('name');
				var relatedForm = $(this).parents('form');

				$this.clearMessages(relatedForm, {rootElement:elementName})
				/** HOTFIX: diese Felder werden nicht onTheFly validiert, da dies zu Problemen führt */
				if(elementName != 'street' ||
				elementName != 'baStreet' ||
				elementName != 'currentSupplier' ||
				elementName != 'bankAccountBankName' ||
				elementName != 'contractNumber' ||
				elementName != 'currentSupplier') {
					window.setTimeout( function() {
						$this.validateAction(relatedForm, {rootElement:elementName})
					},250)
				}
			}
		})
		
		if(!$.browser.mozilla){
			form.find('input.ac_input').live('blur', function() {

				if(!$(this).parents('.editor').exists()) {
					var elementName = $(this).attr('name');
					var relatedForm = $(this).parents('form');

					$this.clearMessages(relatedForm, {rootElement:elementName})
					window.setTimeout( function() {
						$this.validateAction(relatedForm, {rootElement:elementName})
					},250)
				}
			})
		}
				
		form.find('input[type=radio]').live('click', function() {
			if(!$(this).parents('.editor').exists()) {
				var elementName = $(this).attr('name');
				var relatedForm = $(this).parents('form');
				$this.clearMessages($(this).parents('form'), {rootElement:$(this).attr('name')})
				window.setTimeout( function() {
					$this.validateAction(relatedForm, {rootElement:elementName})
				},
				250)
			}
		})
		form.find('select').live('change', function() {

			if(!$(this).parents('.editor').exists()) {
				var elementName = $(this).attr('name');
				var relatedForm = $(this).parents('form');

				$this.clearMessages(relatedForm, {rootElement:elementName})
				/** HOTFIX: diese Felder werden nicht onTheFly validiert, da dies zu Problemen führt */
				if(elementName != 'street' ||
				elementName != 'baStreet' ||
				elementName != 'currentSupplier' ||
				elementName != 'bankAccountBankName' ||
				elementName != 'contractNumber' ||
				elementName != 'currentSupplier') {
					window.setTimeout( function() {
						$this.validateAction(relatedForm, {rootElement:elementName})
					},250)
				}
			}
		})
	},
	sendBill: function(billid, hash) {
		jsMyEprimoProxy.sendBillEmail( function(result, e) {
		}, billid, hash);
		alert('Ihre Rechnung wurde versandt.');
	},
	getFormDefinition: function(form, options) {

		// debug.log('### getFormDefinition ###')

		var $this = this;
		var formDefinition = form.data('definition')

		
    // form.addClass("ajaxloading");
    $this._addFormLoadingIndicator(form);
    
    $this._service(form, {
			method: 'init',
			success: function(result) {

				$this.updateFormDefinition(form, {definition:result.result}, function()
        {
  				$this.renderForm(form)
        });
			}
		})
	},
	updateFormDefinition: function(form, options, callbackOnFinish) {
		var $this = this;
		var extendedData = $.extend(form.data('definition'), options.definition)
		form.data('definition',extendedData)
		$this.updateForm(form, {rerender:false}, callbackOnFinish)
	},
	updateForm: function(form, options, callbackOnFinish) {

		var $this = this;
		var formDefinition = form.data('definition');
		var root = options && options.rootElement ? options.rootElement : formDefinition.rootElement;	
	  //console.debug("UPDATE FORM AJAX START");
		$this._service(form, {
			method: 'update',
			success: function(result) {

				form.data('definition').currentValues = result.result;
				
        // console.debug("AJAX SUCCESSS");
        
				if(options && options.rerender==false ) {
					$this.setValues(form)
				} else {
					$this.renderForm(form)
				}
				$(document).trigger('updatedValues', formDefinition)
				
				if(options && options.initHandlers==false) {
					
				} else {
					$this.initHandlers(form);
				}
			  
        if (callbackOnFinish && typeof(callbackOnFinish) == "function")
          $this.renderForm(form, options);
      
      
      }
      
      
      
		})
	},
	flushForm: function(formname) {
		
		var $this = this;
		jsFormProxy.flushForm( function(result, e) {},formname);
	},
	_currentValuesToDefinition: function(form, options) {

		var $this = this;
		var formDefinition = jQuery.extend(true, {}, form.data('definition'));


		$.each(formDefinition.elements, function(name, element) {
			if(options.currentValues[name]) 
      {
				  
//        if (!element.value)
          element.value = options.currentValues[element.name]
			}
		})
		$.each(formDefinition.elements, function(name, element) {

			if((typeof element.value == 'string') && (element.value != null)) {

				var occurences = (element.value.split('@@').length-1)/2;

				for(var i=0;i<occurences;i++) {

					var occurence = i==0? 0 : i*2;
					var tmp = element.value.split('@@');
					if (tmp.length>1) {
						var string = tmp[1].split('@@')[0] ? tmp[1].split('@@')[0] : false;
						var regex = new RegExp('@@'+ string +'@@', 'g');
						var value = options.currentValues[string] == null ? (string.split('@@').length-1 > 0 ? '@@'+string+'@@' : '&nbsp;') : options.currentValues[string];
						element.value = element.value.replace(regex, value)
					}
				}
			}
		})
		return formDefinition;
	
	},
	_service: function(form, options) {
		var $this = this;
		var formDefinition = form.data('definition');
		var params = options.params ? options.params : formDefinition.params;

		if(options.method=='init') {
			params.push(getUrlParams())
		}
		
		$(document).trigger({
			type:'start_form-'+ options.method,
			form: form
		})
		$.jsonRPC.withOptions({
			endPoint: formDefinition.endpoint
		}, function() {
			this.request(formDefinition[options.method +'Method'], {
				params: params,
				success: function(result) {
					options.success(result)
					$(document).trigger('end_form-'+ options.method, [{
						form: form,
						error: false,
						result: result.result
					}])
				},
				error: function(result) {
					//debug.log('ERROR _service: ' + result)
					$(document).trigger('end_form-'+ options.method, [{
						form: form,
						error: true,
						errordata: result.error
					}])

					if(form.data('definition').defaultErrorBehaviour) {
						if(typeof result.error.message != 'undefined') {
							//debug.log('ERROR: Fehler aus Service Instanz '+ result.error.message)
						}
						//location.href=formDefinition.errorPage
					}
				}
			});
		});
	
	},
	/* Provides a rpc proxy */
	_rpcProxy: function(options){
		var $this = this;

		$(document).trigger({
			type:'start_form-'+ options.method,
			form: form
		})
		$.jsonRPC.withOptions({
			endPoint: formDefinition.endpoint
		}, function() {
			this.request(options.method, {
				params: options.params,
				success: function(result) {
					options.success(result)
					$(document).trigger('end_form-'+ options.method, [{
						form: form,
						error: false,
						result: result.result
					}])
				},
				error: function(result) {
					$(document).trigger('end_form-'+ options.method, [{
						form: form,
						error: true,
						errordata: result.error
					}])					
				}
			});
		});
	},
	renderForm: function(form, options) {
		var $this = this;
		var formDefinition = form.data('definition');
		var definition;

    //console.debug("RENDERFORM");
    //console.debug(formDefinition.currentValues);
    
    $this._removeFormLoadingIndicator(form);
    
		//update with new Values, if they exist
		if(formDefinition.currentValues) {
			definition = $this._currentValuesToDefinition(form, {currentValues:formDefinition.currentValues})
		} else {
			definition = formDefinition;
		}
    //console.debug("... after update");
    //console.debug(formDefinition.currentValues);

		// format the given JSON as a tree for easier templating of the form
		// begin with element which is defined as rootElement in formDefinition
		var root = options && options.rootElement ? options.rootElement : definition.rootElement;
		var groupedElements = $this._getElementsFromRoot(definition, {root:root, grouped:true})

		var formDom = $.tmpl("formTemplate", {title: formDefinition.description, elements: groupedElements});

    
    
		// helper function. returns dom without proceeding
		if(options && options.returnDom) {
			return formDom
		}
		form.addClass(form.attr('name') +'-form')
		form.html(formDom)
		$this.setValues(form);
		$this.initHandlers(form, options);
    //console.debug("RENDERED");
    $(document).trigger('formRendered', [{type:'formRendered',form: form,root: root}])
	},
	submitHint: function(elementname, value) {
		jsFormProxy.prefetchHint( function(result, e) {
		}, elementname,value);
	},	
	initHandlers: function(form, options) {
  
    
  
		 //console.debug("initHandlers!");
     //console.debug(form);
		var $this = this;
		var formDefinition = form.data('definition')
  
    form.find("*[name]").unbind();
  	
		// einen onclick handler für die buttons, der die aktionen verarbeitet
		form.find("input[type=button]").bind('click', {'handler':this, 'formDefinition':formDefinition}, function(event) 
    {
			var formDefinition = event.data.formDefinition;
			var handler = event.data.handler;
			var buttonName = $(this).attr('name');
			var buttonElement = formDefinition.elements[buttonName]
			
			if(typeof buttonElement == 'undefined'){
				return;
			}
			
			// zurücksetzen
			for  (idx in buttonElement.parameters) {
				buttonElement.parameters[idx].processed =false;
			}
      //console.debug(buttonElement);
      
      // $(this)._addFormLoadingIndicator(form);
			$this.processElementAction(form, {buttonName:buttonName,buttonElement:buttonElement});
			
		});

     // Enterkey
    form.find("input").bind("keyup", function(e)
    {
      var code = e.keyCode || e.which;
  		// if the Enterkey
  		if(13 === code) 
      {
          // suche nach dem nächsten submit button...
          var body = $("body");
          var actionObject = []; 
          var parent = $(this).parent();
          
          while (actionObject.length < 1 && parent != body)
          {
              parent = parent.parent();
              actionObject = parent.find(".proceed input[type=button]");
          }
          
          if (actionObject.length > 0)
          {         
            actionObject.trigger("click");
          }
  		}
    
    });

    
		// In $currentActiveForm wird gepuffert, welche die aktuelle Form ist
		$("input").bind("focus", function() {
			$currentActiveForm = $(this).parents('form');
		});

    
		// über alle elements
		for (name in formDefinition.elements) 
    {
     
		  // form.find("*[name]").unbind();
      
    	// über alle parameter
			// mit prefetch markiere feldinhalte während der eingabe zum server schicken, soll der sehen was er damit anfängt
			for  (parameter in formDefinition.elements[name].parameters) {
        
        
        // debug.log('binde '+parameter+': '+name);
				switch (parameter) {
					case 'autocomplete':
           
						// debug.log('binde autocomplete '+name);
            
						var _ref = this._findById('reference',formDefinition.elements[name].parameters.autocomplete.values);
						var _must = this._findById('must',formDefinition.elements[name].parameters.autocomplete.values);

						form.find("*[name="+name+"]").data('autocomplete_ref', _ref)
						form.find("*[name="+name+"]").autocomplete('/rpc/autocomplete', {
							matchContains: true,
							cacheLength:1,
							minChars: 0,
							autoFill:true
						});
						form.find("*[name="+name+"]").bind("focus", function() 
            {
						  //console.debug("HERE I AM!");
            	var name= $(this).attr('name');
							var ref= $(this).data('autocomplete_ref');
							var value = $(this).parents('form').find('*[name='+ ref +']').val();
							$(this).setOptions({ extraParams: {name: name, reference: ref, value: value}})
						})
            
            
            
						break;
					case 'prefetchHints':
						form.find("*[name="+name+"]").bind('keyup change blur click focus', function(event) {
							
              // debug.log('binde prefetchHints '+name);
              var $me = $this;
							var $that = $(this);
							if ($this.keyupTimeoutPointer!=null) {
								window.clearTimeout($this.keyupTimeoutPointer);
							}
							$this.keyupTimeoutPointer= window.setTimeout( function() {
								$me.submitHint($that.attr('name'),$that.val());
							},250);
						}
						);
						break;
					// apply initial "visible" state and bind trigger to all related elements
					case 'visible':
						var targetElement = form.find('*[name='+ name +']');
						var relatedElement = form.find('*[rel='+ name +']');
						
						function hideElement(el){
							el.addClass('hidden')
						}
						
						function showElement(el){
							el.removeClass('hidden')
						}
						
						if(!$this.checkDependencies(form, {elementName:name, parameterName: parameter})){
							hideElement(targetElement)
							hideElement(relatedElement)
						} else {
							showElement(targetElement)
							showElement(relatedElement)
						}
						
						var dependencies = formDefinition.elements[name].parameters[parameter].values;
						for (i in dependencies) {
							form.find("*[name="+ dependencies[i].id +"]").bind('change',
								{
									form: form,
									name: name,
									parameterName: parameter
								},
								function(event) {
									if(!$this.checkDependencies(form, {elementName: event.data.name, parameterName: event.data.parameterName})){
										hideElement(event.data.form.find('*[name='+ event.data.name +']'))
										hideElement(event.data.form.find('*[rel='+ event.data.name +']'))
									} else{
										showElement(event.data.form.find('*[name='+ event.data.name +']'))
										showElement(event.data.form.find('*[rel='+ event.data.name +']'))
									}
								}
							);
						}
					break;
					// apply initial "conditional" (disabled) state and bind trigger to all related elements
					case 'conditional':

						var targetElement = form.find('*[name='+ name +']');
						function disableElement(el){
							el.attr('disabled', true);
							el.val('');							
							$this.clearMessages(form, {rootElement:el.attr("name")})
						}
						
						function enableElement(el){
							el.attr('disabled', false);
						}
						
						if($this.checkDependencies(form, {elementName:name, parameterName: parameter})){
							enableElement(targetElement)
						} else {
							disableElement(targetElement)
						}
						
						var dependencies = formDefinition.elements[name].parameters[parameter].values;
						for (i in dependencies) {
							form.find("*[name="+ dependencies[i].id +"]").bind('change',
								{
									form: form,
									name: name,
									parameterName: parameter
								},
								function(event) 
                {
									if($this.checkDependencies(form, {elementName: event.data.name, parameterName: event.data.parameterName})){
										enableElement(event.data.form.find('*[name ='+ event.data.name +']'))
									} else{
										disableElement(event.data.form.find('*[name ='+ event.data.name +']'))
									}
								}
							);
						}
					break;
				}
			}
		}		
		if(options && typeof options.throwEvent != 'undefined' && options.throwEvent == false){
	
		}else{
			$(document).trigger('handlersInitialized', [{form: form}])
		}			
	},
	// some elements depend on one ore more other elements value(s). These conditions are checked here
	// requires form, "options.elementName" (the element to check) and "options.parameterName" e.g. "visible" or "conditional"
	// returns true, if dependencies are met, false if not or something went wrong
	checkDependencies: function(form, options){
		var $this = this;
		var formDefinition = form.data('definition');
		
		var el = options.elementName, param = options.parameterName;
		
		if(!formDefinition.elements[el] || !formDefinition.elements[el].parameters[param] || !formDefinition.elements[el].parameters[param].values){
			// debug.warn('something went wrong in checkDependencies(form, options), check if options.elementName and options.parameterName are set correctly')
			return false;
		}
		
		var fullfilled, conditions = formDefinition.elements[el].parameters[param].values;
		for (i in conditions) {
			var currentVal = $this.getFieldValue(form, {name: conditions[i].id})
			fullfilled = conditions[i].value == currentVal ? true : false;
			// debug.log('does current field value of "'+ conditions[i].id +'" element matches defined value: '+ conditions[i].value +'? returning: '+ fullfilled)
			if(!fullfilled){
				break;
			}
		}
		// debug.log('Do all conditions match? returning: '+ fullfilled)
		return fullfilled;
	},
	processElementAction: function(form, options) {

		var $this = this;
		var buttonName = options.buttonName;
		var buttonElement = options.buttonElement;

		for  (idx in buttonElement.parameters) {
			if (buttonElement.parameters[idx].type=='action') {
				// aktionen die an diesem button hängen ausführen
				if (!buttonElement.parameters[idx].processed ) {
					switch (buttonElement.parameters[idx].name) {
						case 'resetform':
							buttonElement.parameters[idx].processed=true;
							$this.resetAction(form, {buttonName:buttonName, buttonElement:buttonElement});
							break;
						case 'clearfields':
							buttonElement.parameters[idx].processed=true;
							break;
						case 'validate':
              $(document).trigger('validate-button-clicked', {type: 'feedback', container: 'feedback_container'}) // Ajax Animation            
							buttonElement.parameters[idx].processed=true;
							$this.validateAction(form, {elementName:buttonName, buttonElement:buttonElement, skipClientsideValidation: true});
							return;
							break;
						case 'triggerAction':
							buttonElement.parameters[idx].processed=true;
							var i = idx;
							var _type = $this._findById('type',buttonElement.parameters[i].values);
							var _container = $this._findById('container',buttonElement.parameters[i].values);
							triggerAction({type: _type, containerId: _container})
							break;
						case 'redirect':
							buttonElement.parameters[idx].processed=true;
							$this.redirectAction(form, {buttonName:buttonName, buttonElement:buttonElement});
							return ;
						case 'save':
							buttonElement.parameters[idx].processed=true;
							$this.saveAction(form, {buttonName:buttonName, buttonElement:buttonElement});
							return ;
							break;
						case 'render':
							buttonElement.parameters[idx].processed=true;
							//var rootElement = $this._findById(form, {rootElement:'rootElement', values:buttonElement.parameters[idx].values});
							form.data('definition').rootElement = buttonElement.parameters[idx].values[0].value;
							$this.updateForm(form) ;
							break;
					}
				}
			}
		}
	},
	validateAction: function(form, options) {

		//debug.log('### validate action ###')
		//debug.log(form)
    

		var $this = this;
    $this._addFormLoadingIndicator(form);
		var elementName = options.elementName ? options.elementName : false;
		var buttonElement = options.buttonElement ? options.buttonElement : false;
		
		//var formValues = $this.collectValues(form);
		var formDefinition = form.data('definition')
		//var params = [formDefinition.name];
		//	options.rootElement=false;
		//params.push(formValues)
		var root = options.rootElement ? options.rootElement : formDefinition.rootElement;
		// ggf root element setzen - hs 08.09.11
		if (buttonElement.parameters) {
			if (buttonElement.parameters[idx].values[0]) {
				root=buttonElement.parameters[idx].values[0].value;
			}
		}
		//params.push(root);
		var affectedElements = $this._getElementsFromRoot(formDefinition, {root:root});

		if(typeof options.skipClientsideValidation == 'undefined' || options.skipClientsideValidation == false) {
			// list all the rules that can be validated on clients side
			var clientSideValidatableRules = ['notEmpty', 'length'];
			var validateByClient=true;
			$.each(affectedElements, function(i, element) {
				var allGood = true;
				$.each(element.rules, function(i, rule) {
					var found = false;
					$.each(clientSideValidatableRules, function(i, rulename) {
						if(rule.name == rulename) {
							found = true;
						}
					})
					if(!found) {
						allGood = false;
            $this._removeFormLoadingIndicator(form);
						return;
					}
				})
				if(!allGood) {
					validateByClient = false;
          $this._removeFormLoadingIndicator(form);
					return;
				}
			})
			if(validateByClient) {
				$this.checkElements(form, {elements: affectedElements, rootElement: root})
        $this._removeFormLoadingIndicator(form);
				return;
			}
		}
		
		var formValues = $this.collectValues(form);
		var params = [formDefinition.name];
		params.push(formValues)
		params.push(root);
		
		$this._service(form, {
			method: 'validate',
			params: params,
			success: function(result) {
        //console.debug("OKOKOK");
        $this._removeFormLoadingIndicator(form);      
				$this.validateForm(form, {result:result.result, rootElement: root})
				if (!result.result.hasErrors) {
					if(buttonElement) {
						$this.processElementAction(form, {buttonName:elementName, buttonElement:buttonElement});
					}
					if(options && options.throwEvent) {
						form.trigger('validated')
					}
				}
			}
		})	
	},
	redirectAction: function(form, options) {

    
		var $this = this;
    $this._addFormLoadingIndicator(form);
		var elementName = options.elementName ? options.elementName : false;
		var buttonElement = options.buttonElement ? options.buttonElement : false;
		var formDefinition = form.data('definition')
		if(typeof buttonElement.parameters.redirect!= 'undefined') {
			// debug.log(buttonElement.parameters.redirect.values);
			location.href=formDefinition[buttonElement.parameters.redirect.values[0].value]
		}
	},
	resetAction: function(form, options) {

		var $this = this;
		var elementName = options.elementName ? options.elementName : false;
		var buttonElement = options.buttonElement ? options.buttonElement : false;
		var formDefinition = form.data('definition')
		var params = [formDefinition.name];
		//	options.rootElement=false;
		for (name in formDefinition.elements) {
			form.find("input[name="+name+"]").val('')
		}
	},
	checkElements: function(form, options) {
		var $this = this;
		var formDefinition = form.data('definition')
		var root = options.rootElement? options.rootElement : formDefinition.rootElement;
		$.each(options.elements, function(i,el) {
			var relatedElement = form.find('*[name='+ el.name +']');
			var val = relatedElement.val();
			//debug.log("relatedElement")
			//debug.log("val")
			//debug.log(relatedElement)
			//debug.log(val)
			$.each(el.rules, function(i,rule) {
				var checkResult = check(rule, val);
				if(checkResult.valid==false) {
					var result = {validationResults: [{message: checkResult.message, name: el.name, parameters: null, type: 'error'}]}
					$this.validateForm(form, {result: result, rootElement: root})
					return false;
				} else {
					$this.clearMessages(form, {rootElement: root})
				}
			})
		})
		function check(rule, value) {
			var result = {valid: true, message: 'no errors'};
			//debug.log('check if "'+ value +'" mtaches "'+ rule.name +'"')
			switch(rule.name) {
				case 'notEmpty':
					if($.trim(value).length<1) {
						result.valid = false;
						if(rule.values.length>0 && rule.values[0].value) {
							result.message = rule.values[0].value
						} else {
							result.message = 'error in "notEmpty" check, but no message defined'
						}
					}
					break;
				case 'length':
					var conclusion =[];
					$.each(rule.values, function(i, ruleVal) {
						if(ruleVal.id=='minLength') {
							if(value.length < ruleVal.value) {
								result.valid = false;
								conclusion.push('errorToSmall')
								conclusion.push(ruleVal.value);
							}
						} else if(ruleVal.id=='maxLength') {
							if(value.length > ruleVal.value) {
								result.valid = false;
								conclusion.push('errorToBig')
								conclusion.push(ruleVal.value);
							}
						}
					})
					if(!result.valid) {
						$.each(rule.values, function(i, ruleVal) {
							if(ruleVal.id == conclusion[0]) {
								result.message = $this._replacePlaceholder(ruleVal.value, '@', conclusion[1]);
							}
						})
					}
					break;
				case 'string':
					break;
			}
			//debug.log('clientside validation. "'+ rule.name +'" is '+ result.valid +'. message: "'+ result.message +'"')
			return result;
		}

	},
	_replacePlaceholder: function(string, placeholderMarker, placeholder) {

		var originalString = string;
		var newString = '';
		if((typeof originalString != 'undefined') && (originalString != null) && (originalString.indexOf(placeholderMarker)!= -1)) {
			var occurences = (originalString.split(placeholderMarker).length-1)/2;
			for(var i=0;i<occurences;i++) {
				var occurence = i==0? 0 : i*2;
				var string = originalString.split(placeholderMarker)[1].split(placeholderMarker)[0];
				var regex = new RegExp(placeholderMarker +''+ string +''+ placeholderMarker +'', 'g');
				var value = placeholder;
				newString = originalString.replace(regex, value)
			}
		}
		//debug.log('_replacePlaceholder. Original string: "'+ originalString +'". returning: "'+  newString +'". placeholderMarker was: "'+ placeholderMarker +'", placeholder: "'+ placeholder)
		return newString;
	},
	validateForm: function(form, options) {

		var $this = this;
		var formDefinition = form.data('definition')
		var root = options.rootElement? options.rootElement : formDefinition.rootElement;
		$this.clearMessages(form, {rootElement: root})
		var validationResults = options.result.validationResults;

		for (m in validationResults) {

			var name= validationResults[m].name;

			switch (validationResults[m].type) {
				case 'debug':
					// debug.log(validationResults[m].message);
					break;
				case 'message':
					if(form.find("*[name="+name+"]").exists()==false) {
						//debug.log('ERROR: falscher Wert '+ name)
					}
					var relatedInput = form.find("*[name="+name+"]");
					var relatedParent = relatedInput.parents('.group').exists() ? relatedInput.parents('.group') : relatedInput.parents('fieldset')
					if(relatedParent.find('.message[rel='+ name +']').exists()) {
						relatedParent.find('.message[rel='+ name +']').html('')
						relatedParent.find('.message[rel='+ name +']').append('<span>'+ validationResults[m].message +'</span>')
					} else {
						relatedParent.find('.error-messages').append('<div class="message" rel="'+ name +'"><span>'+ validationResults[m].message +'</span></div>')
					}
					break;
				case 'notice':
					if(form.find("*[name="+name+"]").exists()==false) {
						//debug.log('ERROR: falscher Wert '+ name)
					}
					var relatedInput = form.find("*[name="+name+"]");
					var relatedParent = relatedInput.parents('.group').exists() ? relatedInput.parents('.group') : relatedInput.parents('fieldset')
					if(relatedParent.find('.message[rel='+ name +']').exists()) {
						relatedParent.find('.notice[rel='+ name +']').html('')
						relatedParent.find('.notice[rel='+ name +']').append('<span>'+ validationResults[m].message +'</span>')
					} else {
						relatedParent.find('.error-messages').append('<div class="notice" rel="'+ name +'"><span>'+ validationResults[m].message +'</span></div>')
					}
					break;
				case 'error':
					if(form.find("*[name="+name+"]").exists()==false) {
						//debug.log('ERROR: falscher Wert '+ name)
					}
					var relatedInput = form.find("*[name="+name+"]");
					var relatedParent = relatedInput.parents('.group').exists() ? relatedInput.parents('.group') : relatedInput.parents('fieldset')
					relatedInput.addClass('fieldError');
					if(relatedParent.find('.message[rel='+ name +']').exists()) {
						relatedParent.find('.error[rel='+ name +']').html('')
						relatedParent.find('.error[rel='+ name +']').append('<span>'+ validationResults[m].message +'</span>')
					} else {
						relatedParent.find('.error-messages').append('<div class="error" rel="'+ name +'"><span>'+ validationResults[m].message +'</span></div>')
					}
					break;
				case 'action':
					$this.handleAction(form, {action: validationResults[m]});
					break;
			}
		}
	},
	collectValues: function(form, options) {

		var $this = this;
		var formDefinition = form.data('definition')
		var formValues = new Array();

		for (name in formDefinition.elements) {

			valueObj=new Object();
			valueObj.name=name;
			
			var type =formDefinition.elements[name].type
			if(type=='group' || type == 'text' || type == 'button'){
				valueObj.value=null
			}else{
				valueObj.value=$this.getFieldValue(form, {name:name})
				//debug.log('returned value for "'+ name +'": '+ valueObj.value)
			}				
			formValues.push(valueObj);
		}

		// Cookie Value "eprsrv" mit in formValues als "epsrvCheckVal" packen.
		// Verhindert eine CSFR Attack 
		formValues.push({name:"epsrvCheckVal", value:$.cookie("epsrv")});

		return formValues;

	},
	// get a value out of form. requires form & options.name,
	// returns value of the element as "string" if one element with specified "name"-attribute was found
	// returns value of the element as "Array" if more than one element with specified "name"-attribute was found
	// returns null if no value is set on an element or if some error occured
	getFieldValue: function(form, options){
		var fields = form && options && options.name ? form.find('*[name='+ options.name +']') : false;
		
		// error handling
		if(!fields || !fields.exists()){  
			//debug.warn('something not ok in "getFormValue(form (jQuery object of form element), options(a Javascript Object)')
			if(form && options && options.name){
				//debug.warn('you tried to get the value of the element with "name" attribute of:  "'+ options.name +'" , which seems to not exist.')
			}else{
				//debug.warn('options.name is required, specifying the "name"-Attribute of a from element to search for')
			}
			return null;
		}
		if(fields.length>1){
			fields.each(function(i,el){
				if($(el).attr('type')!='radio'){
					//debug.warn('multiple occurences with attribute "name" = '+ options.name +' found!')
					//debug.warn('multiple elements with same "name" attribute are only allowed with radio buttons')
					return null;
				}
			})
		}
		var fieldtype = fields.eq(0).attr('type');

		var valueToReturn = null;
		switch(fieldtype){
			case 'radio':
				valueToReturn = fields.filter(':checked').exists()? fields.filter(':checked').val() : null;
			break
			case 'checkbox':
				valueToReturn = fields.filter(':checked').exists()? fields.filter(':checked').val() : null;
			break
			default:
				valueToReturn = fields.val();
			break;
		}			
		return valueToReturn;
	},
	_findById: function (id, values) {

		for (idx in values) {
			if (values[idx].id == id) {
				return values[idx].value;
			}
		}
		return null;
	},
	setValues: function(form, options) {
		var $this = this;
		var formDefinition = form.data('definition');
		var definition;

		if(formDefinition.currentValues) {
			definition = $this._currentValuesToDefinition(form, {currentValues:formDefinition.currentValues})
		} else {
			definition = formDefinition;
		}

		//debug.log(definition.elements)
		for (name in definition.elements) {

			// default inhalte
			if(definition.elements[name].type == 'combobox') {
				if ((definition.elements[name].parameters!=undefined) && (definition.elements[name].parameters.options!=undefined)) {
					$this.setFieldOptions(name,definition.elements[name].parameters.options.values, form);
					var v = definition.elements[name].value == null ? definition.elements[name].defaultValue : definition.elements[name].value;
					form.find("*[name="+name+"]").val(v);
				}
			}

			if (definition.elements[name].defaultValue || definition.elements[name].value) {

				switch (definition.elements[name].type) {
					case 'checkbox':
						var v = definition.elements[name].value == null ? definition.elements[name].defaultValue : definition.elements[name].value;
						form.find("input[name="+name+"]").filter('[value='+v+']').attr('checked', true);
						break;
					case 'radio':
						var v = definition.elements[name].value == null ? definition.elements[name].defaultValue : definition.elements[name].value;
						form.find("input[name="+name+"]").filter('[value='+v+']').attr('checked', true);
						break;
					case 'text':
					    if(definition.elements[name].value != formDefinition.elements[name].value){
							form.find('div[rel='+name+']').html('<p>'+ definition.elements[name].value +'</p>')					    	
					    }
						break;
					case 'combobox':
					case 'select':
						var v = definition.elements[name].value == null ? definition.elements[name].defaultValue : definition.elements[name].value;
						form.find("*[name="+name+"]").val(v);
						break;
					default:
						var v = definition.elements[name].value == null ? definition.elements[name].defaultValue : definition.elements[name].value;
						form.find("input[name="+name+"]").val(v);
						
						break;
				}
				
				
			}
			
		}
		$(document).trigger('updatedValues', [{form: definition}])
	},
	saveAction: function(form, options) {

    //console.debug("SAVE");

    
		var $this = this;
    
    $this._addFormLoadingIndicator(form);
    
		var elementName = options.elementName;
		var buttonElement = options.buttonElement;

		var formValues = $this.collectValues(form);
		var formDefinition = form.data('definition');
		
		var root = null;
		// ggf root element setzen - hs 08.09.11
		if (buttonElement.parameters) {
			if (buttonElement.parameters[idx].values[0]) {
				root=buttonElement.parameters[idx].values[0].value;
				//			   params.push(root);
			}
		}
		
		$this._service(form, {
			method: 'save',
			params: [formDefinition.name,formValues,root],
			success: function(rawresult) {
				$this._removeFormLoadingIndicator(form);
        $this.validateForm(form, rawresult);
				if (!rawresult.result.hasErrors) {
					if(buttonElement) {
						$this.processElementAction(form, {buttonName:elementName, buttonElement:buttonElement});
					}				
				}
        
				/*else{ alert("FORM SAVE ERROR") }*/
			}
		})		
	},
	/**
 	* handle action, e.g. sent by validation result
 	*/
	handleAction: function(form, options) {

		//debug.log('### handleAction ###')

		var $this = this;
		var action = options.action;
		switch (action.name) {
			case 'redirect':
				var toPage = $this._findById('toPage',action.parameters);
				//XXXXXXXXXXXXX TODO hier einsetzen andere toPage;
				var formDefinition = form.data('definition')
				// $this.redirectAction(form, {buttonName:buttonName, buttonElement:buttonElement});
				location.href=formDefinition.errorPage
				break;
			case 'render':
				//debug.log('in handle action render:')
				var rootElement = $this._findById('rootElement',action.parameters);
				var currentValues = $this._findById('currentValues',action.parameters);
				// debug.log(currentValues);
				var definition = this._currentValuesToDefinition(form, {currentValues:currentValues})
				$this.renderForm(form, {rootElement: rootElement}) ;
				break;
			case 'reloadForms':
				$.each(action.parameters[0], function(i,formname) {
					if($('form[name='+ formname +']').exists()) {
						eprimoFormHandler.getFormDefinition($('form[name='+ formname +']'))
					}
				})
				break;
			case 'clearRows':
				// todo implement dynamic table row clearing
				break;
			case 'addRow':
				// todo implement dynamic table row adding
				break;
			case 'setOptions':
				// alert(JSON.stringify(action.parameters.options));

				$this.setFieldOptions(action.parameters.field,action.parameters.options, form);
				break;
			case 'set':
				for (p in action.parameters) {
					var n = action.parameters[p].field;
					form.find("input[name="+n+"]").val(action.parameters[p].value);
					if(form.find("input[name="+n+"]").hasClass('fieldError')){
						$this.clearMessages(form, {rootElement: n})
					}
				}
				break;
		}
		// logMessage(JSON.stringify(action));

	},
	setFieldOptions: function(name, options,form) {

		var $this = this;
		// TODO: check for field type

		if (options.length>0) {
			var relatedInput = form.find("*[name="+name+"]");
			var element=$this._getElementsFromRoot(form.data('definition'), {root: name})

			var currentSelection=relatedInput.val();
		
			if(element[0].type=='combobox') {
				var comboboxSelectMarkup = '<select name="'+ name +'">'
				$.each(options, function(i,option) {
					var id = option.id;
					if (id==undefined) {
						id=option;
					}
					var value = option.value;
					if (value==undefined) {
						value = option;
					}
					comboboxSelectMarkup += '<option value="'+ id
					comboboxSelectMarkup +='">'+ value +'</option>'
				})
				comboboxSelectMarkup += '</select>';

				relatedInput.replaceWith($(comboboxSelectMarkup))
				relatedInput = form.find("*[name="+name+"]");
				relatedInput.val(currentSelection);
			} else {
				relatedInput.val(options[0]);
			}
		}
	},
	// clears validation messages & errors 
	clearMessages: function(form, options) {
		var $this = this;
		var formDefinition = form.data('definition');
		var elementsToClear = $this._getElementsFromRoot(formDefinition, {root: options.rootElement, grouped: false})

		$.each(elementsToClear, function(key, value) {
			var relatedInput = form.find("input[name="+value.name+"]");
			var relatedParent = relatedInput.parents('.group').exists() ? relatedInput.parents('.group') : relatedInput.parents('fieldset')
			relatedParent.find('.error-messages div[rel='+ value.name +']').remove()
			relatedInput.removeClass('fieldError')
		})		
	},
	_getElementsFromRoot: function(definition, options) {
		var groupedElements = [];

		$.each(definition.elements, function(i, el) {
			function populate(group) {
				group.members = []; // create an "members" array to store elements belonging to this group
				$.each(definition.elements, function(i, el) { // find the elements, that match the group name
					if((typeof el.groups != 'undefined') &&(el.groups.length>0) && (el.groups[0].name==group.name)) {
						group.members.push(el)
					}
				})
				$.each(group.members, function(i,el) {
					if(el.type=='group') { // if some child elements are groups ..
						populate(el)  // ..call the "populate" function again
					}
				})
			}

			if(el.name==options.root) {
				populate(el)
				groupedElements.push(el)
			}
		})
		// if options grouped == true return grouped elements
		if(options && options.grouped) {
			return groupedElements;
		}

		//else collect in flat array
		var flatElements = [];

		$.each(groupedElements, function(i, el) {
			function getMembers(el) {
				$.each(el.members, function(i, el) {
					flatElements.push(el)
					if(el.members) {
						getMembers(el)
					}
				})
			}

			flatElements.push(el)
			if(el.members) {
				getMembers(el)
			}
		})
		return flatElements;
	},
  _addFormLoadingIndicator: function(form)
  {

    var xtdiv = $("<div class='ajaxloading'><div class='ajaxloading-indicator'></div><div></div>"); 

    var jObj = form;
    var w = jObj.width();
    var h = jObj.height();
    
    // Wenn form in einer subsection...
    // kurz aufmachen - höhe holen und wieder zumachen
    var subsectionContainer = form.parents(".subsection:first").eq(0); 
    if (subsectionContainer.hasClass("closed"))
    {
      subsectionContainer.removeClass("closed")
      w = jObj.width();
      h = jObj.height();
      subsectionContainer.addClass("closed")
    
    }
     
    

    // Wenn Form in einer Lightbox...
    if (form.parents(".lightbox").length > 0)
    {
      jObj = form.parents(".lightbox"); 
      xtdiv.css("margin-left", "-"+jObj.css("padding-left"));
      w+= parseInt(jObj.css("padding-right"))+parseInt(jObj.css("padding-left"));          
      h+= parseInt(jObj.css("padding-top"))+parseInt(jObj.css("padding-bottom"))+38;
      //console.debug(jObj.css("border-radius"));
      xtdiv.css("border-radius", jObj.css("border-radius"));
    }
  
  
  
    xtdiv.height(h).width(w);
    var indicator = xtdiv.find(".ajaxloading-indicator");
    indicator.css("margin-top", Math.round((h-indicator.height())/2));
    
    
    
    
    // form.css("position", "relative").
    jObj.prepend(xtdiv);
    
  },
  _removeFormLoadingIndicator: function(form)
  {
    var jObj = form;
    if (form.parents(".lightbox").length > 0)
    {
      jObj = form.parents(".lightbox"); 
    
    }
  
  
    /*
    console.debug("removeFormLoadingIndicator"); 
*/
    jObj.find(".ajaxloading").remove();
    // form.css("background", "transparent");
    // $(document).trigger('handlersInitialized', [{form: form}])
  }
}

function getUrlParams() {
	var params = {};
	window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(str,key,value) {
		params[key] = value;
	});
	return params;
}


