/* @author Shad Claiborne <sclaiborne@outreachservices.com>
   @description Simple page statistics object
   @created 08/05/2010
   @dependencies None */

// Let's try to make this is small as possible for kicks
function Arch( obj ) {
	var arc = this,
		reports,
		reportTo,
		doFireTimeout,
		arch_nc = "arch_nc", // click 
		arch_dbc = "arch_dbc", // double click
		arch_nd = "arch_nd", // mousedown
		arch_nr = "arch_nr", // mouseup
		arch_nh = "arch_nh", // mouseover 
		arch_no = "arch_no", // mouseout
		arch_nkd = "arch_nkd", // keydown
		arch_nku = "arch_nku", // keyup
		arch_nkp = "arch_nkp", // keypress
		arch_nf = "arch_nf", // focus
		arch_nb = "arch_nb"; // blur

	// constructor code
	if ( isObject( obj ) && obj.url ) {
		// I am used to putting all variables in a common place
		var map = {},
			fn = {},
			els = document.getElementsByTagName( '*' ),
			elen = els.length,
			nodeList = [],
			node,
			nlen,
			nclass,
			mlen,
			match,
			matches,
			archList = [arch_nc,
						arch_nh,
						arch_dbc,
						arch_no,
						arch_nkd,
						arch_nku,
						arch_nkp,
						arch_nf,
						arch_nb,
						arch_nr,
						arch_nd];

		// create our map - this allows for EASY dynamicability for future extension
		map[ arch_nc ] = "click";
		map[ arch_dbc ] = "dblclick";
		map[ arch_nd ] = "mousedown";
		map[ arch_nr ] = "mouseup";
		map[ arch_nh ] = "mouseover";
		map[ arch_no ] = "mouseout";
		map[ arch_nkd ] = "keydown";
		map[ arch_nku ] = "keyup";
		map[ arch_nkp ] = "keypress";
		map[ arch_nf ] = "focus";
		map[ arch_nb ] = "blur";
		
		// create our handlers - again, this allows for EASY dynamicability for future extension
		fn[ arch_nc ] = function( e ) { hookNode( getEventTar( e ), "click", arch_nc, e ); };
		fn[ arch_dbc ] = function( e ) { hookNode( getEventTar( e ), "dblclick", arch_dbc, e ); };
		fn[ arch_nd ] = function( e ) { hookNode( getEventTar( e ), "down", arch_nd, e ); };
		fn[ arch_nr ] = function( e ) { hookNode( getEventTar( e ), "release", arch_nr, e ); };
		fn[ arch_nh ] = function( e ) { hookNode( getEventTar( e ), "hover", arch_nh, e ); };
		fn[ arch_no ] = function( e ) { hookNode( getEventTar( e ), "out", arch_no, e ); };
		fn[ arch_nkd ] = function( e ) { hookNode( getEventTar( e ), "keypress", arch_nf, e ); };
		fn[ arch_nku ] = function( e ) { hookNode( getEventTar( e ), "keydown", arch_nkd, e ); };
		fn[ arch_nkp ] = function( e ) { hookNode( getEventTar( e ), "keyup", arch_nkp, e ); };
		fn[ arch_nf ] = function( e ) { hookNode( getEventTar( e ), "focus", arch_nf, e ); };
		fn[ arch_nb ] = function( e ) { hookNode( getEventTar( e ), "blur", arch_nb, e ); };

		doFireTimeout = obj.fireTimeout && isNumber( obj.fireInt );

		if ( doFireTimeout ) {
			reports = [];
			reportTo = setTimeout( reportInt, obj.fireInt );
		}

		// let's filter out non-element (i.e. node type 2+ ) nodes
		while ( elen-- ) {
			node = els[ elen ];
			if ( isElNode( els[ elen ] ) ) {
				nodeList[ nodeList.length ] = node;
			}
		}

		nlen = nodeList.length;

		// let's build the watch list
		while ( nlen-- ) {
			node = nodeList[ nlen ];
			nclass = classFn( node );
			
			if ( nclass ) {
				matches = nclass.has( archList );
				mlen = matches.length;
	
				// we can add more later if necessary
				while ( mlen-- ) {
					match = matches[ mlen ];
					if ( match ) {
						if ( node.arched !== true ) {
							node.arched = true;
							node.urls = obj.urls;
							node.url = obj.url;
						}
						override( node, match );
						specialize( node );
						fn[ match ].hook( map[ match ], node, true, 1 );
					}
				}
			}
		}
	}
	
	// override stuff that would prevent Arch from 
	function override( node, archType ) {
		var type = "on" + map[ archType ];
		
		// override event level 0 stuff
		if ( isFunction( node[ type ] ) ) {
			node[ "_" + type ] = node[ type ];
			node[ type ] = null;
			attribute( type, "" );
		}
	}
	
	function handleOverride( el, evo, evt ) {
		if ( isFunction( el[ "_on" + evo ] ) ) {
			el[ "_on" + evo ]( evt );
		}
	}
	
	// common code used by event handlers
	function hookNode( tar, et, cls, evt ) {
		if ( tar.arched !== true ) {
			tar = parentWithClass( tar, cls );
		}
		
		if ( isElNode( tar ) && tar.arched && tar.url) {
			genReport( tar, et, evt, ( tar.urls && tar.urls[ et ] ) ? tar.urls[ et ] : tar.url );
		}
	}
	
	// specialize
	function specialize( el ) {
		switch( nodeName( el ).toUpperCase() ) {
			case 'A':
				el._href = attribute( el, "href" );
				attribute( el, "href", "javascript:;" );
				break;
		}
	}
	
	// handle specialized nodes
	function handleSpecial( el ) {
		switch( nodeName( el ).toUpperCase() ) {
			case 'A':
				location.replace( el._href );
				break;
		}
	}
	
	// generates a report
	function genReport( el, evo, evt, url ) {
		var id = getAttribute( el, "id" ),
			ns = getAttribute( el, "ns" ),
			param = getAttribute( el, "param" ),
			nName = nodeName( el ),
			date = new Date(),
			json = '{ "ID": "' 
				+ id 
				+ '", "NAMESPACE": "' 
				+ ns 
				+ '", "NODE": "'
				+ nName
				+ '", "DATE": "'
				+ date.getMonth() + '-' + date.getDay() + '-' + date.getFullYear()
				+ '", "TIME": "'
				+ date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds()
				+ '", "PARAM": "'
				+ param
				+ '", "EVENT": "'
				+ evo
				+ '", "BROW": "'
				+ getBrowser()
				+ '"}';

			if ( id ) {
				// let's report it
				report( json, url, el, evo, evt );
			}
	}

	// primary reporting interval (i.e. timeout) callback
	function reportInt() {
		if ( obj.throttle && isNumber( obj.throttleInt ) ) {
			throttle();
		} else {
			reportFn();
		}
	}

	// timeout for reporting - used for performance
	function reportFn() {
		var rlen = reports.length,
			obj;
			
		while ( rlen-- ) {
			obj = reports[ rlen ];
			newXhr( "POST", obj[ 0 ], true, obj[ 1 ], function(){}, null );
		}
		
		reports = [];
		reportTo = setTimeout( reportInt, obj.fireInt );
	}

	// throttle for arccuracy
	function throttle() {
		var obj;

		if ( reports.length ) {
			obj = reports.pop();
			newXhr( "POST", obj[ 0 ], true, obj[ 1 ], function( request ){ handleSpecial( request.attachment[ 0 ] ); }, [ obj[ 2 ], obj[ 3 ], obj[ 4 ] ] );
			reportTo = setTimeout( throttle, obj.throttleInt );
		} else {
			reportTo = setTimeout( reportInt, obj.fireInt );
		}
	}

	// sends a report
	function report( obj, url, el, evo, evt ) {
		if ( doFireTimeout ) {
			reports[ reports.length ] = [ url, obj, el, evo, evt ];
		} else {
			newXhr( "POST", url, true, obj, function( request ) { 
													 handleSpecial( request.attachment[ 0 ] ); 
													 handleOverride( request.attachment[ 0 ], request.attachment[ 1 ], request.attachment[ 2 ] ); 
											},  [ el, evo, evt ] );
		}
	}

	// shutdown arch :(
	arc.shutdown = function( obj ) {
		if ( isObject( obj ) ) {
		}

		dispose();
	};

	// dispose of arch
	function dispose() {
	}
}
