/*global YAHOO */
/**
 * The IS24 global namespace object
 * @class IS24
 * @static
 */
if (typeof IS24 == "undefined") {
    var IS24 = {};
}

/**
 * Returns the namespace specified and creates it if it doesn't exist
 *
 * @method namespace
 * @static
 * @param  {String*} arguments 1-n namespaces to create
 * @return {Object}  A reference to the last namespace object created
 */
IS24.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; ++i) {
        d=a[i].split(".");
        o=IS24;

        // IS24 is implied, so it is ignored if it is included
        for (j=(d[0] == "IS24") ? 1 : 0; j<d.length; ++j) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }

    return o;
};

IS24.namespace("core");

IS24.core.Class = {
	create: function() {
		return function() {
		    this.init.apply(this, arguments);
		}
	}
};

IS24.namespace("controlRenderer");

IS24.controlRenderer.SelectBoxRenderer= IS24.core.Class.create();

IS24.controlRenderer.SelectBoxRenderer.prototype = {

	init: function(args) {
		this.attachObjectClassRegexp = /\battachobject-([^\b]+)\b/;
		this.options=[];
		this.selectedOptionCssClassName = args.selectedOptionCssClassName;
		this.selectAllOptionCssClassName = args.selectAllOptionCssClassName;
		this.optionCssClassName = args.optionCssClassName;
		this.onChange = args.onChange;
		this.attachObjects(args.element,this);
		this.boxElement=args.element;
		this.pressedKeys = "";

		YAHOO.util.Event.addListener(this.boxElement, "keydown",  this.onKeyPressed,this, true);
		YAHOO.util.Event.addListener(this.boxElement, "focus",  this.onFocusEvent,this, true);
		YAHOO.util.Event.addListener(this.boxElement, "blur",  this.onBlurEvent,this, true);

	},

	onFocusEvent: function(event) {

		if (this.options.length>0) {
			this.options[this.getIndexOfHighlightedOption()].highlight(true,false);
		}

	},

	onBlurEvent: function(event) {

		if (this.options.length>0) {
			this.options[this.getIndexOfHighlightedOption()].hideHighlighting();
		}

	},

	onKeyPressed: function(event,object) {

		var keyCode = YAHOO.util.Event.getCharCode(event);

		if (this.options.length == 0 || event.altKey ==true || event.ctrlKey == true || keyCode == 9 || keyCode == 16) {
			return;
		}

		if (keyCode == 36) {
			return this.onHomeKeyPressed(event);
		}

		if (keyCode == 35) {
			return this.onEndKeyPressed(event);
		}

		if (keyCode == 34 || keyCode == 33 ) {
			return this.onPagingKeyPressed(event);
		}

		return this.findAsYouType(event);

	},

	onHomeKeyPressed: function(event) {
		this.options[0].highlight(true,true);
		YAHOO.util.Event.preventDefault(event);
	},

	onEndKeyPressed: function(event) {
		this.options[this.options.length -1].highlight(true,true);
		YAHOO.util.Event.preventDefault(event);
	},

	onPagingKeyPressed: function(event) {

		var i;
		var keyCode = YAHOO.util.Event.getCharCode(event);

		var indexOfHighlightedOption = this.getIndexOfHighlightedOption();
		var direction;
		var defaultHighlightedOption;
		var scrollToCheck;

		if (keyCode == 34) {
			direction = 1;
			defaultHighlightedOption = this.options.length - 1;
			scrollToCheck = function(highlightedOption, optionToCheck) {
				return !optionToCheck.isVisible();
			}
		}

		if (keyCode == 33) {
			direction = -1;
			defaultHighlightedOption = 0;
			scrollToCheck = function(highlightedOption, optionToCheck) {
				return !highlightedOption.isVisibleIfOtherOptionInFocus(optionToCheck);
			}
		}

		var hit = false;
		for (i=indexOfHighlightedOption; i< this.options.length && i >= 0; i = i + direction) {
			if (scrollToCheck.call(this,this.options[indexOfHighlightedOption],this.options[i])) {
				this.options[i].highlight(true,true);
				hit = true;
				break;
			}
		}

		if (!hit) {
			this.options[defaultHighlightedOption].highlight(true,true);
		}
		YAHOO.util.Event.preventDefault(event);
	},

	getIndexOfHighlightedOption: function() {
		var i;

		for (i=0; i< this.options.length; i++) {
			if (this.options[i].highlighted) {
				return i;
			}
		}

		return 0;
	},

	/**
	 * Die Browser keycodes werden in US Keyboard Chars umgewandelt (ausser Y und Z). Also aus U-Umlaut wird ein ";".
	 * Deswegen versuchen wir hier die wichtigsten deutschen Buchstaben zu mappen. Das ist zwar
	 * schrecklich, aber besser als nichts. Wir geben einen leer String zurueck, wenn wir kein Mapping durchfuehren konnten.
	 */
	charFromGermanPCKeyCode: function(keyCode) {

		var usChar = String.fromCharCode(keyCode);

		//NumPad
		if (keyCode >= 96 && keyCode <= 105) {
			return "" + (keyCode - 96);
		}

		// Uuml
		if (keyCode == 59 || keyCode == 186) {
			return String.fromCharCode(252);
		}

		// Ouml
		if (keyCode == 192) {
			return String.fromCharCode(246);
		}

		// Auml
		if (keyCode == 222) {
			return String.fromCharCode(228);
		}

		// Szlig
		if (keyCode == 219) {
			return String.fromCharCode(223);
		}

		// A-Z 0-9
		if (usChar.match(/[\w\-]/)) {
			return usChar;
		}

		return "";

	},

	findAsYouType: function(event) {

		var i;
		var keyCode = YAHOO.util.Event.getCharCode(event);
		var matched = false;

		var deChar = this.charFromGermanPCKeyCode(keyCode);

		for (i=0; i< this.options.length; i++) {
			if (this.options[i].getText().toLowerCase().indexOf((this.pressedKeys + deChar).toLowerCase()) == 0) {

				this.options[i].highlight(true,true);
				matched = true;
				break;
			}
		}

		if (matched) {
			this.pressedKeys += String.fromCharCode(keyCode);
			var instance = this;

			if (this.findAsYouTypeTimer != null) {
				window.clearTimeout(this.findAsYouTypeTimer);
			}

			this.findAsYouTypeTimer = window.setTimeout(function() {
				instance.pressedKeys = "";
			},1000);
		} else {
			this.pressedKeys = "";
			if (this.findAsYouTypeTimer != null) {
				window.clearTimeout(this.findAsYouTypeTimer);
			}
		}

		// Unterdruecken von find as you type bei "normalen" Tastendruecken
		// unterbinden.
		if (deChar) {
			YAHOO.util.Event.preventDefault(event);
		}
	},

	attachObjects: function(node,attachTo) {
		var i;

		if(node.nodeType == 1) {

			var hits=this.attachObjectClassRegexp.exec(node.className);
		    if(hits) {

				var newAttachTo = attachTo[hits[1]](node);

				if (newAttachTo) {
					attachTo=newAttachTo;
				}

			}
		}

		if (node.hasChildNodes()) {
			for(i=0; i<node.childNodes.length;i++) {
				this.attachObjects(node.childNodes[i],attachTo);

			}
		}

	},

	setSelectAllOption: function(node) {
			this.selectAllOption = new IS24.controlRenderer.Option(this,node,new IS24.controlRenderer.SelectAllOptionOnChangeDelegate(this));
			this.options.push(this.selectAllOption);
			return this.selectAllOption;
		  },

	addOption: function(node) {
			var newOption = new IS24.controlRenderer.Option(this,node, new IS24.controlRenderer.OptionOnChangeDelegate(this));
			this.options.push(newOption);
			return newOption;
		  },

	createResetFunction: function() {
		var that = this;

		return function() {
			var i;
			for (i=0;i<that.options.length;i++) {
				that.options[i].reset();
			}
		}
	  },

	deselectAllOptions: function(selectAllOption) {
		var i;
		for (i=0;i<this.options.length;i++) {
			if (this.options[i] != this.selectAllOption) {
				this.options[i].setCheckBoxToChecked(false);
				this.options[i].dehighlight();
			}
		}
		this.selectAllOption.highlight(true,true);
	},

	countSelectedOptions: function() {
		var i, counter = 0;
		for (i=0;i<this.options.length;i++) {
			if (this.options[i].isSelected()) {
				counter ++;
			}
		}
		return counter;
	},

	countHighlightedOptions: function() {
		var i, counter = 0;
		for (i=0;i<this.options.length;i++) {
			if (this.options[i].highlighted) {
				counter ++;
			}
		}
		return counter;
	}
};

