function htIRCMixins(x) {
	x.createP = function (text) {
		var p = document.createElement('p');
		p.appendChild(document.createTextNode(text));
		return p;
	}
	
	x.setP = function (name, text) {
		var p;
		if(p = document.getElementById(name)) {
			p.firstChild.nodeValue = text;
		} else {
			var p = document.createElement('p');
			p.appendChild(document.createTextNode(text));
			p.id = name;
			p.style.display = 'none';
			document.getElementsByTagName('body')[0].appendChild(p);
		}
	}

	x.getP = function (name) {
		return document.getElementById(name).firstChild.nodeValue;
	}
}

//--

function htIRCMyNick() {
	htIRCMixins(this);

	this.set = function (nick) {
		this.setP('chatnick', nick);
	}

	this.get = function () {
		return this.getP('chatnick');
	}

	this.randomize = function () {
		var n = this.get();
		n = n.substring(0, 25) + Math.floor(Math.random()*99999).toString();
		//TODO: Cleanup
		this.set(n);
		serverSendText('NICK ' + n);
	}

	if(window.htirc.initialNick) {
		this.set(window.htirc.initialNick);
	} else {
		this.set('Guest' + Math.floor(Math.random()*99999).toString());
	}
}

//--

function htIRCNamesList() {
	this.elem = document.getElementById('chatnames').cloneNode(true);
	this.elem.style.display = 'none';
	this.elem.style.className = 'nameslist';
	this.elem.id = '';
	document.getElementById('chatnamesd').appendChild(this.elem);

	this.hash = new Object;
}

htIRCNamesList.prototype.foreach = function (call) {
	var el = this.elem.firstChild;
	while(el) {
		var ret = call(el);
		if(ret) { return ret; }
		el = el.nextSibling;
	}
}

htIRCNamesList.prototype.getAlph = function (name) {
	var lname = name.toLowerCase();
	
	return this.foreach(function (el) {
		if(el.firstChild.value.toLowerCase() > lname) {
			return el;
		}
	});
}

htIRCNamesList.prototype.add = function (name) {
	var n = document.createElement('li');
	var b = document.createElement('input');
	b.type = 'button';
	b.value = name;
	b.className = 'nameslistitem';
	b.onclick = function () { openQuery(this.value); };
	n.appendChild(b);
	
	var aln = this.getAlph(name);
	if(aln) {
		this.elem.insertBefore(n, aln);
	} else {
		this.elem.appendChild(n);
	}

	this.hash[name.toLowerCase()] = n;
}

htIRCNamesList.prototype.exists = function (name) {
	return this.hash[name.toLowerCase()];
}

htIRCNamesList.prototype.del = function (name) {
	var e;

	if(e = this.hash[name.toLowerCase()]) {
		this.elem.removeChild(e);
		this.hash[name.toLowerCase()] = false;
		return true;
	}
	else {
		return false;
	}
}

htIRCNamesList.prototype.change = function(oldn, newn) {
	var e;

	if(e = this.hash[oldn.toLowerCase()]) {
		this.elem.removeChild(e);
		this.add(newn);
		this.hash[oldn.toLowerCase()] = false;
		return true;
	}
	else {
		return false;
	}
}

htIRCNamesList.prototype.addList = function (nameslist) {
	var names = nameslist.split(' ');

	for(var x in names) {
		if(names[x]) {
			var actualname;
			if(names[x].match(/^[~&@%+]/)) {
				actualname = names[x].substring(1);
			}
			else {
				actualname = names[x];
			}

			if(!this.hash[actualname.toLowerCase()]) {
				this.add(actualname);
			}
		}
	}
}

htIRCNamesList.prototype.clear = function () {
	var el;
	while(el = this.elem.firstChild) {
		this.elem.removeChild(el);
	}

	this.hash = new Object;
}

//--

function htIRCUI() {
	var sframe = new htIRCFrame('-server-');
	sframe.activate();

	document.forms.chatform.onsubmit = userTypedSomething;
	document.getElementById('chatinput').onkeydown = function (e) {
		if((e && e.keyCode == 9) || (window.event && window.event.keyCode == 9)) {
			window.htirc.frames.current.autoComplete(this);
			return false;
		}
	}
	
	document.getElementById('chatclose').onclick = function () {
		window.htirc.frames.current.destroy();
	}
}

