/** 
  * GUIControle is the base object for GUI controls. Many controls as based on this prototype.
  * @param {Object} model, the model this control uses
  * @param {String} nameInModel, the unique name the GUI control has in the model
  * @param {String} refInHTML, a reference to an ID or class (if multiple instances) in the HTML
  * @param {string} opt_type Argument 'Type' is optional if the control is already present in the DOM.
  * @param {Array} opt_domainData is optional. Should be used if a domain is shared between different controls. Domain data is by default 
					retrieved based on the model name but can be overridden in the constructor by passing a domain as an array. 
  */
 function GUIControle(pmodel, nameInModel, refInHTML, opt_type, opt_domainData){
 	var model = pmodel;
	var nameInModel = nameInModel;
	var refInHTML = refInHTML;
	var type = opt_type;
	var domainData; // Holds the domain data if any for this control
	var modelValues; // The data that this control owns in the model

	
	/***
	 * INITIALIZE. The first method called to initialize this control by 
	 * binding it to the model and the GUI (DOM elements). 
	 */
	var initialize = function(){
		
		
		console.log("Initializing control: " + nameInModel);
		// Make sure value(s) is in an Array
		modelValues = typeof(model.getData()[nameInModel]) !== 'undefined' ? model.getData()[nameInModel] : new Array();
		modelValues = $.isArray(modelValues) ? modelValues : [modelValues];
		
		console.info(modelValues);
		
		// Attach the functions that should be called when the model 
		// has performed a validation or update of the data
		model.addValidationListeners(manageValidationResult, this)
		model.addUpdateListers(this.manageUpdateResult, this)
		
		// Build the controls here if domainData is present. Domain data is by default 
		// retrieved based on the model name but can be overridden in the constructor by passing
		// a domain as an array.
		if (typeof opt_domainData != "undefined") {
			domainData = opt_domainData;
			buildControls(domainData)
			
		}
		
		else if (typeof model.getDomainData()[nameInModel] != "undefined"){
			domainData = model.getDomainData()[nameInModel];
			buildControls(domainData)
			
		}
		
		// Find and set initial values to input controls
		findSimpleControls(modelValues);
		
		
		// Attach change events to update data model when GUI changes. 
		// This should only be done once per UI_control element so to prevent 
		// complex controls that share UI_control container from getting double 
		// evenhandlers we first check the presence of existing change events.
		
		var myEvents = $(refInHTML).data("events");		
		if(!(myEvents && myEvents.change)){
			
				
			var thisContext = this;
			
			$(refInHTML).live("change", function(evt){
				
				clearMessages(); // Remove existing messages
				// The changed method can be overridden by object augmentation   
				thisContext.changed(evt);
			});
					

		}
		

		
	}// END INITIALIZE
	
	
	
	/***
	 * This method is called when the control was changed. It can be overridden
	 * by object augmentation to create more specialized controls.
	 * @param {Object} evt
	 */
	
	this.changed = function(evt){
		
		// The model needs to know if it will be updating an Array or a single value
		var targetType = (type === 'checkbox') ? 'multiple' : 'single'; 	
		model.update(nameInModel, $(evt.target).attr("value"), targetType);			
	}
	
	
	
	/***
	 * Can be called to force the control to be redrawn using the current state of model data.
	 * Is useful for complex controls that interact on the same model. The only reason to force
	 * a new render is that the domain has changed. 
	 */
	this.forceRender = function(newDomain){
		// Put the controls on the screen
		buildControls(newDomain);
		
		// If the domain changed from not existing to an existing, i.e. the domain was not
		// present whne the control was initialized we have to make sure we have the values  		
		// Make sure value(s) is in an Array
		modelValues = $.isArray(model.getData()[nameInModel]) ? model.getData()[nameInModel] : [model.getData()[nameInModel]];
		// Select the values corresponding to the model state
		findSimpleControls(modelValues);		
	}
	
	
	/***
	 * Build domains for controls with domains from the server
	 * @param {Object} domainItems
	 */
	var buildControls = function(domainItems){
		var GUIControle = $(refInHTML);
		
		switch(type) {
			case "checkbox":
				// Remove any previous elements
				GUIControle.find('input').each(function(index, Element){
					if($(Element).attr('type') === 'checkbox') {$(Element).parent().remove()}					
				});
				
				// Create the new elements
				
				for (var domnr=0; domnr<domainItems.length; domnr++) {
					GUIControle.append("<label><input type='checkbox' value='" + domainItems[domnr] +"'>"+domainItems[domnr]+"</label>")
				};
				break;
			case "radio":
				// Remove any previous elements
				GUIControle.find('input').each(function(index, Element){
					if($(Element).attr('type') === 'radio') {$(Element).parent().remove();
					}
				});
				
				// Create the new elements 
				for (var domnr=0; domnr<domainItems.length; domnr++) {
					GUIControle.append("<label><input type='radio' name='"+ refInHTML +"' value='" + domainItems[domnr] +"'>"+domainItems[domnr]+"</label>")
				};
				break;
			case "select-one":
				var selectList = $(GUIControle).find('select');
				selectList.append("<option value=''></option>"); // Add empty to top to represent no selection
				for (var domnr=0; domnr<domainItems.length; domnr++) {
					selectList.append("<option value='" + domainItems[domnr]+"'>"+domainItems[domnr]+"</option>")
				};
				
				//
				break;
		}


		
	}
	
	
	
	// Find simple controls
	var findSimpleControls = function(values){	

				
		// For each HTML input element belonging to this control. 
		$(refInHTML).find("input").andSelf().filter("input").each($.proxy(function(index, element){


				// For each value in the model belonging to this control
				for (var i = 0; i < values.length; i++) {
					initSimpleControl(values[i], element);
			}
			
		},this));
	
		// For each HTML Select element belonging to this control. 
		$(refInHTML).find("Select").andSelf().filter("Select").each($.proxy(function(index, element){

			// For each value in the model belonging to this control
			for (var i=0; i<values.length; i++) {
				initSimpleControl(values[i], $(element).find("option[value|=' + values[i] +']"));			
			}
		
		},this));
		
		// For each HTML textarea element belonging to this control. 
		$(refInHTML).find("textarea").andSelf().filter("textarea").each($.proxy(function(index, element){

			// For each value in the model belonging to this control
			for (var i=0; i<values.length; i++) {
				initSimpleControl(values[i], element);			
			}
		
		},this));
		

		
	}
	
 
 	// Initializes simple controls
	var initSimpleControl = function(value, element){
		
		if($(element).is("textarea")){
			$(element).val(value);
			return;	
		}
		
		var HTMLType = $(element).attr("type");
		
		// checkboxes and radio buttons
		if(HTMLType == "radio" || HTMLType == "checkbox") {				
			if($(element).attr("value") == value) $(element).attr("checked", "checked");					
		}
		// text fields
		else if(HTMLType == "text" || HTMLType == "number"){
			$(element).attr("value", value);
		}	
		// single select lists
		else if($(element).parent().attr("type") == "select-one"){ // <option> tags don't have a type so we check for parent
			$(element).attr("selected", "selected");
		}
		// multiple select lists
		else if($(element).parent().attr("type") == "select-multiple"){ // <option> tags don't have a type so we check for parent
			$(element).attr("selected", "selected");
		}	
		
	}
	
	/**
	 * If there was an error message present it is removed
	 */
	var clearMessages = function(){
		
		var uiControl = $(refInHTML).hasClass("UI_control") ? $(refInHTML) : $(refInHTML).parents(".UI_control");
		if ($(uiControl).find(".validationError").length > 0) {
			$(uiControl).find(".validationError").remove();
		}
		return 
		
	}
	
	/**
	 * If the model validation resulted in an action or an error the name of 
	 * the GUI control affected is sent back to be dealt with here
	 * @param {Object} message {"Reciever" : nameInModel, "Error":{string}}
	 */
	var manageValidationResult = function(message){	
		// Check if the message is for me
		
		if(message.Reciever == nameInModel){

			var uiControl = $(refInHTML).hasClass("UI_control") ? $(refInHTML) : $(refInHTML).parents(".UI_control");
			
			// If there was an error message present it is updated.			
			if(uiControl.find(".validationError").length > 0){
				uiControl.find(".validationError").html(message.Error);
			} 
			else{
				$(uiControl).append("<div class='validationError'>" + message.Error + "</div>");
			}
			
			// Give other objects the possibility to augment this function
			this.manageValidationResult();

		}		
	};
	
	/**
	 * Use to augment the processing of the validation result. 
	 * Is only called if the validation msg was for me (this UI control).
	 */
	this.manageValidationResult = function(message){
		
	}
	
	/**
	 * Here we can handle code that must be executed if the model data was updated.
	 * This is normally overwritten by object augmentation to support complex UI controls
	 * @param {Object} message {"Reciever" : nameInModel, "Error":{string}}
	 */
	this.manageUpdateResult = function(message){	
		// Check if the message is for me
		if(message.Reciever == nameInModel){
			// Do stuff if needed
		}		
	};
	
	// Initialize this object and make sure the context is kept in the inner function by using call
	initialize.call(this);	
	this.update = function(){}
	

	  
 }
