// Source: http://dotnetslackers.com/columns/ajax/AspNetAjaxExceptionLogging.aspx

/* The listener base class */

Type.registerNamespace('Kazi.Logging');

Kazi.Logging.BaseTraceListener = function(lineSeparator) {
	this._lineSeparator = '\n';

	if (lineSeparator) {
		this._lineSeparator = lineSeparator;
	}
}

Kazi.Logging.BaseTraceListener.prototype = {

	get_lineSeparator : function() {
		if (arguments.length !== 0) throw Error.parameterCount();

		return this._lineSeparator;
	},

	dispose : function() {
	},

	publishException : function(errorCode, exceptionInfo, environmentInfo) {
		throw Error.notImplemented();
	},

	formatException : function(errorCode, exceptionInfo) {
		var lineSeparator = this.get_lineSeparator();
		var errorInfo = new Sys.StringBuilder();

		errorInfo.append('Error: ' + errorCode + lineSeparator);

		if (exceptionInfo.message !== null && exceptionInfo.message !== '')
			errorInfo.append('Message: ' + exceptionInfo.message + lineSeparator);

		if (exceptionInfo.type !== null && exceptionInfo.type !== '')
			errorInfo.append('Type: ' + exceptionInfo.type + lineSeparator);

		if (exceptionInfo.stackTrace !== null && exceptionInfo.stackTrace !== '')
			errorInfo.append('Stack Trace: ' + exceptionInfo.stackTrace + lineSeparator);

		if (exceptionInfo.httpStatusCode !== null && exceptionInfo.httpStatusCode !== '')
			errorInfo.append('HttpStatusCode: ' + exceptionInfo.httpStatusCode + lineSeparator);

		if (exceptionInfo.lineNumber !== null && exceptionInfo.lineNumber !== '')
			errorInfo.append('LineNumber: ' + exceptionInfo.lineNumber + lineSeparator);

		if (exceptionInfo.url !== null && exceptionInfo.url !== '')
			errorInfo.append('Error Url: ' + exceptionInfo.url + lineSeparator);

		return errorInfo.toString();
	}
}

Kazi.Logging.BaseTraceListener.registerClass('Kazi.Logging.BaseTraceListener', null, Sys.IDisposable);


/* Trace listener class - send to a textarea id='TraceConsole' */

Kazi.Logging.SysDebugTraceListener = function(lineSeparator) {
	Kazi.Logging.SysDebugTraceListener.initializeBase(this, [lineSeparator]);
}

Kazi.Logging.SysDebugTraceListener.prototype = {

	dispose : function() {
		Kazi.Logging.SysDebugTraceListener.callBaseMethod(this, 'dispose');
	},

	publishException : function(errorCode, exceptionInfo, environmentInfo) {
		// Calling the base class method
		var errorInfo = Kazi.Logging.SysDebugTraceListener.callBaseMethod(this, 'formatException', [errorCode, exceptionInfo]);
		Sys.Debug.traceDump(errorInfo);
	}
}

Kazi.Logging.SysDebugTraceListener.registerClass('Kazi.Logging.SysDebugTraceListener', Kazi.Logging.BaseTraceListener);


/* Alert listener class - alert the error message */

Kazi.Logging.AlertTraceListener = function(lineSeparator) {
	Kazi.Logging.AlertTraceListener.initializeBase(this, [lineSeparator]);
}

Kazi.Logging.AlertTraceListener.prototype = {

	dispose : function() {
		Kazi.Logging.AlertTraceListener.callBaseMethod(this, 'dispose');
	},

	publishException : function(errorCode, exceptionInfo, environmentInfo) {
		// Calling the base class method
		var errorInfo = 'Error occured';
		if (errorCode !== null && errorCode !== '')
			errorInfo += ':\n' + errorCode;
		errorInfo += '\nTry again or email support@tradesmenbids.com for assistance';
		alert(errorInfo);
	}
}

Kazi.Logging.AlertTraceListener.registerClass('Kazi.Logging.AlertTraceListener', Kazi.Logging.BaseTraceListener);


/* Div listener class - send results to a div, newest to the top */

Kazi.Logging.DivTraceListener = function(lineSeparator, targetElement) {
	if (typeof(targetElement) == 'string') {
		this._element = $get(targetElement);
	} else {
		this._element = targetElement;
	}

	Kazi.Logging.DivTraceListener.initializeBase(this, [lineSeparator]);
}

