//===============================================================================================
// GLOBALS
//-----------------------------------------------------------------------------------------------

// CONTROL FLAG / DEFAULT VALUE CONSTANTS -------------------------------------------------------
AF.cfg.DISABLE_HIDEABLE_SECTIONS = false;
AF.cfg.DEFAULT_DBG_MSG_LEVEL = 2;
AF.cfg.DEFAULT_DBG_LEVEL = 2;

// DEBUG LEVEL CONSTANTS ------------------------------------------------------------------------

// debug level at which certain debug messages are displayed
AF.dbgLvl = {
	SUB_SECTIONS: 2,
	PAGE_MSG: 4,
	DBG_MSG_INFO: 5,
	WARNINGS: 2,
	EXT_DEBUG_INFO: 5		// misc extended debug info
}


//===============================================================================================
// Debug CLASS (client-side)
//	- Encapsulates debug messaging services, current debug level, etc.
//	- Must be instantiated before other client-side page objects
//===============================================================================================
function Debug(iDbgLevel, oMsgAreaElem) {
	// initialise globals/class globals
	var oDbg = this;																	// pointer to current instance of Debug class

	// initialise class properties
	this.level = iDbgLevel;
	this.msgAreaElem = oMsgAreaElem;
	this.msgArea = null;

	this.IsExtended = AreDefaultMsgsEnabled;
	this.IsEnabled = function(){ return (this.level>0) }
	// default prop = return true if debugging enabled, else return false
	this.toString = this.valueOf = new DynamicProp( function(){ return (this.level > 0) } );

	// declare class methods
	this.Msg = AddDbgMsg;
	this.DelMsg = DelDbgMsg;
	this.Clear = ClearDbgMsgs;
	this.Write = Write;
	this.WarnMsg = WarningMsg;

	this.StartSubSection = StartSubSection;
	this.EndSubSection = EndSubSection;
	this.DumpObj = DumpObjProps;
	this.InitPageMsgArea = InitPageMsgArea;

	// NOT NEEDED? (not included in new)
		//this.HTMLSpy = HTMLSpy;							// declare event handlers
	
	Initialise();
	function Initialise() {
		AF.Dbg = oDbg;
		InitDebugLevel();				// if iDbgLevel not specified, use iDbgLevel global, or fall back to AF.cfg.DEFAULT_DBG_LEVEL
		/* OLD:
				if(oDbg.IsEnabled()) {
					// if no debug messages area specified when creating this debug object, create debug messages window instead
					if(!IsObj(oDbg.msgAreaElem)) {
						var oDebugWin = DebugMsgWin();
						oDbg.msgAreaElem = oDebugWin.document.body;
					}
					// NOT NEEDED? (not included in new)
						// if message area element specified, create page messaging object for it
						//if(IsObj(oPageMsg))	oDbg.msgArea = new oPageMsg.Area(oDbg.msgAreaElem);		//, AF.val.SINGLE_MSG_LIST
		
					// redirect all Write calls to specified message area
					Write.target = oDbg.msgAreaElem;
				}
		*/
		
		// call AttachHTMLSpy when window finished loading
		if(oDbg.IsExtended()) window.attachEvent("onload", AttachHTMLSpy);
		// OLD: 
				// attach HTMLSpy to display selection HTML when F12 pressed
				//if(oDbg.IsExtended()) document.body.attachEvent("onkeydown", HTMLSpy);
	}
	

	//=============================================================================================
	// METHODS
	//---------------------------------------------------------------------------------------------
	
	function AddDbgMsg(sText, iMsgDbgLevel, sCssClass) {
		if(!IsNum(iMsgDbgLevel)) iMsgDbgLevel = AF.cfg.DEFAULT_DBG_MSG_LEVEL;
		if(oDbg.level >= iMsgDbgLevel) {
			if(oDbg.level >= AF.dbgLvl.DBG_MSG_INFO) WriteMsg("AddDbgMsg(\""+ sText +"\", "+ iMsgDbgLevel + BlankIfBlank(", \"", sCssClass, "\"") + ")", "dbgMsgDbgMsg");
			sCssClass = IsNBStr(sCssClass)? sCssClass + " dbgMsg" : "dbgMsg";
			// OLD: DbgMsg("Dbg.Msg( "+ sText +" , "+ iMsgDbgLevel + BlankIfBlank(", ", sCssClass, "") + ")", 5);

			InitPageMsgArea();		// attempt to initialise debug page messaging area if necessary and possible

			// add page msg to debug messaging area (or write direct if page messaging not enabled)
			if(IsObj(oDbg.msgArea))
				return oDbg.msgArea.Add(sText, sCssClass);		// add message to msg area and return pointer
			else {
				WriteMsg(sText, sCssClass);
				return null;
			}
		}
		else return "MSG_HIDDEN";
	}

	function DelDbgMsg(vMsgObjOrNum) {	if(IsObj(oDbg.msgArea)) oDbg.msgArea.Del(vMsgObjOrNum); }
	function ClearDbgMsgs() {						if(IsObj(oDbg.msgArea)) oDbg.msgArea.Clear(); }

	// write self-contained list element message to debug message area
	function WriteMsg(sText, sCssClass) {
		sCssClass = IsNBStr(sCssClass)? " "+sCssClass : "";		// prefix with space if non-blank
		Write("\n<ul title='Posted by "+ WriteMsg.CallerName() +"()' class='dbgMsg"+ sCssClass +"'><li>"+ sText +"</li></ul>\n");
	}

	// if debug level above specified level (or default message level), send output to Write
	function DbgWrite(vOutput, iDbgLevel) {
		if(oDbg.level >= (IsNum(iDbgLevel)? iDbgLevel : AF.cfg.DEFAULT_DBG_MSG_LEVEL))
			Write(vOutput);
	}

	// append specified output to the element pointed to by Write.target
	function Write(vOutput) {
		// unless redirected, write debug output direct to the element msgAreaElem (or to main window if undefined)
		if(!IsObj(Write.target)) Write.target = IsObj(oDbg.msgAreaElem)? oDbg.msgAreaElem : window.document.body;

		if(Write.buffer)
			Write.outputBuffer += vOutput;
		else {
			InitPageMsgArea();		// attempt to initialise debug page messaging area if necessary and possible

			// OLD (breaks dynamic event handlers for other elements in target): Write.target.innerHTML += vOutput;
			Write.target.insertAdjacentHTML("beforeEnd", vOutput);
		}

		// OLD:		Write.targetDoc = oDbg.msgAreaElem.document; if(Write.targetDoc.readyState == "interactive") Write.targetDoc.write(vOutput);
	}
	
	// write buffering (needed to delay parsing of output and to speed up blocks of writes)
	Write.outputBuffer = "";
	Write.BeginBuffer = function() { Write.buffer = true; }
	Write.FlushBuffer = function() { InitPageMsgArea(); Write.target.insertAdjacentHTML("beforeEnd", Write.outputBuffer); Write.outputBuffer = ""; Write.buffer = false; }
	Write.ClearBuffer = function() { Write.outputBuffer = ""; Write.buffer = false; }

	
	// start/end sub messages area
	function StartSubSection(bExpand, sCssClass) {
		if(oDbg.level >= AF.dbgLvl.SUB_SECTIONS) {
			var oDoc = Write.target.document;
			var divSubSect = oDoc.createElement("DIV");
			if(bExpand) divSubSect.showInitially = "true";

			// CSS classes
			var sCssClassText = "subSection";
			if(!AF.cfg.DISABLE_HIDEABLE_SECTIONS) sCssClassText += " hideable";
			if(IsNBStr(sCssClass)) sCssClassText += " " + sCssClass;
			divSubSect.className = sCssClassText;

			Write.target.appendChild(divSubSect);
			Write.target = divSubSect;
			++StartSubSection.iCurrDepth;
		}
	}
	StartSubSection.iCurrDepth = 0;
	
	function EndSubSection() {
		if(StartSubSection.iCurrDepth > 0) {
			Write.target = Write.target.parentElement;
			--StartSubSection.iCurrDepth;
		}
		// OLD: if( oDbg.level >= AF.dbgLvl.SUB_SECTIONS )
	}


	// dump interactive list of properties for specified object (allows drill into sub objects)
	function DumpObjProps(oObj, sObjName, bHideBlanks) {
		var x, oErr, sItemName, sObjPrefix = "";

		if(oDbg.IsEnabled()) {
			Write.BeginBuffer();
			Write("<ul><li>"+ (typeof sObjName == "string"? "<b>"+ sObjName +"</b> ": "") + (typeof oObj.tagName == "string"? oObj.tagName : "") +" Element Properties:</li>", 3);
			Write("<ul>");
				if(IsObj(oObj) && (NumProps(oObj)>0) ) {
					// cycle through properties
					for(x in oObj) {
						try {
							// if property is null/blank and bHideBlanks is true, omit from listing
							if( !((bHideBlanks == true) && ((oObj[x]==null) || (oObj[x]==""))) ) {
								// get full name of this property ('x', sObjName.x or sObjName[x] depending on whether sObjName specified and whether x is a number)
								sItemName = String((typeof sObjName == undefined)? x : sObjName + (isNaN(parseInt(x))? "."+ x : "["+ x +"]"));
								Write("<li>"+ sItemName + " = ");
								
								// write property value
								try {
									// if property is an object with at least one property of its own, write drill down link
									if((typeof oObj[x] == "object") && (NumProps(oObj[x])>0) ) {
										if(IsObj(Write.target.document.parentWindow.opener)) sObjPrefix = "window.opener.";
										// create link around value to allow explore that object
										Write("<a href=# onclick=\""+ sObjPrefix + "AF.Dbg.DumpObj("+ sObjPrefix + sItemName +", '"+ sItemName +"', "+ bHideBlanks +"); "+ sObjPrefix +"AF.Dbg.Write.target.lastChild.scrollIntoView(); return false;\">"+ oObj[x] +"</A>");
									}
									else if(typeof oObj[x] == "string")
										// if string, wrap in quotes and encode < and > chars
										Write("\"<i>" + oObj[x].replace(/</g,"&lt;").replace(/>/g,">") + "</i>\"");
									else
										// convert numbers, etc to string and dump
										Write(String(oObj[x]));
								}
								catch(oErr) {
									Write("[CANNOT DISPLAY]");
								}
								Write("</li>");
							}
						}
						catch(oErr) {}
					}
				}
				else
					DbgWrite("<li>Not an object</li>", 3);
			Write("</ul>");
			if(typeof sObjName == "string") Write("</ul>");
			Write.FlushBuffer();
		}
	}
	

	// initialises debug page messaging area (called when first client-side debug message written) if possible and necessary
	function InitPageMsgArea() {
		var oPageMsg = AF.PageMsg;			// point oPageMsg to PageMessaging object
		if(oDbg && oPageMsg && !IsObj(oDbg.msgArea)) {
			// if no debug messages area specified when creating this debug object, create debug messages window instead
			if(!IsObj(oDbg.msgAreaElem))
				oDbg.msgAreaElem = DebugMsgWin().document.body;

			// create page messaging area object
			oDbg.msgArea = new oPageMsg.Area(oDbg.msgAreaElem);		//, AF.val.SINGLE_MSG_LIST

			// redirect all Write calls to page message area element
			Write.target = oDbg.msgAreaElem;
		}
	}

	function WarningMsg(sText, sCssClass) {
		if(oDbg.level >= AF.dbgLvl.WARNINGS) {
			// if CSS class not blank/null/undefined, prefix with a space character, else make blank string
			sCssClass = IsNBStr(sCssClass) ? sCssClass + " warnMsg" : "warnMsg";
			var oWarnMsg = oDbg.Msg("<b>Warning</b>: " + sText, AF.dbgLvl.WARNINGS, sCssClass);
			if(oDbg.level >= AF.dbgLvl.EXT_DEBUG_INFO) oWarnMsg.title = "Warning generated by WarningMsg";
		}
	}


	//=============================================================================================
	// DYNAMIC PROPERTY FUNCTIONS
	//---------------------------------------------------------------------------------------------
	
	function AreDefaultMsgsEnabled() {
		return (oDbg.level >= AF.cfg.DEFAULT_DBG_MSG_LEVEL);
	}


	//=============================================================================================
	// HELPER ROUTINES
	//---------------------------------------------------------------------------------------------

	function DbgMsg(sText, iMsgDbgLevel, sCssClass) {
		if(oDbg.level >= AF.dbgLvl.EXT_DEBUG_INFO)
			oDbg.Msg(sText, iMsgDbgLevel, (sCssClass || "") +" debugDbgMsg");
	}
	
	// returns number of properties in specified object (excluding 'length' property, if is one)
	function NumProps(oObj) {
		var i=0, x;
		for(x in oObj) if(x != "length") ++i;			// count properties (don't include 'length' property of collections)
		return i;
	}

	//---------------------------------------------------------------------------------------------
	// HTMLSpy()
	//	When F12 hit, HTML of current selection displayed in an alert (or whole document HTML if no selection).
	//	If CTRL held down when F12 hit, HTML of parent element of selection is displayed instead.
	//	Intended for use as an event handler for onkeydown.
	//---------------------------------------------------------------------------------------------
	function HTMLSpy() {
		var oDoc = this.document, oWin = oDoc.parentWindow;
		// if F12 pressed
		if(oWin.event.keyCode == 123) {
			// if user has selected a portion of the doc
			if(oDoc.selection.type != "None") {
				// create TextRange object from current document selection
				var oRange = oDoc.selection.createRange();
				// alert HTML of current selection (step up to parent element if CTRL held down)
				oWin.alert( oWin.event.ctrlKey? oRange.parentElement().outerHTML : oRange.htmlText );
			}
			else
				oWin.alert(oDoc.documentElement.outerHTML);					// alert whole document HTML
		}
		// OLD (backtrack up hierarchy):	if(!event.altKey) while(IsObj(oObj.parentElement) && (oObj.parentElement != document.body)) oObj = oObj.parentElement;
	}

	// returns reference to new debug window (automatically closes after 10 seconds, unless user focuses on it)
	function DebugMsgWin() {
		var oDebugWin = window.open("", "debugWin", "width=500,resizable=yes,scrollbars=yes");
		window.focus();				// immediately refocus on main window

		// configure debug window properties, behaviour and content
		oDebugWin.document.open();
		oDebugWin.document.write("<title>AF Debug Messages</title><link rel=stylesheet href='/af/css/af.css'><link rel=stylesheet href='/af/css/af_debug.css'><script>var Dbg = window.opener.AF.Dbg;</SC"+"RIPT><BODY class=dbgWin><H1 class=dbg>Debug Messages</H1></BODY>");
		// DISABLED (to allow scripts to contine to write to window): oDebugWin.document.close();

		// close messages window after 2 mins
		oDebugWin.iTimerId = oDebugWin.setTimeout("close()", 120000);
		// cancel closing of messages window if user clicks window
		oDebugWin.document.onfocusin = function() { oDebugWin.clearTimeout(oDebugWin.iTimerId) }
		// refresh main window instead, when F5 hit for this window
		oDebugWin.document.body.attachEvent("onkeydown", function() { if(oDebugWin.event.keyCode == 116) location.reload(); } );
		// attach HTMLSpy to display selection HTML when F12 pressed (call in context of oDebugWin to allow 'this' to work within HTMLSpy)
		oDebugWin.document.body.attachEvent("onkeydown", function() { HTMLSpy.call(oDebugWin) } );
		
		// OLD: window.onunload = function() { oDebugWin.close() }		// close debug messages window when main window closed/refreshed
		
		return oDebugWin;
	}
	
	// if debug level is invalid, use global iDbgLevel value (if defined), or fall back to AF.cfg.DEFAULT_DBG_LEVEL constant
	function InitDebugLevel() {
		if(!IsNum(oDbg.level)) oDbg.level = IsNum(iDbgLevel)? iDbgLevel : AF.cfg.DEFAULT_DBG_LEVEL;
	}
		
	// attach HTMLSpy to display selection HTML when F12 pressed
	function AttachHTMLSpy() {
		document.body.attachEvent("onkeydown", HTMLSpy);
	}
	
}
