/**
 * Returns the type of the object oObject
 * @param {Object} oObject The object to get type type of
 */
function etypeof(oObject) {
	

	// Declare variables
	var strType = typeof(oObject);
	switch(strType) {
		default:
			return strType;
			break;
		case "Object":
		case "object":
			if(oObject.__type != undefined) {
				return oObject.__type;
			} else {
				return strType;
			}
			break;
	}
}

/**
 * Binds the function with a fixed context
 * @param {Object} context the object to function as the [this] variable in the scope of the function
 */
Function.prototype.Bind = function Function$Bind(context) {
	var _function = this;
	var args = Array.createFrom(arguments);
	var context = args.shift();
	return function() {
		_function.apply(context, args.concat(Array.createFrom(arguments)));
	}
}

Array.createFrom = function Array$createFrom(collection) {
	if (collection.toArray) {
		return collection.toArray();
	} else {
		var result = [];
		for (var i = 0; i < collection.length; i++)
			result[i] = collection[i];
		return result;
	}
}

/**
 * Inhert to passed object
 * @param {Object} subtype The type to inhert supertype
 * @param {Object} supertype The type to inherit from
 */
Object.inherits = function Object$inherits(subtype, supertype) {
	subtype.prototype = new supertype;
};

/**
 * Extend this object with the passed extensions
 * @param {Object} extensions An array containing the extensions. The key as the porperty name, the value als the actual property.
 */
Object.extend = function Object$extend(subject, extensions) {
	for(var property in extensions) {
		subject[property] = extensions[property];
	}
	return subject;
};

var UI = {};

UI.ClearForm = function UI$ClearForm(hElement) {
	this.Initialize(hElement);
}
UI.ClearForm.prototype.Initialize = function(hElement) {
	
	// Store variables
	this.hForm = hElement;
	
	// Initialize variables
	this.aFields = new Array();
	this.aValidateFunctions = new Array();
	this.aOnFocusFunctions = new Array();
	this.aOnBlurFunctions = new Array();
	this.aOnMouseOverFunctions = new Array();
	this.aOnMouseOutFunctions = new Array();
	this.aValidateRequests = new Array();
}
UI.ClearForm.prototype.AddField = function(id) {
	
	// Declare variables
	var hElement;
	
	// Get element
	hElement = document.getElementById(id);
	this.aFields[id] = hElement;
	
	// Hookup events
	hElement.onfocus = this.OnInputFocus.Bind(this, id);
	hElement.onblur = this.OnInputBlur.Bind(this, id);
	hElement.onchange = this.OnInputChanged.Bind(this, id);
	hElement.onmouseover = this.OnInputMouseOver.Bind(this, id);
	hElement.onmouseout = this.OnInputMouseOut.Bind(this, id);
}

UI.ClearForm.prototype.RegisterFunction = function(fieldId, type, fn) {
	switch(type) {
		case "onfocus":
			this.aOnFocusFunctions[fieldId] = fn;
			break;
		case "onmouseover":
			this.aOnMouseOverFunctions[fieldId] = fn;
			break;
		case "onblur":
			this.aOnBlurFunctions[fieldId] = fn;
			break;
		case "onmouseout":
			this.aOnMouseOutFunctions[fieldId] = fn;
			break;
		case "validate":
			this.aValidateFunctions[fieldId] = fn;
			break;
	}
}

UI.ClearForm.prototype.OnInputFocus = function(id) {
	if(this.aOnFocusFunctions[id])
		this.aOnFocusFunctions[id].call(this, id);
}

UI.ClearForm.prototype.OnInputMouseOver = function(id) {
	if(this.aOnMouseOverFunctions[id])
		this.aOnMouseOverFunctions[id].call(this, id);
}

UI.ClearForm.prototype.OnInputBlur = function(id) {
	if(this.aOnBlurFunctions[id])
		this.aOnBlurFunctions[id].call(this, id);
	else 
		return;
		
	if(this.aValidateFunctions[id])
		this.aValidateFunctions[id].call(this, id);
}

