PastelBaseView.prototype.constructor = PastelBaseView;

/**
 * Get the value of an element. 
 * Works for input, textarea, select, and html elements.
 */
PastelBaseView.prototype._getValue = function($element, ignoreDefaultValue) {
	if($element !== undefined) {
		if (ignoreDefaultValue == undefined) {
			ignoreDefaultValue = false;
		}
		
		var defaultValue = $element.data("defaultValue"); 
		
		var value = "";
		if($element.is("input") || $element.is("select") || $element.is("textarea")) {
			value = $.trim($element.val());
		} else {
			value = $.trim($element.html());
		}
 
		if (!ignoreDefaultValue && value == defaultValue) {
			return "";
		}
		
		return value;		
	}
	return "";
};

/**
 * Set the value of an element. 
 * Works for input, textarea, select, and html elements.
 */
PastelBaseView.prototype._setValue = function($element, value) {	
	if($element !== undefined) {
		if($element.is("input") || $element.is("textarea")) {
			// decode html entities
			value = $.htmlEntitiesDecode(value);
			// set value
			$element.val(value);
		} else if($element.is("select")) {
			var $options = $("option", $element);
			$options.each(function(i) {
				if($(this).val() == value) {
					$(this).attr("selected", "selected");
				} else {
					$(this).removeAttr("selected");	
				}
			});
		} else {
			$element.html(value);
		}
	}
};

/**
 * Show animated notification with color change on a jQuery object
 * @param {jQuery} $object
 */
PastelBaseView.prototype.colorNotification = function($object) {
	bgColor = $object.css("background-color");
	opacity = $object.css("opacity");
	$object.animate({backgroundColor: ["#fabf10", "easeInQuint"], 
					 opacity: [0.8, "easeInQuint"]}, 
					 200, 
					 function() {
						 $object.animate({
							 backgroundColor: [bgColor, "easeOutQuint"],
							 opacity: [opacity, "easeOutQuint"]},
							 1300,
							 function() {
								 $object.removeAttr("style");
							 }
						 );
					});
};

PastelBaseView.prototype.fitTextAndAddTooltip = function($element, maxLength, dataAttrName, options) {
	this.hoverTooltip.remove($element);
	var text = $.trim($element.html());
	if (text.length > maxLength) {
		$element.html(text.substr(0, maxLength - 1) + "...");
		$element.data(dataAttrName, text);

		if (options === undefined) {
			options = {};
		}
		
		var topOffset = options.topOffset;
		if (topOffset === undefined) {
			topOffset = 0;
		}
		
		var leftOffset = options.leftOffset;
		if (leftOffset === undefined) {
			leftOffset = 0;
		}
		this.hoverTooltip.add($element, {				
			message: text,
			topOffset: topOffset,
			leftOffset: leftOffset
		});
	}
};

/**
 * @constructor
 * @param selector
 */