//--

function htIRCFrameContainer() {
	this.current = undefined;
	this.list = new Object();
}

htIRCFrameContainer.prototype.get = function (name) {
	var id = name.toLowerCase();

	var ret;
	if(ret = this.list[id]) {
		return ret;
	}
	else {
		return new htIRCFrame(name);
	}
}

htIRCFrameContainer.prototype.foreach = function (f) {
	var ret;
	for(id in this.list) {
		ret = f(this.list[id]);
		if(ret) return ret;
	}
}

htIRCFrameContainer.prototype.appendAll = function (text) {
	for(id in this.list) {
		this.list[id].append(text);
	}
}

htIRCFrameContainer.prototype.updateTabs = function () {
	var tablist = document.getElementById('chatframelist');
	while(tablist.firstChild) {
		tablist.removeChild(tablist.firstChild);
	}
	
	for(id in this.list) {
		var frame = this.list[id];
		var e = document.createElement('input');
		
		e.type = 'button';
		e.frameTarget = frame;
		e.value = frame.name;
		e.onclick = function () { this.frameTarget.activate(); };
		e.activate = function () { this.className = 'activetab'; };
		e.deactivate = function () {
			if(!this.className.match(/inactivetab/)) {
				this.className = 'inactivetab';
			}
		};

		e.deactivate();
		tablist.appendChild(e);
		frame.tab = e;
	}
}

htIRCFrameContainer.prototype.exists = function (name) {
	return this.list[name.toLowerCase()];
}

//--

function htIRCFrame(name) {
	htIRCMixins(this);

	this.name = name;
	this.id = name.toLowerCase();
	
	this.elem = document.getElementById('chatbox').cloneNode(true);
	//this.elem.style.overflow = 'auto';
	this.elem.style.display = 'none';
	this.elem.className = 'chatbox';
	this.elem.id = '';
	document.getElementById('chatboxc').appendChild(this.elem);

	this.nl = new htIRCNamesList;

	window.htirc.frames.list[this.id] = this;
	window.htirc.frames.updateTabs();

	this.activate();
}