IS24.controlRenderer.SelectAllOptionOnChangeDelegate = IS24.core.Class.create();

IS24.controlRenderer.SelectAllOptionOnChangeDelegate.prototype = {

	init: function(selectBox,option) {
		this.selectBox = selectBox;
	},

	doOnSelect: function() {
		this.selectBox.deselectAllOptions();
	},

	doOnDeselect: function() {
		this.selectBox.selectAllOption.select(true);
	}
};

IS24.controlRenderer.OptionOnChangeDelegate = IS24.core.Class.create();

IS24.controlRenderer.OptionOnChangeDelegate.prototype = {

	init: function(selectBox) {
		this.selectBox = selectBox;
	},

	doOnSelect: function() {
		if(this.selectBox.selectAllOption && this.selectBox.selectAllOption.isSelected() ) {
			this.selectBox.selectAllOption.deselect(false);
		}
	},

	doOnDeselect: function() {
		if(this.selectBox.selectAllOption && this.selectBox.countSelectedOptions() == 0 ) {
			this.selectBox.selectAllOption.select(false);
		}
	}
};


IS24.controlRenderer.Option  = IS24.core.Class.create();

IS24.controlRenderer.Option.prototype = {

	init: function(selectBox, divNode, onChangeDelegate) {
		this.selectBox = selectBox;
		this.divNode = divNode;
		this.onChangeDelegate = onChangeDelegate;
		this.properties = [];
		this.highlighted = false;
		this.text = null;
		this.initialState = false;
	},

	getText: function() {
		if (this.properties && this.properties.length > 1) {
			return this.properties[0];
		} else {

			if (this.text == null) {
				// RTrim
				this.text = this.extractTextFromNode(this.divNode,"").replace( /^\s+/g, "" );
			}


			return this.text;
		}
	},

	extractTextFromNode: function(node, text) {

		var i;
		// 1== Element Node
		// 2 == Text Node
		if (node.nodeType == 3) {
			return text + node.nodeValue;
		} else if (node.nodeType == 1) {

			var childs = node.childNodes;
			var textFromChilds = "";

			for (i=0;i<childs.length;i++) {
				textFromChilds = textFromChilds + (this.extractTextFromNode(childs[i], text));
			}

			return textFromChilds;
		}
	},

	addProperty: function(node) {
		if (node.hasChildNodes()) {
			text = node.firstChild.nodeValue;
			this.properties[this.properties.length]=text;
		}
	},

	setCheckBoxToChecked: function(checked) {
		// das feuern von onclick events beim aendern des Werts unterbinden
		var savedOnclick = this.checkBox.oncklick;
		this.checkBox.oncklick = function() {};
		this.checkBox.checked = checked;
		this.checkBox.oncklick = savedOnclick;
	},

	onChange: function(event) {

		if (!event) var event = window.event;
		event.cancelBubble = true;

		if (event.stopPropagation) event.stopPropagation();

		if (this.checkBox.checked) {
			this.select(true);
			this.onChangeDelegate.doOnSelect(this);
		} else {
			this.deselect(true);
			this.onChangeDelegate.doOnDeselect(this);
		}
		eval(this.selectBox.onChange);
	},

	setCheckBox: function(node) {
		this.checkBox = node;
		this.initialState = node.checked;

		var instance = this;
		node.onclick=function(e){instance.onChange(e)};
	},

	reset: function() {
		if (this.initialState) {
			this.select(true);
		} else {
			this.deselect(false);
		}
	},

	deselect: function(focus) {
		if (this.checkBox.checked) {
			this.setCheckBoxToChecked(false);
		}

		if (focus) {
			this.highlight(true,true);
		} else {
			this.dehighlight();
		}
	},

	hideHighlighting: function() {
		this.resetCss();

		if (this.checkBox.checked) {
			this.divNode.className = this.divNode.className + " " + this.selectBox.selectedOptionCssClassName;
		}
	},

	/*
	 * Der Eintrag wird nicht nur hervorgehoben, sondern auch in den sichtbaren Bereich gescrollt, wenn erforderlich.
	 */
	highlight: function(focus,scrollTo) {

		var i;
		this.resetCss();

		if (focus) {

			for (i=0; i< this.selectBox.options.length; i++) {
				this.selectBox.options[i].dehighlight();
			}

			this.highlighted = true;

			if (this.checkBox.checked) {
				this.divNode.className = this.divNode.className + " " + this.selectBox.selectedOptionCssClassName;
			}

		} else {
			this.divNode.className = this.divNode.className + " " + this.selectBox.selectedOptionCssClassName;
		}

		if (scrollTo) {
			var firstOptionNodePosition = YAHOO.util.Dom.getRegion(this.selectBox.options[0].divNode);

			var optionPosition = YAHOO.util.Dom.getRegion(this.divNode);

			var scrollToPosition = optionPosition.top - firstOptionNodePosition.top;

			if (!this.isVisible()) {
				this.selectBox.boxElement.scrollTop = scrollToPosition;
			}
		}

	},

	isVisibleIfOtherOptionInFocus: function(theOtherOption) {
		var selectBoxPosition= YAHOO.util.Dom.getRegion(this.selectBox.boxElement);
		var selectBoxHeight = selectBoxPosition.bottom - selectBoxPosition.top;

		var optionPosition = YAHOO.util.Dom.getRegion(this.divNode);
		var otherOptionPosition = YAHOO.util.Dom.getRegion(theOtherOption.divNode);

		return Math.abs(optionPosition.top - otherOptionPosition.top) <= selectBoxHeight;
	},

	isVisible: function() {
		var selectBoxPosition= YAHOO.util.Dom.getRegion(this.selectBox.boxElement);
		var selectBoxHeight = selectBoxPosition.bottom - selectBoxPosition.top;

		var firstOptionNodePosition = YAHOO.util.Dom.getRegion(this.selectBox.options[0].divNode);

		var optionPosition = YAHOO.util.Dom.getRegion(this.divNode);

		var currentScrollPosition = this.selectBox.boxElement.scrollTop;
		var scrollToPosition = optionPosition.top - firstOptionNodePosition.top;
		var optionHeight = optionPosition.bottom - optionPosition.top;

		return 	!(scrollToPosition <  currentScrollPosition || scrollToPosition + optionHeight > (currentScrollPosition + selectBoxHeight));
	},

	dehighlight: function() {
		this.highlighted = false;

		this.resetCss();

		if (this.isSelected()) {
			this.divNode.className = this.divNode.className + " " + this.selectBox.selectedOptionCssClassName;
		}
	},

	select: function(focus) {
		if (! this.checkBox.checked) {
			this.setCheckBoxToChecked(true);
		}
		this.highlight(focus,true);
	},

	isSelected: function() {
		return this.checkBox.checked;
	},

	resetCss: function() {
		if (this == this.selectBox.selectAllOption) {
			this.divNode.className = this.selectBox.selectAllOptionCssClassName;
		} else {
			this.divNode.className = this.selectBox.optionCssClassName;
		}
	}
};
