var LiveDOM = function() {
	var textarea = null;    // HTMLTextAreaElement
	var dom = null;         // HTMLULElement
	var iframe   = null;    // HTMLIFrameElement
	var innerhtml = null;   // HTMLElement
	var docTitle = null;    // HTMLHeadingElement
	var msgLog = null;      // MessageLog
	var parseMode = 0;
	var lastString = null;
	
	var constructor = function(input, viewer, title, html, domTree, log) {
		textarea  = input;
		iframe    = viewer;
		docTitle  = title;
		innerhtml = html;
		dom       = domTree;
		msgLog    = log;

		this.update = update;
		this.updateDOM = updateDOM;
		this.setParseMode = setParseMode;
		this.getCompatMode = getCompatMode;
	}

	function setParseMode(mode) {
		parseMode = mode;
	}

	error = function (a, b, c) {
		log.record('error: ' + a + ' on line ' + c);
	}
	
	w = function (s) {
		log.record('log: ' + s);
	}

	function update(force) {
		if (force || lastString != textarea.value) { // Check if the update is necessary
			log.clear();
			if (parseMode == LiveDOM.PARSE_MODE_BROWSER) {
				iframe.contentWindow.document.open();
				iframe.contentWindow.onerror = error;
				iframe.contentWindow.w = w;
				iframe.contentWindow.document.write(textarea.value);
				iframe.contentWindow.document.close();
				afterParse();
			} else { // LiveDOM.PARSE_MODE_HTML5
				iframe.contentWindow.onerror = error;
				iframe.contentWindow.w = w;
				window.parseHtmlDocument(textarea.value, iframe.contentWindow.document, afterParse, null);
			}
		}
	}

	function afterParse() {
		lastString = textarea.value;
		setTimeout(function(callback){
			return function() {
				callback();
			}
		}(updateDOM), 100);
	}

	function updateDOM() {
		while (innerhtml.firstChild) innerhtml.removeChild(innerhtml.firstChild);
		innerhtml.appendChild(document.createTextNode(iframe.contentWindow.document.documentElement.innerHTML));

		printDOM(dom, iframe.contentWindow.document);

		if (viewer.contentWindow.document.title)
			docTitle.textContent = viewer.contentWindow.document.title;
		else
			docTitle.textContent = "(Untitled)";
	}

	function printDOM(ul, node) {
		while (ul.firstChild) ul.removeChild(ul.firstChild);
		for (var i = 0; i < node.childNodes.length; i += 1) {
			var li = document.createElement('li');
			li.className = 't' + node.childNodes[i].nodeType;
			if (node.childNodes[i].nodeType == 10) {
				li.appendChild(document.createTextNode('DOCTYPE: '));
			}
			var code = document.createElement('code');
			code.appendChild(document.createTextNode(node.childNodes[i].nodeName));
			li.appendChild(code);
			if (node.childNodes[i].nodeValue) {
				var span = document.createElement('span');
				span.appendChild(document.createTextNode(node.childNodes[i].nodeValue));
				li.appendChild(document.createTextNode(': '));
				li.appendChild(span);
			}
			if (node.childNodes[i].attributes) {
				for (var j = 0; j < node.childNodes[i].attributes.length; j += 1) {
					if (node.childNodes[i].attributes[j].specified) {
						var attName = document.createElement('code');
						attName.appendChild(document.createTextNode(node.childNodes[i].attributes[j].nodeName));
						attName.className = 'attribute name';
						var attValue = document.createElement('code');
						attValue.appendChild(document.createTextNode(node.childNodes[i].attributes[j].nodeValue));
						attValue.className = 'attribute value';
						var att = document.createElement('span');
						att.className = 't2';
						att.appendChild(attName);
						att.appendChild(document.createTextNode('="'));
						att.appendChild(attValue);
						att.appendChild(document.createTextNode('"'));
						li.appendChild(document.createTextNode(' '));
						li.appendChild(att);
					}
				}
			}
			if (node.childNodes[i].parentNode == node) {
				if (node.childNodes[i].childNodes.length) {
					var ul2 = document.createElement('ul');
					li.appendChild(ul2);
					printDOM(ul2, node.childNodes[i]);
				}
			} else {
				li.className += ' misparented';
			}
			ul.appendChild(li);
		}
	}


	function getCompatMode() {
		return iframe.contentWindow.document.compatMode;
	}


	constructor.PARSE_MODE_BROWSER = 0
	constructor.PARSE_MODE_HTML5   = 1

	return constructor;	
}();

var MessageLog = function() {
	var output = null; // HTMLPreElement

	var constructor = function(pre) {
		output = pre;

		this.record = record;
		this.clear = clear;
		this.getMessages = getMessages;
		clear();
	}

	var messages = [];

	function record(s) {
		messages[messages.length] = s;
		if (output) {
			output.appendChild(document.createTextNode(s + "\n"));
		}
	}

	function clear() {
		messages = [];
		output.innerHTML = "";
	}

	function getMessages() {
		return messages.concat();
	}

	return constructor;	
}();

