/**
 * Since this is a logger, if there is an error we don't want to barf. If the
 * user isn't using firebug, create a dummy method.
 */
if ( typeof console == 'undefined' || typeof console.log == 'undefined' ) {
	console = {};
	console.log = function(arg) {};
}

if ( typeof YAHOO != 'undefined' ) {
	YAHOO.namespace('com.compendium');
}

/**
 * The YAHOO.com.compendium.BrowserDetect class will use the YUI agent
 * detection and return a string representing the browser. Before using
 * the YUI agent detection we just try and return the UA string.
 */
(function() {
	YAHOO.com.compendium.BrowserDetect = function() {
	};
	YAHOO.com.compendium.BrowserDetect.get = function() {
		var browser = null;

		if ( typeof YAHOO != 'undefined' && typeof YAHOO.env != 'undefined' && typeof YAHOO.env.ua != 'undefined' ) {
			if ( YAHOO.env.ua.air ) {
				browser = 'AIR ' + YAHOO.env.ua.air;
			} else if ( YAHOO.env.ua.gecko ) {
				browser = 'Firefox/Mozilla ' + YAHOO.env.ua.gecko;
			} else if ( YAHOO.env.ua.ie ) {
				browser = 'Internet Explorer ' + YAHOO.env.ua.ie;
			} else if ( YAHOO.env.ua.mobile ) {
				browser = 'Mobile: ' + YAHOO.env.ua.mobile;
			} else if ( YAHOO.env.ua.opera ) {
				browser = 'Opera ' + YAHOO.env.ua.opera;
			} else if ( YAHOO.env.ua.webkit ) {
				browser = 'WebKit (Safari, Webkit) ' + YAHOO.env.ua.webkit;
			}
		}

		if ( typeof navigator != 'undefined' && typeof navigator.userAgent != 'undefined' ) {
			browser = browser + ', ' + navigator.userAgent;
		}

		return browser;
	};
})();


/**
 * The YAHOO.com.compendium.Logger will handle both remote and local logging.
 * Local logging is handled by calling YAHOO.log, which, if no LogReader has
 * been setup, will have no effect. Remote logging is handled via an async
 * request to a proxy URL which will submit it to a backend logging service.
 *
 * Requires utilities.js and json-min.js.
 *
 * Usage:
 *     YAHOO.com.compendium.Logger.load();
 *     YAHOO.com.compendium.Logger.getLogger().log(msg,cat,src);
 *     		OR
 *     YAHOO.com.compendium.log(msg,cat,src);
 *
 * Notes:
 *     load is idempotent.
 *     load takes two optional args, a type and some parameters
 *     A variable named CMP_LOGGER_PARAMS is looked for if not passed to load
 *     getLogger is only available after load has been called.
 *     A parameter can be set for a log instance by calling:
 *         YAHOO.com.compendium.Logger.getLogger().setParameter(val)
 */