Kazi.Logging.DivTraceListener.prototype = {

	dispose : function() {
		this._element = null;
		Kazi.Logging.DivTraceListener.callBaseMethod(this, 'dispose');
	},

	publishException : function(errorCode, exceptionInfo, environmentInfo) {
		// Calling the base class methods
		var errorInfo = Kazi.Logging.DivTraceListener.callBaseMethod(this, 'formatException', [errorCode, exceptionInfo]);
		var lineSeparator = Kazi.Logging.DivTraceListener.callBaseMethod(this, 'get_lineSeparator');

		if (this._element.innerHTML.length > 0) {
			// Put some extra breaks
			this._element.innerHTML = lineSeparator + lineSeparator + this._element.innerHTML;
		}

		this._element.innerHTML = errorInfo + this._element.innerHTML;
	}
}

Kazi.Logging.DivTraceListener.registerClass('Kazi.Logging.DivTraceListener', Kazi.Logging.BaseTraceListener);


/* Web Service listener class - send results to a service */

Kazi.Logging.WebServiceTraceListener = function(lineSeparator) {
	Kazi.Logging.WebServiceTraceListener.initializeBase(this, [lineSeparator]);
}

Kazi.Logging.WebServiceTraceListener.prototype = {

	dispose : function() {
		Kazi.Logging.WebServiceTraceListener.callBaseMethod(this, 'dispose');
	},

	publishException : function(errorCode, exceptionInfo, environmentInfo) {
		// Calling the base class method
		var errorInfo = Kazi.Logging.WebServiceTraceListener.callBaseMethod(this, 'formatException', [errorCode, exceptionInfo]);
		var lineSeparator = Kazi.Logging.WebServiceTraceListener.callBaseMethod(this, 'get_lineSeparator');

		var logContent = new Sys.StringBuilder();
		logContent.append(errorInfo);

		if (environmentInfo.url !== null && environmentInfo.url !== '')
			logContent.append('Env. Url: ' + environmentInfo.url + lineSeparator);

		if (environmentInfo.referrer !== null && environmentInfo.referrer !== '')
			logContent.append('Referrer: ' + environmentInfo.referrer + lineSeparator);

		if (environmentInfo.scripts !== null && environmentInfo.scripts.length > 0)
			logContent.append('Scripts Loaded: ' + environmentInfo.scripts.join(', ') + lineSeparator);

		var info = logContent.toString();

		// Invoking the WebService - matches the service to be called
		TradesmenBids.WebGui.Logging.LogException( info, Kazi.Logging.WebServiceTraceListener.web_success, Kazi.Logging.WebServiceTraceListener.web_failed );
	},
	web_success : function(result) {
		// We wrote the error to the service, and this is the problem id
	},
	web_failed : function(e) {
		// We couldn't write the error to the service
		// Try again? Tell user? Die?
	}

}

Kazi.Logging.WebServiceTraceListener.registerClass('Kazi.Logging.WebServiceTraceListener', Kazi.Logging.BaseTraceListener);


/* The singleton manager that calls all the trace listeners */

Kazi.Logging.ExceptionManager = function() {
	this._handleErrors = true;
	this._listeners = new Array();
	Kazi.Logging.ExceptionManager.initializeBase(this);
}