UI.ClearForm.prototype.OnInputMouseOut = function(id) {
	if(this.aOnMouseOverFunctions[id])
		this.aOnMouseOverFunctions[id].call(this, id);
	else
		return;
		
	if(this.aValidateFunctions[id])
		this.aValidateFunctions[id].call(this, id);
}

UI.ClearForm.prototype.OnInputChanged = function(id) {
	//this.aValidateFunctions[id].call(this, id);
}

UI.ClearForm.prototype.XmlValidate = function(id, url) {
	
	// Declare variables
	var oRequest, hElement;
	
	hElement = this.aFields[id];
	
	oRequest = new Net.Request(url, "post", "validate=" + escape(hElement.value), true);
	oRequest.OnComplete.Suscribe(this.OnXmlValidate, this, id);
	oRequest.MakeRequest();
	
	this.aValidateRequests[id] = oRequest;
}

UI.ClearForm.prototype.OnXmlValidate = function(id) {
	
	// Declare variables
	var oRequest, hElement;
	
	hElement = document.getElementById(id + ".tip");
	oRequest = this.aValidateRequests[id];
		
	if(!oRequest.ExtendedResult) {
		hElement.innerHTML = "<p class=\"icon iconError\">Unable to validate field</p>\n";
		return false;
	}
	
	switch(oRequest.ExtendedResult.result) {
		case false:
			hElement.innerHTML = "<p class=\"icon iconError\">" + oRequest.ExtendedResult.message + "</p>";
			break;
		case true:
			hElement.innerHTML = "<p class=\"icon iconAccept\">" + oRequest.ExtendedResult.message + "</p>";
			break;
	}
}

UI.ClearForm.prototype.hForm = null;

UI.ClearForm.prototype.aFields = null;
UI.ClearForm.prototype.aValidateFunctions = null;
UI.ClearForm.prototype.aOnFocusFunctions = null;
UI.ClearForm.prototype.aOnBlurFunctions = null;
UI.ClearForm.prototype.aOnMouseOverFunctions = null;
UI.ClearForm.prototype.aOnMouseOutFunctions = null;
UI.ClearForm.prototype.aValidateRequests = null;


var Net = {};
Net.XMLHttpRequest = function Net$XMLHttpRequest() {
	
	// Declare variables
	var oXmlHttpRequest;
	
	if (window.XMLHttpRequest) {
	
		// Mozilla firefox & IE 7
		try {
	    	oXmlHttpRequest = new XMLHttpRequest();
	    } catch(ex) {
	    	throw new Error("The XMLHttpRequest object is not available on this computer.");
	    }
	} else if (window.ActiveXObject) {
		
		// Microsoft IE <= 6
		try {
			oXmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
		} catch(ex) {
			try {
				oXmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
			} catch(ex) {
				throw new Error("The XMLHttpRequest object is not available on this computer.");
			}
		}
	}
	
	/*
	// Extend the xml htpp request
	Object.extend(oXmlHttpRequest, {
		__type: "Net.XMLHttpRequest"
	} );
	*/
	
	return oXmlHttpRequest;
};

Net.Request = function Net$Request(url, method, data, extended) {
	
	// Save settings
	this._url = url || "";
	this._method = method || "GET";
	this._data = data || null;
	this._extended = extended || false;
	this._async = true;
	this.ExtendedResult = null;
	
	// Error handling
	this.Error = false;
	this.Exceptions = [];
	
	// Events
	this.OnStateChanged = new Delegate();
	this.OnComplete = new Delegate();
};

Net.Request.prototype.MakeRequest = function Net$Request$MakeRequest() {
	
	// Get the xml http request
	this.hXMLHttpRequest = new Net.XMLHttpRequest();
	this.hXMLHttpRequest.open(this._method, this._url, this._async);
	this.hXMLHttpRequest.onreadystatechange = this._OnReadyStateChange.Bind(this);
	if(this._data !== null)
		this.hXMLHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	this.hXMLHttpRequest.send(this._data);
}