(function() {
	/**
	 * Private Variables
	 */
	var
		$E = YAHOO.util.Event,
		$D = YAHOO.util.Dom,
		$JSON = YAHOO.lang.JSON,
		$L = YAHOO.lang,
		$C = YAHOO.util.Connect;

	/**
	 * Constructor.
	 *
	 * Create a new instance of the YAHOO.com.compendium.Logger class. This
	 * logs messages remotely or locally.
	 *
	 * @param String type - The type of logger we are
	 * @param Array _config - Configuration attributes.
	 */
	YAHOO.com.compendium.Logger = function(type, _config) {
		// Last time we logged a message
		this.lastLog = 0;

		// local or remote
		this.LoggerType = null;

		// The Url to submit the data to
		this.Url = null;
		this.Method = 'GET';

		// The items that can be set on the service. We try and pull in
		// some of these via the global variable CMP_LOGGER_PARAMS. All
		// values in this object can be overridden via that variable or
		// by calling YAHOO.com.compendium.Logger.getLogger().setParam()
		this.Transaction = null;
		this.Creator = null;
		this.Created = null;
		this.Sent = null; // Done on the fly, shouldn't be externally setable. We don't actually use this.
		this.Klass = null; // The class of event like the code class or a generic class. Set by log(src) if not set
		this.Type = 'JavaScript'; // Specific on a per class basis. A message type.
		this.Signature = null; // Used to identify messages on a per class/type basis. Set by log(cat) if not set
		this.Description = null; // Text. Readable description of message.
		this.Value = null; // Possibly non-text value for description (json, etc)

		this.setLoggerType(type);

		// We have some configuration options
		if ( typeof _config != 'undefined' && $L.isObject(_config) ) {
			for ( var config in _config ) {
				var method = 'set' + config;
				// Improve this, check that it's actually a function
				if ( typeof this[method] != 'undefined' ) {
					this[method](_config[config]);
				}
			}
		}
	};

	// Methods for YAHOO.com.compendium.Logger.
	YAHOO.com.compendium.Logger.prototype = {

		/**
		 * Should we log a remote message or not? If it's been less
		 * than 1ms since we last logged a message return false
		 * otherwise return true. Let's not flood the system with
		 * messages.
		 *
		 * @param Integer _time - Override the default value of 100
		 * @returns Boolean true or false.
		 */
		shouldLog: function(_time) {
			var d = new Date();
			var t = d.getTime();
			var time = 1;
			if ( typeof _time != 'undefined' ) {
				time = _time;
			}
			if ( t > (this.lastLog + time) ) {
				this.lastLog = t;
				return true;
			} else {
				return false;
			}
		},

		/**
		 * Get the type of logger.
		 *
		 * This method returns the logger type, should be one of remote
		 * or local.
		 *
		 * @returns String.
		 */
		getLoggerType: function() {
			return this.LoggerType;
		},

		/**
		 * Set the type of logger.
		 *
		 * This method sets the logger type, should be one of remote or
		 * local.
		 *
		 * @param String type - The type to set the logger as
		 *
		 * @throws Exception if the type is invalid.
		 */
		setLoggerType: function(type) {
			switch ( type ) {
				case 'remote':
				case 'local':
					this.LoggerType = type;
					break;
				default:
					throw "Invalid LoggerType specifed. Must be one of remote or local.";
			}
		},

		setUrl: function(url) {
			this.Url = url;
		},
		getUrl: function() {
			return this.Url;
		},

		setMethod: function(method) {
			switch ( method ) {
				case 'POST':
				case 'GET':
					this.Method = method;
					break;
				default:
					throw "Invalid Method specified. Must be one of POST or GET.";
			}
		},
		getMethod: function() {
			return this.Method;
		},

		setTransaction: function(transaction) {
			this.Transaction = transaction;
		},
		getTransaction: function() {
			return this.Transaction;
		},

		setCreator: function(creator) {
			this.Creator = creator;
		},
		getCreator: function() {
			return this.Creator;
		},

		setCreated: function(created) {
			this.Created = created;
		},
		getCreated: function() {
			return this.Created;
		},

		/**
		 * Notice there is no setSent method.
		 */
		getSent: function() {
			var d = new Date();
			// YYYY-MM-DDTHH:MM:SS+HH:MM
			var offset = d.getTimezoneOffset();
			offset = Math.abs(offset/60);
			if ( offset < 10 ) {
				offset = '0' + offset;
			}
			if ( (d.getTimezoneOffset()*-1) < 0 ) {
				offset = '-' + offset;
			} else {
				offset = '+' + offset;
			}
			offset = offset + ':00';
			return d.toISO8601String(5,offset);
		},

		setKlass: function(klass) {
			
			console.log("Called with class " + klass);

			this.Klass = klass;
		},
		getKlass: function() {
			return this.Klass;
		},

		setType: function(type) {
			this.Type = type;
		},
		getType: function() {
			return this.Type;
		},

		setSignature: function(sig) {
			this.Signature = sig;
		},
		getSignature: function() {
			return this.Signature;
		},

		setDescription: function(descr) {
			this.Description = descr;
		},
		getDescription: function() {
			return this.Description;
		},

		setValue: function(value) {
			this.Value = value;
		},
		getValue: function() {
			return this.Value;
		},

		/**
		 * msg: message
		 * cat: info, warn, error, time, etc
		 * src: src of message.
		 */
		log: function(msg, cat, src) {
			if ( typeof msg != 'undefined' ) {
				this.setDescription(msg);
			} else {
				throw "Description is a required parameter for logging.";
			}

			if ( typeof cat != 'undefined' ) {
				if ( !this.getSignature() ) {
					this.setSignature(cat);
				}
			}

			if ( typeof src != 'undefined' ) {
				if ( !this.getKlass() ) {
					this.setKlass(src);
				}
			}

			var attribs = [];
			if ( this.getTransaction() ) {
				attribs.push('Transaction=' + encodeURIComponent(this.getTransaction()));
			}
			if ( this.getCreator() ) {
				attribs.push('Creator=' + encodeURIComponent(this.getCreator()));
			}
			if ( this.getCreated() ) {
				attribs.push('Created=' + encodeURIComponent(this.getCreated()));
			}
			if ( this.getKlass() ) {
				attribs.push('Class=' + encodeURIComponent(this.getKlass()));
			}
			if ( this.getType() ) {
				attribs.push('Type=' + encodeURIComponent(this.getType()));
			}
			if ( this.getSignature() ) {
				attribs.push('Signature=' + encodeURIComponent(this.getSignature()));
			}
			if ( this.getDescription() ) {
				attribs.push('Description=' + encodeURIComponent(this.getDescription()));
			}
			if ( this.getValue() ) {
				attribs.push('Value=' + encodeURIComponent(this.getValue()));
			} else {
				var browser = YAHOO.com.compendium.BrowserDetect.get();
				if ( !browser ) {
					browser='';
				}
				var loc = location.href;
				if ( !loc ) {
					loc = '';
				}
				attribs.push('Value=' + encodeURIComponent(browser + "\n" + loc));
			}

			attribs.push('Sent=' + encodeURIComponent(this.getSent()));

			var url = null;
			var postData = null;
			if ( this.getUrl() ) {
				url = this.getUrl();
			} else if ( typeof base_url != 'undefined' ) {
				url = base_url + '/app/proxy/log/json';
			} else {
				url = 'http://127.0.0.1/proxy.php';
			}

			if ( this.Method == 'GET' ) {
				url += '?' + attribs.join('&');
			} else {
				postData = attribs.join('&');
			}
			if ( this.getLoggerType() == 'remote' && this.shouldLog() ) {
				var callback = {
					success: function(o) {
						var response = o.responseText;
						try {
							response = $L.JSON.parse(response);
							if ( typeof response.Success != 'undefined' ) {
								// Do nothing, we were successful
							} else {
								console.log("Failed, no success response");
							}
						} catch ( e ) {
							console.log("Caught exception: " + e);
						}
					},
					failure: function(o) {
						console.log("Failure case: Status was " + o.status + " and statusText was " + o.statusText);
					}
				};
				var trans = YAHOO.util.Connect.asyncRequest(this.getMethod(), url, callback, postData);
			} else if ( this.getLoggerType() == 'local' ) {
				if ( YAHOO && YAHOO.log ) {
					YAHOO.log(this.getDescription(), this.getSignature(), this.getKlass());
				}
			}
		}
	};

	YAHOO.com.compendium.Logger.FATAL = 'FATAL';
	YAHOO.com.compendium.Logger.ERROR = 'ERROR';
	YAHOO.com.compendium.Logger.WARN = 'WARN';
	YAHOO.com.compendium.Logger.INFO = 'INFO';
	YAHOO.com.compendium.Logger.DEBUG = 'DEBUG';
	YAHOO.com.compendium.Logger.TRACE = 'TRACE';

	/**
	 * Load the logger.
	 *
	 * @param String type - Type of logger to instantiate
	 * @param Object params - Attributes to pass to the logger
	 */
	YAHOO.com.compendium.Logger.load = function(_type, params) {
		if ( YAHOO.com.compendium.Logger.getLogger ) {
			return;
		}
		var lp = {};
		var type = 'remote';
		if ( typeof _type != 'undefined' ) {
			type = _type;
		}

		if ( typeof params != 'undefined' ) {
			lp = params;
		} else if ( typeof CMP_LOGGER_PARAMS != 'undefined' ) {
			lp = CMP_LOGGER_PARAMS;
		}
		var logger = new YAHOO.com.compendium.Logger(type, lp);
		// Singleton of sorts
		YAHOO.com.compendium.Logger.getLogger = function() {
			return logger;
		};
		YAHOO.com.compendium.log = function (a,b,c) { return logger.log(a,b,c); };
	};
})();

