/** Async server-side logger.
* @author Russ Tennant (russ@i2d.com)
* @require i2rd-util.js
*/
if (typeof log4jsdefined == 'undefined') {
i2rd.hasConsole = function() {
	return (typeof console != 'undefined' && typeof console.info != 'undefined');
};
log4jsdefined=true;
var log4js = {};
log4js.funcname = function (f) {
    // Update to find anonymous function by searching object properties - needs done in caller.
    // Update to include arguments for all functions to make truly anonymous functions easier to locate.
    var s, v = /function (\w*)/.exec(f.toString());
    if(v && v.length > 1){s=v[1];}
    if (!s || (s.length==0)){return "anonymous";}
    return s;
};
log4js.stacktrace = function () {
	try {
	    var ac,s = ""; 
	    for(ac=arguments.callee;!!ac;ac=ac.caller) {
			if(ac==log4js.stacktrace||ac==log4js.logger.log){continue;}
	        s += log4js.funcname(ac) + "\n";
	        // ac.arity - is the number of expected arguments for the function.
	        // ac.toSource - get the function source code. 
	        if(ac.caller==ac){break;}
	    }
	    return s;
    } catch(ee) {
    	alert("Unable to get stack trace. " + ee.message + "\n" + ee.stack);
    }
};
log4js.LogEvent = function(logLevel, message, exceptionName, exceptionMessage, stackTrace) {
	this.logLevel = logLevel;
	this.message = message;
	this.exceptionName = exceptionName;
	this.exceptionMessage = exceptionMessage;
	this.stackTrace = stackTrace;
};
log4js.LogEvent.prototype = {
	encodeParam : function(queueId) {
		var b="&loglevel."+queueId+"="+this.logLevel+"&message."+queueId+"="+encodeURIComponent(this.message);
		if(this.exceptionName){b+="&exceptionname."+queueId+"="+encodeURIComponent(this.exceptionName);}
		if(this.exceptionMessage){b+="&exceptionmessage."+queueId+"="+encodeURIComponent(this.exceptionMessage);}
		if(this.stackTrace){b+="&exceptionstacktrace."+queueId+"="+encodeURIComponent(this.stackTrace);}
		return b;
	}
};
log4js.LogLevel = {};
log4js.LogLevel.FATAL = "FATAL";
log4js.LogLevel.ERROR = "ERROR";
log4js.LogLevel.WARN = "WARN";
log4js.LogLevel.INFO = "INFO";
log4js.LogLevel.DEBUG = "DEBUG";
log4js.__Logger = function() {
	this.ajaxSync = false;
	this.logTarget = window.location || document.location || document.URL;
	this.internalError = false;
	this.currentLevel = -1;
	this.timeout = null;
	this.lastlog = new Date();
	var level = i2rd.getCookie("loglevel");
	if(level){this.currentLevel = this.getLogLevelInt(level);}
	var me = this;
	i2rd.addEvent(window, 'unload', function() {me.cleanup(); delete me;});
};
log4js.__Logger.queue = [];
log4js.__Logger.prototype = {
	queue: [],
	cleanup: function(evt) {
		if(this.timeout){window.clearTimeout(this.timeout);}
		this.ajaxSync = true;
		this.flushQueue(true);
	},
	getLogLevelInt: function(level) {
		switch(level) {
			case log4js.LogLevel.FATAL:
				return 50000;
			case log4js.LogLevel.ERROR:
				return 40000;
			case log4js.LogLevel.WARN:
				return 30000;
			case log4js.LogLevel.INFO:
				return 20000;
			case log4js.LogLevel.DEBUG:
				return 10000;
			default:
				return 20000;
		}
	},
	canLog: function(level) {
		return this.currentLevel <= this.getLogLevelInt(level);
	},
	setLogLevel: function(level) {
		this.currentLevel = this.getLogLevelInt(level);
		var now = new Date();
		var future = new Date(now.getTime() + (180000));
		i2rd.setCookie("loglevel", level, future);
	},
	setInternalError: function() {
		this.internalError = true;
	},
	isOn: function() {
		return !this.internalError;
	},
	log: function(logLevel, msg, ex) {
		// TODO <russ@i2rd.com> we could be smarter about how we ask the server what the current log level is.
		var shouldLog = this.canLog(logLevel);
		try	{
			if(!msg){return;}
			var level=log4js.LogLevel.INFO;
			if(logLevel){level = logLevel;}
			if(!this.isOn() || !shouldLog){return;}
			this.lastlog = new Date();
			var en,em,st;
			if(ex) {
				if(ex.name){en = ex.name;}
				if(ex.message){em = ex.message;}
				if(ex.stack){st = ex.stack;}
				else{st = log4js.stacktrace();}
			}
			var le = new log4js.LogEvent(logLevel, msg, en, em, st);
			log4js.__Logger.queue.push(le);
			if(log4js.__Logger.queue.length > 50){this.flushQueue(true);}
			else if(!this.timeout) {
				var me = this;
				this.timeout = window.setTimeout(function(){me.flushQueue(); delete me;}, 
				1500);
			}
		}
		catch(e) {
			alert("Logger misconfigured: " + e.message);
		}
	},
	fatal: function(msg, ex){this.log(log4js.LogLevel.FATAL, msg, ex);},
	error: function(msg, ex){this.log(log4js.LogLevel.ERROR, msg, ex);},
	warn: function(msg, ex){this.log(log4js.LogLevel.WARN, msg, ex);},
	info: function(msg, ex){this.log(log4js.LogLevel.INFO, msg, ex);},
	debug: function(msg, ex){this.log(log4js.LogLevel.DEBUG, msg, ex);},
    clog: function(msg, ll){
        if(!i2rd.hasConsole()) {return;}
        switch(ll){
            case log4js.LogLevel.FATAL:
            case log4js.LogLevel.ERROR:
                console.error(msg); break;
            case log4js.LogLevel.WARN:
                console.warn(msg); break;
            case log4js.LogLevel.INFO:
                console.info(msg); break;
            case log4js.LogLevel.DEBUG:
            default:
                console.debug(msg); break;
        }
    },
	flushQueue : function(force) {
		this.timeout = null;
		var size = log4js.__Logger.queue.length;
		if(size==0){return;}
		var now = new Date();
		if(!force && (now.getTime() - this.lastlog.getTime()) < 250) {
			var me = this;
			this.timeout = window.setTimeout(function(){me.flushQueue(); delete me;}, 500);
			return;
		}
		var h,b = "?log4js.queue_size=" + size;
		for(h=0;h<size;h++){
			var qe = log4js.__Logger.queue[h];
            try{
                this.clog(qe.message, qe.logLevel);
                if(qe.exceptionMessage){this.clog(qe.exceptionMessage, qe.logLevel);}
                if(qe.stackTrace){this.clog(qe.stackTrace, qe.logLevel);}
            }catch(e){}
			b += qe.encodeParam(h);
		}
		var ajax = i2rd.getAjaxTransport();
		if(ajax) {
			if(!this.ajaxSync) { // async
				ajax.onreadystatechange = log4js.__getReqCallback(ajax, this);
			}
			ajax.open("POST", window.location.href, !this.ajaxSync);
			ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			ajax.setRequestHeader("Filter-Logging", "on");
			ajax.setRequestHeader("Filter-Logging_Queue_Size", size);
			ajax.send(b);
		}
		log4js.__Logger.queue.length=0;
	}
};
log4js.logger = new log4js.__Logger();
log4js.__getReqCallback = function(ajax, log) {
	var req = ajax, logger = log;
	return function() {
		if (req.readyState==4) {
    	    if (req.status==200||req.status==0) {
    	    	if(req.responseText) {
    	    		logger.setLogLevel(req.responseText);
   	    		}
	        } else {
	        	logger.setInternalError();
	        }
	        try { 
		        if(req && req.onreadystatechange){delete req.onreadystatechange;}
	        } catch(e) {} // Ignore for IE 6
    	}
	};
};

}// End conditional eval.