Net.Request.prototype._OnReadyStateChange = function Net$Request$_OnReadyStateChange() {
	
	this.OnStateChanged.Invoke();
	
	if(this.hXMLHttpRequest.readyState == 4) {
		this._ProcessResponse();
	}
}

Net.Request.prototype._ProcessResponse = function Net$Request$_ProcessResponse() {
	
	if(!this._extended) {
		
		// Notify the request is complete
		this.OnComplete.Invoke();
	} else {
		
		// Pre-process response
		if(this.hXMLHttpRequest.responseXML) {
			
			try {
				
				// Declare variables
				var hExceptions = this.hXMLHttpRequest.responseXML.getElementsByTagName("exception");
				var hResult = this.hXMLHttpRequest.responseXML.getElementsByTagName("result")[0];
				var hException, newException, resultType, requestResult;
				
				// Process remote exceptions (if any)
				if(hExceptions.length > 0) {
					this.Error = true;
					for(i = 0; i < hExceptions.length; i++) {
						hException = hExceptions[i];
						
						if(hException.hasChildNodes()) {
							newException = new Net.RemoteException(hException.firstChild.nodeValue)
							this.Exceptions.push(newException);
						}
					}
				}
				
				// Process the result
				resultType = hResult.getAttribute("type");
				switch(resultType) {
					case "int":
					
						// Return request result as an integer
						requestResult = parseInt(hResult.firstChild.nodeValue);
						break;
					case "bool":
					
						// Return request result as a boolean
						requestResult = hResult.firstChild.nodeValue;
						if(requestResult == "true") {
							requestResult = true;
						} else {
							requestResult = false;
						}
						break;
					default:
					case "string":
					
						// Return request result as a string
						requestResult = hResult.firstChild.nodeValue;
						break;
					case "object":
					
						// Return request result as an object
						eval("requestResult = " + hResult.firstChild.nodeValue);
						break;
					case "xml":
					
						// Return request result as a xml node (skip all text nodes on this branch)
						for(var z = 0; z < hResult.childNodes.length; z++) {
							if(hResult.childNodes[z].nodeType == 1)
								requestResult = hResult.childNodes[z];
						}
						
						break;
				}
				this.ExtendedResult = requestResult;
			}
			catch(ex)
			{
				this.Error = true;
				this.Exceptions.push(new Error("An error ocurred while processing the xml response from the server: " + ex.message));
			}
			
		} else {
			
			// Invalid response
			this.Error = true;
			this.Exceptions.push(new Error("The Net.Request object received an invalid response from the server (no xml data)."));
		}
		
		// Notify the request is complete
		this.OnComplete.Invoke();
	}
}

Net.Request.prototype.GetResponseText = function Net$Request$GetResponseText() {
	
	return this.hXMLHttpRequest.responseText;
}

Net.Request.prototype.Abort = function Net$Request$Abort() {
	
	this.hXMLHttpRequest.abort();
}

Net.RemoteException = function(message, innerException) {
	if(message !== undefined) {
		this.Message = message;
	}
	if(innerException !== undefined) {
		if(Exception.prototype.isPrototypeOf(innerException)) {
			this.InnerException = innerException;
		} else {
			throw new ArgumentException("The innerException must be of the type Exception");
		}
	}
}
Object.inherits(Net.RemoteException, Error);
Net.RemoteException.prototype.__type = "Net.RemoteError";

/**
 * Intializes a new instance of the Delegate object.
 */
function Delegate() {
	this.arrFunctions = [];
	this.arrContexts = [];
	this.arrArguments = [];
};

Delegate.prototype.__type = "Delegate";
/**
 * Invokes all the suscribed functions in the context and passes the passed arguments.
 * @param {Object} args Any number of arguments to be passed to the suscribed functions
 */