/**
 * This has been ripped off from the web. AFAIK it's public domain.
 */
Date.prototype.toISO8601String = function (format, offset) {
	/* accepted values for the format [1-6]:
	 1 Year:
	 YYYY (eg 1997)
	 2 Year and month:
	 YYYY-MM (eg 1997-07)
	 3 Complete date:
	 YYYY-MM-DD (eg 1997-07-16)
	 4 Complete date plus hours and minutes:
	 YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
	 5 Complete date plus hours, minutes and seconds:
	 YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
	 6 Complete date plus hours, minutes, seconds and a decimal
	 fraction of a second
	 YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
	 */
	var odate = null;
	if (typeof format == 'undefined') { format = 6; }
	if (typeof offset == 'undefined') {
		offset = 'Z';
		odate = this;
	} else {
		var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
		var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
		offsetnum *= ((d[1] == '-') ? -1 : 1);
		odate = new Date(Number(Number(this) + (offsetnum * 60000)));
	}

	var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; };

	var str = "";
	str += odate.getUTCFullYear();
	if (format > 1) { str += "-" + zeropad(odate.getUTCMonth() + 1); }
	if (format > 2) { str += "-" + zeropad(odate.getUTCDate()); }
	if (format > 3) {
		str += "T" + zeropad(odate.getUTCHours()) +
		":" + zeropad(odate.getUTCMinutes());
	}
	if (format > 5) {
		var secs = Number(odate.getUTCSeconds() + "." +
		((odate.getUTCMilliseconds() < 100) ? '0' : '') +
		zeropad(odate.getUTCMilliseconds()));
		str += ":" + zeropad(secs);
	} else if (format > 4) { str += ":" + zeropad(odate.getUTCSeconds()); }

	if (format > 3) { str += offset; }
	return str;
};