Kazi.Logging.ExceptionManager.prototype = {

	initialize : function() {
		Sys.Application.add_load(Function.createDelegate(this, this._appOnLoad));  
		Kazi.Logging.ExceptionManager.callBaseMethod(this, 'initialize');
	},

	dispose : function() {
		if (this._listeners && this._listeners.length > 0) {
			for(var i = 0; i < this._listeners.length; i++) {
				this._listeners[i].dispose();
			}
		}

		delete this._listeners;

		window.onerror = null;
		Sys.WebForms.PageRequestManager.getInstance().remove_endRequest(Function.createDelegate(this, this._endRequest));

		Kazi.Logging.ExceptionManager.callBaseMethod(this, 'dispose');
	},

	_appOnLoad : function() {
		if ( this.get_handleErrors() ) {
			Sys.WebForms.PageRequestManager.getInstance().add_endRequest(Function.createDelegate(this, this._endRequest));
			window.onerror = Function.createDelegate(this, this._windowError);
		}
	},

	_endRequest : function(sender, args) {
		var e = args.get_error();

		if (e != null && this._handleErrors) {
			this.publishException('UpdatePanel/Request exception', e);
			args.set_errorHandled(true);
		}
	},

	_windowError : function(message, url, lineNumber) {
		// Since our framework only understand exception we have to convert it
		var e = Error.create(message, {description:message, name:'UnhandledError', lineNumber:lineNumber, url:url});
		this.publishException('window.onerror', e);

		return true;
	},

	addListener : function(listener) {
		var e = Function._validateParams(arguments, [{name: 'listener', type: Kazi.Logging.BaseTraceListener}]);
		if (e) throw e;

		Array.add(this._listeners, listener);
	},

	removeListener : function(listener) {
		var e = Function._validateParams(arguments, [{name: 'listener', type: Kazi.Logging.BaseTraceListener}]);
		if (e) throw e;

		listener.dispose();

		Array.remove(this._listeners, listener);
	},

	get_handleErrors : function() {
		return this._handleErrors;
	},

	set_handleErrors : function(value) {
		var e1 = Function._validateParams(arguments, [{name: 'value', type: Boolean}]);
		if ( e1 ) {
			throw e1;
		}

		if ( value !== this._handleErrors ) {
			// Wire or unwire the handlers
			if ( value ) {
				window.onerror = Function.createDelegate(this, this._windowError);
			} else {
				window.onerror = null;
			}
		}
		this._handleErrors = value;
	},

	publishException : function(errorCode, exception) {

		var e1 = Function._validateParams(arguments, [{name: 'errorCode', type: String}, {name: 'exception', type: Error}]);
		var e2 = Function._validateParams(arguments, [{name: 'errorCode', type: String}, {name: 'exception', type: Sys.Net.WebServiceError}]);

		if ((e1) && (e2)) {
			throw e1;
		}

		if (this._listeners && this._listeners.length > 0) {
			var environmentInfo = this._getEnvironmentInfo();
			var exceptionInfo = this._getExceptionInfo(exception);

			for(var i = 0; i < this._listeners.length; i++) {
				this._listeners[i].publishException(errorCode, exceptionInfo, environmentInfo);
			}
		}

		if ( this._handleErrors === false ) {
			throw exception;
		}
	},

	_getEnvironmentInfo : function() {
		var scriptTags = document.getElementsByTagName('script');
		var scripts = new Array(); 

		if (scriptTags) {
			for(var i = 0; i < scriptTags.length; i++) {
				var scriptTag = scriptTags[i];

				if (typeof(scriptTag.src) != 'undefined') {
					if (scriptTag.src.length > 0) {
						var tag = scriptTag.src;
						if ( typeof(scriptTag.readyState) != 'undefined' ) {
							tag += ' : ' + scriptTag.readyState; // IE
						}
						Array.add(scripts, tag);
						tag = null;
					}
				}
			}
		}

		var url = window.location.href;

		var referrer = '';

		if (document.referrer) {
			referrer = document.referrer;
		}

		return {url:url, referrer:referrer, scripts:scripts};
	},

	_getExceptionInfo : function(exception) {
		var message = '';
		var type = '';
		var stackTrace = '';
		var httpStatusCode = '';
		var lineNumber = '';
		var url = '';

		if (typeof(exception.get_exceptionType) != 'undefined') {
			// asp.net ajax exception
			message = exception.get_message();
			type = exception.get_exceptionType();
			stackTrace = exception.get_stackTrace();
		} else {
			// dom exception
			message = exception.message;
			type = exception.name;
			if ( exception.stack != null ) {
				stackTrace = exception.stack; // Mozilla
			} else {
				stackTrace = exception.description; // IE
			}
		}

		// Only Update Panel has this field
		if (typeof(exception.httpStatusCode) != 'undefined') {
			httpStatusCode = exception.httpStatusCode;
		}

		// Only Unhandled error have the following two field

		if (typeof(exception.lineNumber) != 'undefined') {
			lineNumber = exception.lineNumber;
		}

		if (typeof(exception.url) != 'undefined') {
			url = exception.url;
		}

		return {message:message, type:type, stackTrace:stackTrace, httpStatusCode:httpStatusCode, lineNumber:lineNumber, url:url};
	}

}

Kazi.Logging.ExceptionManager.registerClass('Kazi.Logging.ExceptionManager', Sys.Component);

// Making the ExceptionManager a Singleton Class, but unfortunately
// there is no way to restrict its constructor to be called.

Kazi.Logging.ExceptionManager._staticInstance = $create(Kazi.Logging.ExceptionManager, {'id':'exceptionManager'});

Kazi.Logging.ExceptionManager.getInstance = function() {
	return Kazi.Logging.ExceptionManager._staticInstance;
}

if (typeof(Sys) != 'undefined') {
	Sys.Application.notifyScriptLoaded();
}