Delegate.prototype.Invoke = function Delegate$Invoke() {
	
	// Declare variables
	var hContext, hFunction, aArguments;
	
	// Iterate trough suscribed functions
	for(var i = 0; i < this.arrFunctions.length; i++) {
		hFunction = this.arrFunctions[i];
		hContext = this.arrContexts[i];
		aArguments = this.arrArguments[i];
		aArguments = aArguments.concat(Array.createFrom(arguments));
		
		// Call function if valid
		if(hFunction && hContext) {
			hFunction.apply(hContext, aArguments);
		}
	}
}

/**
 * Suscribes the passed function and context to this delegate
 * @param {Function} hFunction The function to suscribe to this delegate
 * @param {Object} hContext The context of the function to suscribe
 */
Delegate.prototype.Suscribe = function Delegate$Suscribe(hFunction, hContext) {

	// Validate types
	if(etypeof(hFunction) != "function" || !Object.prototype.isPrototypeOf(hContext)) {
		throw new ArgumentException("The hFunction argument must be of the type 'function' and the hContext argument must be of the type 'object'.");
	}
	
	aArguments = Array.createFrom(arguments);

	// Store the function and object pointer
	this.arrFunctions.push(aArguments.shift());
	this.arrContexts.push(aArguments.shift());
	this.arrArguments.push(aArguments);
}

/**
 * Unsuscribes the passed function and context from this delegate
 * @param {Function} hFunction The function to unsuscribe from this delegate
 * @param {Object} hContext The context of the function to unsuscribe
 */
Delegate.prototype.Unsuscribe = function Delegate$Unsuscribe(hFunction, hContext) {

	// Validate types
	if(etypeof(hFunction) != "function" || !Object.prototype.isPrototypeOf(hContext)) {
		throw new ArgumentException("The hFunction argument must be of the type 'function' and the hContext argument must be of the type 'object'.");
	}
	
	// Attempt to find the stored function and context
	for(i = 0; i < this.arrFunctions.length; i++) {
		if(this.arrFunctions[i] == hFunction && this.arrContexts[i] == hContext) {
		
			// Delete the values from arrays
			this.arrFunctions.splice(i, 1);
			this.arrContexts.splice(i, 1);
			this.arrArguments.splice(i, 1);
			break;
		}
	}
}


Error.prototype.__type = "Error";
Error.prototype._stackTrace = "";
Error.prototype.HandleByUser = function Error$HandleByUser() {
	
	core.ShowErrorDialog(this);
}

Error.stackTrace = function Error$stackTrace(startingPoint)
	{
		var stackTraceMessage = "Stack trace: \n";
		var nextCaller = startingPoint;
		while(nextCaller)
		{
			stackTraceMessage += Error.getSignature(nextCaller) + "\n";
			nextCaller = nextCaller.caller;
		}
		stackTraceMessage += "\n\n";
		
		return stackTraceMessage;
	}
	
Error.getSignature = function Error$getSignature(theFunction)
	{
		var signature = Error.getFunctionName(theFunction);
		signature += "(";
		for(var x=0; x<theFunction.arguments.length; x++)
		{
			// trim long arguments
			var nextArgument = theFunction.arguments[x];
			if(nextArgument.length > 30)
				nextArgument = nextArgument.substring(0, 30) + "...";
			
			// apend the next argument to the signature
			signature += "'" + nextArgument + "'"; 
			
			// comma seperator
			if(x < theFunction.arguments.length - 1)
				signature += ", ";
		}
		signature += ")";
		return signature;
	}
	
Error.getFunctionName = function Error$getFunctionName(theFunction)
	{
		// mozilla makes it easy. I love mozilla.
		if(theFunction.name)
		{
			return theFunction.name;
		}
		
		// try to parse the function name from the defintion
		var definition = theFunction.toString();
		var name = definition.substring(definition.indexOf('function') + 8,definition.indexOf('('));
		if(name)
			return name;

		// sometimes there won't be a function name 
		// like for dynamic functions
		return "anonymous";
	}