htIRCFrame.prototype.destroy = function () {
	var prev;
	
	//FIXME
	if(this.id.match(/^#/)) {
		serverSendText('PART ' + this.id);
	}

	for(x in window.htirc.frames.list) {
		if(!prev) { prev = x; }

		if(x == this.id) {
			break;
		}

		prev = x;
	}
	
	this.elem.style.display="none";	
	//FIXME?
	this.nl.elem.style.display="none";

	delete(window.htirc.frames.list[id]);
	window.htirc.frames.updateTabs();
	window.htirc.frames.list[prev].activate();

	//FIXME
	this.nl.elem.parentNode.removeChild(this.nl.elem);
	this.nl = undefined;

	this.elem.parentNode.removeChild(this.elem);
}

htIRCFrame.prototype.doScroll = function () {
	this.elem.parentNode.scrollTop = this.elem.parentNode.scrollHeight;
}

htIRCFrame.prototype.append = function (text) {
	var p = this.createP(text);
	p.className = 'chatline';

	this.elem.appendChild(p);
	this.doScroll();
}

htIRCFrame.prototype.highlight = function () {
	if(this.tab.className == 'inactivetab') {
		this.tab.className += ' highlighttab';
	}
}

htIRCFrame.prototype.activate = function () {
	for(id in window.htirc.frames.list) {
		var frame = window.htirc.frames.list[id];

		frame.elem.style.display="none";
		if(frame.nl)
			frame.nl.elem.style.display="none";
		frame.tab.deactivate();
	}

	this.elem.style.display="block";
	if(this.nl)
		this.nl.elem.style.display="block";
	
	window.htirc.frames.current = this;
	this.tab.activate();
	this.doScroll();

	//FIXME ?
	document.getElementById('chatclose').style.visibility = (this.id == '-server-' ? 'hidden' : 'inherit');

	document.forms.chatform.chatinput.focus();
}

htIRCFrame.prototype.autoComplete = function (textbox) {
	var n = /(.*(?:^| ))(.+)(\W*)$/.exec(textbox.value);
	var c = this.nl.getAlph(n[2]);
	if(c) {
		c = c.firstChild.value;
	}
	else {
		c = this.nl.elem.firstChild.firstChild.value;
	}

	if(!n[1]) c += ', ';

	textbox.value = n[1] + c;
}

//--

//htIRCDispatch

//--

function htIRC() {
	htIRCMixins(this);

	this.begin = function () {
		this.nick = new htIRCMyNick;
		this.frames = new htIRCFrameContainer;
		this.ui = new htIRCUI;
		
		createIFrame();
		hello();
	}
}

htIRC.prototype.dispatch = function (line) {
	var msgParts = /^:?(.*?)(?: :(.*))?$/.exec(line);
	if(!msgParts) return;
	msgParts.shift();
	var params = msgParts[0].split(' ');
	var msg = msgParts[1];

	if(params[1] == 'PRIVMSG') {
		var nick = maskGetNick(params[0]);
		var dst;
		if(insEqual(params[2], this.nick.get())) {
			dst = nick;
		} else {
			dst = params[2];
		}
	
		var ctcp = /^\001(.*?)(?: (.*?))?\001?$/.exec(msg);
		if(ctcp) {
			if(ctcp[1] == 'ACTION') {
				this.frames.get(dst).append('* ' + nick + ' ' + ctcp[2]);
				this.frames.get(dst).highlight();
			}
			else {
				this.frames.get('-server-').append('--- Received CTCP ' + ctcp[1] + ' from ' + nick);

				if(ctcp[1] == 'VERSION') {
					serverSendText('NOTICE ' + nick + ' :\001VERSION htIRC experimental prerelease\001');
				}
			}
		}
		else {
			this.frames.get(dst).append('<' + nick + '> ' + msg);
			this.frames.get(dst).highlight();
		}
	}
	else if(params[0] == 'PING') {
		serverSendText('PONG :' + msg);
	}
	else if(params[1] == '001') { // end of motd
		serverSendText('JOIN :' + window.htirc.initialChans);
	}
	else if(params[1] == '433') { // nickname in use
		this.nick.randomize();
	}
	else if(params[1] == '332') { // topic
		this.frames.get(params[3]).append('--- Topic for ' +
				params[3] + ' is: ' + msg);
	}
	else if(params[1] == '353') { // names response
		this.frames.get(params[4]).nl.addList(msg);
	}
	else if(params[1] == 'JOIN') {
		userJoined(maskGetNick(params[0]), msg);
	}
	else if(params[1] == 'PART') {
		userLeft(maskGetNick(params[0]), params[2], undefined, msg);
	}
	else if(params[1] == 'KICK') {
		userLeft(params[3], params[2], maskGetNick(params[0]), msg);
	}
	else if(params[1] == 'NICK') {
		var outmsg;
		if(insEqual(maskGetNick(params[0]), this.nick.get())) {
			outmsg = '--- You are now known as ' + msg;
			this.nick.set(msg);
		}
		else {
			outmsg = '--- ' + maskGetNick(params[0]) + ' is now known as ' + msg;
		}

		this.frames.foreach(function (e) {
			if(e.nl.change(maskGetNick(params[0]), msg)) {
				e.append(outmsg);
			}
		});
	}
	else if(params[1] == 'TOPIC') {
		appendChatLine('--- ' + maskGetNick(params[0]) + ' has changed the topic to: ' + msg, params[2]);
	}
	else if(params[1] == 'QUIT') {
		var outmsg = '<-- ' + maskGetNick(params[0]) + ' has quit (' + msg + ')';
		this.frames.foreach(function (frame) {
			if(frame.nl.del(maskGetNick(params[0]))) {
				frame.append(outmsg);
			}
		});
	}
	else {
		appendChatLine(line, '-server-');
	}
}

htIRC.prototype.gotDisconnected = function () {
	//FIXME
	window.htirc.frames.appendAll("Connection lost. To reconnect, reload this page.");
}

window.htirc = new htIRC;

window.htirc.oldonload = window.onload;
window.onload = function () {
	window.htirc.begin();
	if(window.htirc.oldonload) window.htirc.oldonload();
};