function PastelBaseView(selector) {
  
	/**
	 * @type jQuery
	 */
	this.container = null;
	
	/**
	 * @type jQuery
	 */
	this.wrapper = null;
	
	/**
	 * @type PastelValidator
	 */
	this.validator = null;

	/**
	 * @type PastelHoverTooltip
	 */	
	this.hoverTooltip = null;
	
	/**
	 * Set and initialize the view container and wrapper container
	 * @param viewSelector - css selector
	 * @param wrapperSelector - css selector
	 */
	this.setContainerSelectors = function(viewSelector, wrapperSelector) {
		if (viewSelector == wrapperSelector) {
			this.container = $(viewSelector);
			this.wrapper = this.container;
		} else {
			this.wrapper = $(wrapperSelector);
			this.container = $(viewSelector, this.wrapper);
		}
	};


	// Effect Functions ===========================================================================
	this._showEffectFunc = null;
	this._hideEffectFunc = null;
	
	/**
	 * Set show effect description
	 * @param {Object} effectDesc
	 */
	this.setShowEffectDesc = function(effectDesc) {
		this._showEffectFunc = this._getEffectFunction(effectDesc);
	};

	/**
	 * Set hide effect description
	 * @param {Object} effectDesc
	 */
	this.setHideEffectDesc = function(effectDesc) {
		this._hideEffectFunc = this._getEffectFunction(effectDesc);
	}; 
 
	/**
	 * Determine the effect function and return it for later execution
	 * @param {Object} effectDesc
	 * @return Function
	 * @type Function
	 */
	this._getEffectFunction = function (effectDesc) {
		var viewEffectFunction = null;
		var effectName = effectDesc.name;
		// is the function a css change
		if (effectName == "css") {
			viewEffectFunction = $.proxy(function(callback){
				var opts = effectDesc.opts;
				this.container.css(opts.key, opts.val);
				PastelValidator.removeAllMessages();
				if ($.isFunction(callback)) {
					callback();
				}
			}, this);
		} else {
			var effectFunction = $.proxy(this.container[effectName], this.container);
			var effectDuration = effectDesc.duration;
			viewEffectFunction = $.proxy(function(callback){
				PastelValidator.removeAllMessages();
				effectFunction(effectDuration, $.proxy(function(){
					if ($.isFunction(callback)) {
						callback();
					}
				}, this));
			}, this); 
		}			
		return viewEffectFunction;
	};
 
	// Toggle default
	var toggleInit = false;
	
	var toggleDefaultElements = [];
	
	this.resetToggleDefault = function() {
		toggleDefaultElements = [];
		toggleInit = false;
	};
	
	this.addToggleDefault = function($element) {
		toggleDefaultElements.push($element);
	};
	
	
	this.setupToggleDefault = function() {
		if (toggleInit) {
			return;
		}
		toggleInit = true;
 
		for (var i = 0; i < toggleDefaultElements.length; i++) {
			var $element = toggleDefaultElements[i];
			var defaultValue = $element.data("defaultValue");
			if (defaultValue == null || 
				defaultValue == undefined) {
				defaultValue = $element.attr("title");
				$element.removeAttr("title");
				$element.data("defaultValue", defaultValue);
			}
			
			if ($.trim($element.val()) == "") {
				$element.val(defaultValue);
			}
			
			$element.focus($.proxy(function(event) {
				var $target = $(event.target);
				var defaultValue = $target.data("defaultValue");
				var value = this._getValue($target, true);
				if (value == defaultValue) {
					this._setValue($target, "");			
				}
			}, this));
			
			$element.blur($.proxy(function(event) {
				var $target = $(event.target);
				var defaultValue = $target.data("defaultValue");
				var value = this._getValue($target, true);
				if ($.trim(value) == "") {
					this._setValue($target, defaultValue);						
				}
			}, this));
		}
	};
	
	// Change watch
	var watchElements = [];
	var watchJsonData = "_not_set_";
	
	this.resetWatchChange = function() {
		watchElements = [];
		watchJsonData = "_not_set_";
	};
	
	this.watchChange = function(value) {
		if (value !== undefined) {
			watchElements.push(value);
		} else {
			watchJsonData = "_pending_";
		}
	};
	
	this.watchSetup = function() {
		for (var i = 0; i < watchElements.length; i++) {
			var $element = watchElements[i];
			var value = $element.val();
			$element.data("watchChange", value);
		};
		
		if (watchJsonData == "_pending_") {
			watchJsonData = this.getJsonData();
		}
	};
	
	this.watchResetup = function() {
		if (watchJsonData != "_not_set_") {
			watchJsonData = "_pending_";
		}
		this.watchSetup();
	};
	
	this.isChanged = function() {
		if (!this.isVisible) {
			return false;
		}
		for (var i = 0; i < watchElements.length; i++) {
			var $element = watchElements[i];
			var value = $element.val();
			var watchValue = $element.data("watchChange");
			if (value != watchValue) {
				return true;
			}
		}
		
		if (watchJsonData != "_not_set_") {
			return watchJsonData != this.getJsonData();
		}
		
		return false;
	};
	
	this.restoreFromWatchJsonData = function() {
		if (watchJsonData != "_not_set_" && watchJsonData != "_pending_") {
			this.setJsonData(watchJsonData);
		}
	};
	
	
	/**
	 * Trigger validation 
	 * @param {String} group
	 * @return Boolean
	 */
	this.validate = function(group) {
		if (this.validator == null) {
			throw("Call initValidator first!");
		}
		return this.validator.validate(group);
	};
	
	/**
	 * Removes all validation messages and highlights 
	 */
	this.resetValidator = function() {
		if (this.validator == null) {
			throw("Call initValidator first!");
		}
		this.validator.reset();
	};
	
	/**
	 * Call this method to setup the validator. 
	 * It is usualy called in the init() of the view followed by validator 
	 * configuration
	 * @param {String} containerSelector - the container of the validation messages
	 * @param {String} messageId - the id which will be used to identify messages. If not 
	 * provided it defaults to the current class name.
	 */
	this.initValidator = function(containerSelector, messageId) {
		this.validator = new PastelValidator();
		this.validator.setContainerSelector(containerSelector);
		if (messageId == undefined) {
			messageId = /(\w+)\(/.exec(this.constructor.toString())[1];
		}
		this.validator.setMessageId(messageId);
	};

	/**
	 * Call this method to setup the hover tooltip. 
	 * It is usualy called in the init() of the view followed adding tooltips 
	 * @param {String} containerSelector - the container in which the tooltip is appended
	 */
	this.initHoverToolip = function(containerSelector) {
		this.hoverTooltip = new PastelHoverTooltip(containerSelector);
	};	
	
	/**
	 * Reset toggle default and change watch and call init()
	 */
	this.init = function() {
		this.resetToggleDefault();
		
		this.resetWatchChange();
		
		this.initHook();
	};
	
	/**
	 * Override this method to perform initializaton.
	 */
	this.initHook = function() {};
	
	/**
	 * @type Boolean
	 */
	this.isVisible = false;
 
	/**
	 * Shows the view container
	 */
	this.show = function(callback) {
		this.isVisible = true;
		
		var newCallback = $.proxy(function() {
			if ($.isFunction(callback))	{
				callback();
			}
		
			this.setupToggleDefault();
			this.watchSetup();
		}, this);
		
		this._showEffectFunc(newCallback);
	};
 
	/**
	 * Hides the view container
	 */
	this.hide = function(callback) {
		this.isVisible = false;
		this._hideEffectFunc(callback);
	};
	
	/**
	 * Get the form data.
	 * Implement this to collect data from the view.
	 * @return Object
	 */
	this.getData = function() { return {}; };
	
	/**
	 * @type String
	 * @return the data presented as JSON
	 */
	this.getJsonData = function() {
		var data = this.getData.apply(this, arguments);
		return $.toJSON(data); 
	};
	
	/**
	 * Set form data.
	 * @param {Object} data - the object which contains the data to be set
	 */
	this.setData = function(data) {};
	
	/**
	 * @param {String} jsonData - the data to be set presented as JSON
	 */
	this.setJsonData = function(jsonData) {
		var data = $.evalJSON(jsonData);
		this.setData(data);
	};
	
	/**
	 * Multipourpose method for getting/setting values of element and using jQuery methods
	 * jQuery supported methods: attr, css, vata
	 * Other: get, set, validator
	 */
	this._getset = function(selector, args, context) {
		if (context === undefined) {
			context = this.container;
		}
		
		var $elem = $(selector, context);
		var ret = null;
		var argLen = args.length;
 
		switch(argLen) {
			case 0:
				ret = $elem;
				break;
			case 1: 
				var opt = args[0];
				if (opt == "get") {
					ret = this._getValue($elem);
				} else {
					ret = false;
				}
				break;
			case 2:
				var opt = args[0];
				if (opt == "set") {
					var value = args[1];
					ret = true;
					this._setValue($elem, value);
				} else if (opt == "validator") {
					if (this.validator != null) {
						var option = args[1];

						if (!option) {
							this.validator.remove($elem);
						} else {
							this.validator.add($elem, option);
						}
						
						ret = true;
					} else {
						ret = false;
						throw("Call initValidator first!");
					}
				} else if (opt == "attr" || opt == "css" || opt == "data") {
					var key = args[1];
					ret = $.proxy($elem[opt], $elem)(key);
				} else {
					ret = false;
				}
				break;
			case 3:
				var opt = args[0];
				if (opt == "validator") {
					if (this.validator != null) {
						var option = args[1];
						var group = args[2];
						this.validator.add($elem, option, group);
						ret = true;
					} else {
						ret = false;
						throw("Call initValidator first!");
					}
				} else if (opt == "attr" || opt == "css" || opt == "data") {
					var key = args[1];
					var value = args[2];
					$.proxy($elem[opt], $elem)(key, value);
					ret = true;
				} else {
					ret = false;
				}
				break;
		} 
		return ret;
	};
}
