/**
 * @author haw5855
 */
var documentation = new function () {
	
	var me = this;
	
	
	
	var insertSrcRequests = new Array();
	var requestInProcess = false;
	var doctypeRe = new RegExp("<!DOCTYPE[ .\n\r\t::a-zA-Z0-9\-\"\/]+>");
	var singleLineCommentRe = new RegExp("//[\s\S]*$" ,"g");
	var multiLineCommentRe = /\/\*[\s\S]+?\*\//g;
	var crRe = /\n/g;
	var mscrRe = /\r/g;
	var endLineRe = new RegExp("\n", "g");
	var endLineReplaceString = ' zoltanDummy="yes" ';
	var endLineReplaceStringRe = new RegExp(endLineReplaceString, 'g')
	var firstCarriageReturnsRe = new RegExp("\n");
	var lastCarriageReturnsRe = new RegExp("\n*$");
	var listingTableHeadTemplate = '<table class="src"><thead>';
	var listingTableRowTemplate = '<tr><th class="lineNumber">%s</th><td class="lineContent %s">%s&nbsp;</td></tr>';
	var listingTableFootTemplate = '</table>';
	var JSObjects = new Array('Array', 'Boolean', 'Date', 'Error', 
		'Function', 'java', 'JavaArray', 'JavaClass', 'JavaObject', 
		'JavaPackage', 'Math', 'netscape', 'Number', 'Object', 'Packages', 
		'RegExp');
		

	var JSObjectRegExps = new Array();
	var JSObjectReferenceLink = '$1<a href="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:$2:$3">$2.$3</a>';
	var JSObjectRegExpString = '([^\\w])(%s)\\.(\\w*)'
	
	var DOMMethodObjects = new Array ('(doc)ument', '(win)dow', '(nav)igator', '(scr)een',
		'history');
	var DOMMethodObjectRegExps = new Array();
	var DOMMethodObjectReferenceLink = '$1<a href="http://www.w3schools.com/htmldom/met_$3_$4.asp">$2.$4</a>$5';
	var DOMMethodObjectRegExpString = '([^\\w])(%s)\\.(\\w*)(\\([\\w\'\"]*\\))'
	
	var DOMPropertyObjects = new Array ('(doc)ument', '(win)dow', '(nav)igator', '(scr)een',
		'history');
	var DOMPropertyObjectRegExps = new Array();
	var DOMPropertyObjectReferenceLink = '$1<a href="http://www.w3schools.com/htmldom/prop_$3_$4.asp">$2.$4</a>$5';
	var DOMPropertyObjectRegExpString = '([^\\w])(%s)\\.(\\w*)([^\\()])'
	
	var pageText = new Array();
	
	
	function isCached(pageURL) {
		if (pageText[pageURL]) {
			return true;
		} else {
			return false;
		}
	}
	
	function setCachedPage(pageURL, text) {
		pageText[pageURL] = text;
	}
	
	function getCachedPage(pageURL) {
		return pageText[pageURL];
	}
	
	function transformCodeObjects() {
		
		
		var codeObjects = CSSHelpers.getElementsByClassName(
			document, 'documentation-code'); 
		
		for (var i=0; i<codeObjects.length; i++ ) {
			
			
			var code = codeObjects[i];
			
			if (CSSHelpers.isMemberOfClass(code, 'documentation-src')) {
				insertSrc(code);
				
			} else if (CSSHelpers.isMemberOfClass(code, 'documentation-xmlObject')) {
				insertXMLObject(code);
				
			} else if (CSSHelpers.isMemberOfClass(code, 'documentation-function')) {
				insertFunction(code);
			} else {
				var ignoreSpace;
				if (code.nodeName == 'PRE') {
					ignoreSpace = 'ignoreSpace';
				} else {
					ignoreSpace = null;
				}
				
				var html = StringHelpers.entify(
					StringHelpers.uncommentHTML(code.innerHTML), ignoreSpace);
				
				
				setSource(code, fixCarriageReturns(html))
				//code.innerHTML = fixCarriageReturns(html);
				
			}
		}
	}
	
	function insertSrc(node) {
		var params = StringHelpers.uncommentHTML(node.innerHTML).trim().split(',');
		
		var url = params[0];
		if (params.length == 1) {
			
			insertSrcRequests.push(new RequestHelper(url, processInsertSrcRequest, node));
		} else {
			params[0]=node;
			
			insertSrcRequests.push(new RequestHelper(url, processInsertSrcFragmentRequest, node, params[1], params[2]));
		}
	}
	
	function processInsertSrcRequest(req, node) {
		// node[0].innerHTML = StringHelpers.entify(req.responseText);
		var argv = new Array(node[0], 1, getNumberOfLines(req.responseText));
	
		processInsertSrcFragmentRequest(req, argv)
	}
	
	function processInsertSrcFragmentRequest(req, argv) {
		
		var node = argv[0]
		var begin = argv[1]
		var end = argv[2];
		
		setSource(node, getLines(req.responseText, begin, end, true));
	}
	
	function getLines(str, begin, end, printLineNumbers) {
		var sb = new StringBuffer();
		
		var strArray = str.split('\n');
		
		sb.append(listingTableHeadTemplate);
		
		// j is only there to alternate colours.
		var i, j;
		for (i=begin - 1, j=0; i<end; i++, j++) {
			
			var lineClass = (j%2 == 0)?"even":"odd";
			var line = referenceJSCode(StringHelpers.entify(strArray[i].split('\r')[0]));
			sb.append(StringHelpers.sprintf(listingTableRowTemplate, 
				printLineNumbers?(i+1):"", lineClass, line ));
		}
		sb.append(listingTableFootTemplate)
		
		return sb.toString();
	}
	
	function insertFunction(node) {
		var parameters=StringHelpers.uncommentHTML(node.innerHTML).trim().split(',');
		var argc = parameters.length;
				
		if (argc != 2 && argc != 4) {
			setSource(node, 
				StringHelpers.sprintf('Error: not enough arguments (%d)', argc)) ;
		}
		
		var file = parameters[0];
		var functionName = parameters[1];
		var begin = parameters[2];
		var	end = parameters[3];
		
		
		//DebugHelpers.log("getting function %s in file %s", functionName, file );
		insertSrcRequests.push(new RequestHelper(file, processInsertFunctionRequest, node, file, functionName, begin, end))
	}
	
	function processInsertFunctionRequest(req, argv) {
		
		var node = argv[0]

		var file = argv[1];
		var functionName = argv[2];
		var begin = parseInt(argv[3]);
		var end = parseInt(argv[4]);
		
		//DebugHelpers.log("node: %s, file: %s, func: %s", node, file, functionName)
		
		var src = req.responseText;
		
		var loc = src.indexOf(functionName);
		var tailString = src.substring(loc, src.length-1);
		
		var headString = src.substring(0, loc);
		var lineNumberOfFunction = headString.split('\n').length;

		
		
		if (!begin) {
			var functionObject = extractFirstFunction(tailString);
			setSource(node, getLines(src, lineNumberOfFunction, 
				lineNumberOfFunction + functionObject.lastLine, true));
		} else {
			setSource(node, getLines(src, lineNumberOfFunction + begin - 1, 
				lineNumberOfFunction + end - 1, true));
		}
	}
	
	function extractFirstFunction(string) {
		var firstLine, lastLine;
		
		/* string = string.replace(singleLineCommentRe, '')
					.replace(multiLineCommentRe, ''); */
		
		
		
		var lines = string.split('\n');
		
		var output = new StringBuffer();
		
		var parenCount=0;
		var gotFirstParen = false;
		
		
		
		for (var i=0; i<lines.length; i++) {
			var line = lines[i];
			
			DebugHelpers.log("line %d: %s", i, line)
			
		
			var leftParenPartition = line.split('{');
			
			if (leftParenPartition.length > 1) {
				firstLine = i;
				gotFirstParen = true;
			}
			
			
			
			if (gotFirstParen) {
				
				
				for (var j=0; j<leftParenPartition.length; j++) {
					var theSubstring = leftParenPartition[j];
					var rightParenPartition = theSubstring.split('}');
					var numRightParens = rightParenPartition.length - 1;
					
					
					
					if (j!= 0 ) {
						output.append("{")
						parenCount ++;
						DebugHelpers.log("increased. parenCount is %d", parenCount);
					}
					
					if (rightParenPartition.length == 1)  {
						output.append(StringHelpers.entify(theSubstring));
					} else {
						for (var k=0; k<rightParenPartition.length; k++) {
							var theSubSubstring = rightParenPartition[k];
							
							output.append(StringHelpers.entify(theSubSubstring));
							
							if (k != rightParenPartition.length -1) {
								output.append("}")
								parenCount--;
								DebugHelpers.log("decreased. parenCount is %d", parenCount);
								
								if (parenCount == 0) {
									lastLine = i;
									
									// This function will return the first line and last line.
									//return fixCarriageReturns(output.toString());
									var r = new Object();
									r.firstLine = firstLine;
									r.lastLine = lastLine;
									r.numberOfLines = lastLine - firstLine;
									r.functionString = output.toString();
									return r;
								}
							}
							
						}
					}
				}
			} else {
				output.append(line)
				
			}
			
			
			
			
		}
		

		
		return fixCarriageReturns(output.toString());
		
	}
	
	function fixCarriageReturns(s) {
	
	  var r = s.replace(mscrRe, '\n<br />')
	  			.replace(crRe, '\n<br />')
				.replace(endLineReplaceStringRe, '');
	  
	  return r;
	}
	
	function insertXMLObject(node) {
		var argv = StringHelpers.uncommentHTML(node.innerHTML).trim().split(',');
		var argc = argv.length;


		if (3 > argc || argc > 4) { 
			setSource(node, 
				StringHelpers.sprintf('Error: not enough arguments (%d)', argc));
		} else if (isCached(argv[0])){
			extractObjectToNode(node, getCachedPage(argv[0]), argv[1], argv[2], argv[3]);
		} else {
			insertSrcRequests.push(new RequestHelper(argv[0], 
				processInsertXMLObjectRequest, node, argv[1], argv[2], argc[3]));
		}	
	}
	
	function processInsertXMLObjectRequest(req, argv) {
		var node = argv[0];
		var src = req.responseText.replace(doctypeRe, '');
		var selector = argv[1];
		var attrName = argv[2];
		var attrValue = argv[3];
		

		setCachedPage(argv[0], src);

		extractObjectToNode(node, src, selector, attrName, attrValue);
	}
	
	function extractObjectToNode(node, src, selector, attrName, attrValue) {
		
		
		// IE doesn't register carriage returns in text nodes, so we put in
		// the text zoltanDummy="yes" in replace of them in the original text,
		// so we can replace them with carriage returns later.  Yes, this is 
		// a hack, but it seems like it is the only way to solve this issue.
		var newSrc = src
			.replace(lastCarriageReturnsRe, '')
			.replace(firstCarriageReturnsRe, '')
			.replace(endLineRe, endLineReplaceString);
		
		//document.getElementById('debug').innerHTML = (StringHelpers.entify(newSrc));
		var xmlObj = XMLHelpers.parseXML(newSrc);
		
		var objs = xmlObj.getElementsByTagName(selector);
		var objAttrValue;
		

		if (objs.length == null) {
			setSource(node, 
				StringHelpers.sprintf('error with selector %s on page %s'),
					selector, node);
			return;
		} else {
			//DebugHelpers.log('obj is ' + obj)
		}
		
		var itemNum = parseInt(attrName);
		var obj = null;
		if (isFinite(itemNum)) {
			obj = objs[itemNum];

			//alert(XMLHelpers.getOuterXML(obj))
			var srcString = (XMLHelpers.getOuterXML(obj)).replace(endLineRe,'').replace(endLineReplaceStringRe, '\n');
			
			setSource(node,
				getLines(srcString, 1, getNumberOfLines(srcString), false));
			return;
		} else {
		
			for (var i=0; i<objs.length; i++) {
				obj = objs[i];
				objAttrValue = DOMHelpers.getAttributeValue(obj);
				
				if (objAttrValue == attrValue) {
					setSource(node, 
						fixCarriageReturns(StringHelpers.entify(
							XMLHelpers.getOuterXML(obj))));
					
					return;
				}
			}
		
		
			//alert(StringHelpers.entify(XMLHelpers.getOuterXML(obj)));
			setSource(node, 'error');
		}
	}
	
	function getNumberOfLines(s) {
		
		return s.split('\n').length;
	}
	
	function setSource(node, html) {
		node.innerHTML = html;
		/* return;
		
		CSSHelpers.setHeight(node, 1);
		node.style.overflow = 'hidden';
		node.innerHTML = html;
		var table = node.getElementsByTagName('table')[0];
		
		node.style.display = 'block';
		
		me.expandNodeTimeout(node, CSSHelpers.getHeight(table), CSSHelpers.getHeight(table)/10, 0); */
	}
	
	me.expandNodeTimeout = function(node, toHeight, step, counter) {
		CSSHelpers.setHeight(node, counter*step);
		
		if (counter == 10) {
			return;
		} else {
			me.timer.setTimeout('expandNodeTimeout', 
				20, node, toHeight, step, counter + 1);
		}
	}
	
	function referenceJSCode (s) {
		var r = s;
		
		for (var i=0; i<JSObjectRegExps.length; i++) {
			r = r.replace(JSObjectRegExps[i], JSObjectReferenceLink)
		}
		
		for (var i=0; i<DOMMethodObjectRegExps.length; i++) {
			r = r.replace(DOMMethodObjectRegExps[i], DOMMethodObjectReferenceLink)
		}
		
		for (var i=0; i<DOMPropertyObjectRegExps.length; i++) {
			r = r.replace(DOMPropertyObjectRegExps[i], DOMPropertyObjectReferenceLink)
		}
		
		return r;
	}
	
	
	function setUpJSRegexps() {
		for (var i=0; i<JSObjects.length; i++) {
			JSObjectRegExps[i] = new RegExp(
				StringHelpers.sprintf(JSObjectRegExpString, JSObjects[i]),
				"g");
		}
		
		for (var i=0; i<DOMMethodObjects.length; i++) {
			DOMMethodObjectRegExps[i] = new RegExp(
				StringHelpers.sprintf(DOMMethodObjectRegExpString, DOMMethodObjects[i]),
				"g");
		}
		
		for (var i=0; i<DOMPropertyObjects.length; i++) {
			DOMPropertyObjectRegExps[i] = new RegExp(
				StringHelpers.sprintf(DOMPropertyObjectRegExpString, DOMPropertyObjects[i]),
				"g");
		}
	}
	
	function insertLinks() {
		return;
		var h3List = document.getElementsByTagName('h3');
		
		for (var i=0; i<h3List; i++) {
			var h3 = h3List[i];
			
			
			switch(h3.innerHTML) {
				case "HTML":
					
			}
		}
	}
	
	me.init = function (e) {
		if (EventHelpers.hasPageLoadHappened(arguments)) {
			return;
		}
		setUpJSRegexps();
		transformCodeObjects();
		insertLinks();
	}
}



EventHelpers.addPageLoadEvent('documentation.init');