if ( typeof isArray == "undefined" )
	isArray = function ( obj ) {
		return Object.prototype.toString.call ( obj ) == "[object Array]";
	}

var ChatClient = ( function ( ) {
	if ( arguments.callee.called ) return;
	arguments.callee.called = true;
	//var style = $('<style type="text/css">@import "/style/chatclient/chatclient.css";</style>').appendTo("head");
	/* */

	$(document).ready ( function ( ) {
		var link = document.createElement("link");
		link.setAttribute ( "rel", "stylesheet" );
		link.setAttribute ( "type", "text/css" );
		link.setAttribute ( "href", "/style/chatclient/chatclient.css" );
		document.getElementsByTagName("head")[0].appendChild ( link );
		link.DNE = true;
	} );
	/* */

	var $class = function ( locator, chats ) {
		if ( !window.chatClient )
			window.chatClient = this;

		if ( !arguments.length ) {
			locator = document.body;
		} else if ( locator.constructor == Chat ) {
			chats = [ locator ];
			locator = document.body;
		} else if ( isArray(locator) ) {
			chats = locator;
			locator = document.body;
		} else if ( typeof locator == "string" ) {
			locator = document.getElementById(locator);
		}
		if ( !locator || !locator.nodeName ) return null;

		var client = $('<div class="chatWindowWrapper"></div>')
			.append (
				$('<div class="chatWindow"></div>')
					.append($('<div class="chatHandle"><div><span></span>MSG Chat</div></div>'))
					.append($('<div class="chatInnards"><ul class="chats"></ul><div class="tabs"><ul></ul></div></div>'))
			)
			.appendTo ( locator )
			.css ( {
				"position" : "absolute",
				"right" : 0,
				"bottom" : "100px",
				"margin" : "0 5px 5px 0"
			} )[0];

		$(window).resize ( function ( ) {
			var margin = $("#portal_middle").outerWidth() - $("#portal_body").outerWidth() + 5;
			//console.log ( "Chat Offset: " + $("#portal_middle").outerWidth() + " - " + $("#portal_body").outerWidth() + " + " + 5 + " = " + margin );
			client.style.marginRight = margin + "px";
		} ).resize();

		var handle = $('.chatHandle',client)
			.mousedown ( function ( initial_event ) {

				var stop = function ( e ) {
					e.cancel = true;
					e.cancelBubble = true;
					e.returnValue = false;
					if ( e.stopPropagation )
						e.stopPropagation();
					if ( e.preventDefault )
						e.preventDefault();
					return false;
				}

				if ( !initial_event ) initial_event = window.event;

				var initial_position = {
					x : parseFloat ( client.style.right ),
					y : parseFloat ( client.style.bottom )
				};

				var move = function ( current_event ) {
					if ( !current_event ) current_event = window.event;
					var deltaX = -(current_event.clientX - initial_event.clientX);
					var deltaY = -(current_event.clientY - initial_event.clientY);

					client.style.right = Math.max ( 0, initial_position.x + deltaX ) + "px";
					client.style.bottom = Math.max ( 0, initial_position.y + deltaY ) + "px";

					return stop ( current_event );
				}

				var release = function ( event ) {
					$(document.body)
						.unbind ( "mousemove", move )
						.unbind ( "mouseup", release );
					return stop ( event );
				}

				$(document.body)
					.mousemove ( move )
					.mouseup ( release );

				return stop ( initial_event );
			} );
		this._node = client;

		this._tabs = {}
		this._currentChat = null;

		if ( !chats ) chats = new Chat ( "Untitled", this );
	}
	$class.prototype.addChat = function ( chat ) {
		if ( isArray(chat) ) {
			this.addChat.apply ( this, chat );
			return;
		} else {
			for ( var i = 0; i < arguments.length; i ++ ) {
				( function ( chat ) {
					if ( chat.constructor == Chat ) {
						if ( !chat.id ) chat.id = "chat_"+(""+Math.random()).substr(2);
						var tab = $('<li>' + chat.getTitle() + '</li>')
							.appendTo ( $('.tabs ul',this._node) )
							.click ( function ( ) {
								chat.focus();
							} )
							[0];
						this._tabs[chat.id] = tab;
						$(chat._node).css ( "display", "none" );
						if ( !this._currentChat ) this.selectChat ( chat );
					}
				} ).call ( this, arguments[i] );
			}
			var count = 0;
			for ( chat in this._tabs )
				count ++;
			if ( count )
				$(this._node).show();
			var height = $('.tabs',this._node).css ( { 'display' : ( count == 1 ? "none" : "block" ) } ).outerHeight();
			var bottom = parseFloat ( $(this._node).css("bottom") );
			$(this._node).css ( "bottom", bottom + ( count == 1 ? 0 : parseInt ( height ) ) );
			this[(this._drawerClosed?"hide":"show")+"Drawer"](true);
		}
	}
	$class.prototype.removeChat = function ( chat ) {
		if ( ( chat.constructor == Chat ) && this._tabs[chat.id] ) {
			$(this._tabs[chat.id]).remove();
			delete this._tabs[chat.id];
			$(chat._node).remove();
			if ( !$('.tabs ul li',this._node).length )
				$(this._node).hide();
			for ( tab in this._tabs ) {
				$(this._tabs[tab]).click();
			}
		}
	}
	$class.prototype.selectChat = function ( chat ) {
		if ( ( chat.constructor == Chat ) && this._tabs[chat.id] ) {
			$('.tabs ul li',this._node).removeClass ( 'selected' );
			$('.chats li.chat',this._node).css ( "display", "none" );
			$(this._tabs[chat.id]).removeClass( 'flash' ).addClass ( 'selected' );
			$(chat._node).css ( "display", "block" );
			this._currentChat = chat;
		}
	}
	$class.prototype.hideDrawer = function ( immediate ) {
		this._drawerClosed = true;
		$('.drawer',this._node)
			.stop()
			.animate ( { width : 0 }, {
				duration : immediate ? 1 : 250,
				complete : function ( ) {
					$(this).css ( "display", "none" );
				}
			} );
		$('.chatWindow',this._node)
			.addClass ( "no-drawer" )
			.stop()
			.animate ( { right : 0 }, immediate ? 1 : 250 );
	}
	$class.prototype.showDrawer = function ( immediate ) {
		this._drawerClosed = false;
		var drawerWidth = 150;
		var difference = $('.drawer',this._node)
			.css("display","block")
			.stop()
			.animate ( { width : drawerWidth }, immediate ? 1 : 250 )
			.css ( "paddingRight" );
		$('.chatWindow',this._node)
			.removeClass ( "no-drawer" )
			.stop()
			.animate ( { right : drawerWidth + ( parseFloat ( difference ) || 12 ) }, immediate ? 1 : 250 );
	}
	$class.prototype.toggleDrawer = function ( immediate ) {
		this[(this._drawerClosed?"show":"hide")+"Drawer"](immediate);
	}
	return $class;
} )();

var Chat = ( function ( ) {

	var stopped = false;

	var imMessageAttention = { play : function ( ) { } };

	SoundStage.whenReady ( function ( ) {
		var sound = new SoundStage.Sound( '/audio/im_message_attention.mp3', {
			noSubtitles : true,
			id : "attention-" + (""+Math.random()).substr(2),
			onLoad : function ( ) {
				imMessageAttention = this;
			}
		} );
	} );

	var getDelay = function ( str ) {
		if ( !arguments.callee.called ) {
			arguments.callee.called = true;
			return 1;
		}
		if ( document.location.search.substr(1) === "test" )
			return 1;
		var length = (str||"").replace(/<a[^>]*>.*?<\/a>/ig,'link').length;
		return 500 // half a second
		//		+ ( Math.random() * 500 ) // some fraction of half a second
				+ ( 75 * length ) // 3/4 of the length * 1/10 of a second
		//		+ ( Math.random() * 100 * length / 4 ) // 1/4 of the length * some fraction of 1/10 of a second
		;
	}
	var flash = function ( chat ) {
		var on = false;
		var interval = setInterval ( function ( ) {
			$('.input',chat._node)[((on=!on)?'add':'remove')+"Class"]('flash');
			if ( chat._client && ( chat._client._currentChat != chat ) )
				$(chat._client._tabs[chat.id])[on?"addClass":"removeClass"]("flash");
		}, 500 );
		return {
			stop : function ( ) {
				clearInterval ( interval );
				$('.input',chat._node).removeClass("flash");
				$(chat._client._tabs[chat.id]).removeClass("flash").css("opacity",1);
			}
		}
	}
	var converse = function ( chat, conversation, choiceCB, eocCB, noLag ) {
		if ( stopped ) return;
		var next = conversation.next(noLag);
		if ( next ) {
			if ( next.wait ) {
				if ( noLag || chat.getWaitPoint ( next.wait ) === true ) {
					chat.setWaitPoint ( next.wait, null )
					// wait point has been resumed already, so we ignore this and carry on
					if ( noLag ) {
						converse ( chat, conversation, choiceCB, eocCB, noLag );
					} else {
						next.onWait ( function ( ) {
							converse ( chat, conversation, choiceCB, eocCB, noLag );
						}, chat, conversation );
					}
				} else if ( !noLag && typeof next.wait == "number" ) {
					setTimeout ( function ( ) {
						next.onWait ( function ( ) {
							converse ( chat, conversation, choiceCB, eocCB, noLag );
						} )
					}, next.wait );
				} else {
					// save the function to call to continue the conversation for when resume is called
					chat.setWaitPoint ( next.wait, function() {
						next.onWait ( function ( ) {
							converse ( chat, conversation, choiceCB, eocCB, noLag );
						}, chat, conversation );
					} );
				}
			} else if ( isArray ( next.choices ) ) {
				var choices = [];
				for ( var i = 0; i < next.choices.length; i ++ ) {
					( function ( i ) {
						choices.push ( {
							value : next.choices[i].line,
							toString : function ( ) { return next.choices[i].line; },
							choose : function ( ) {
								chat.showMessage ( next.choices[i].actor, next.choices[i].line );
								next.choices[i].choose();
								if ( !noLag && typeof next.choices[i].meta.onSay == "function" ) {
									next.choices[i].meta.onSay ( function ( ) {
										converse ( chat, conversation, choiceCB, eocCB, noLag );
									}, chat, conversation );
								} else {
									converse ( chat, conversation, choiceCB, eocCB, noLag );
								}
							}
						} );
					} )( i );
				}
				choiceCB ( choices );
			} else {
				setTimeout ( function ( ) {
					if ( next.meta && next.meta.audio ) {
						chat._contacts[next.actor].startAudio();
						// TO DO: hook into SoundStage...
						// for now, just timeout...
						setTimeout ( function ( ) {
							chat._contacts[next.actor].stopAudio();
							chat.showMessage ( next.actor, next.line );
							if ( !noLag && typeof next.meta.onSay == "function" ) {
								next.meta.onSay ( function ( ) {
									converse ( chat, conversation, choiceCB, eocCB, noLag );
								}, chat, conversation );
							} else {
								converse ( chat, conversation, choiceCB, eocCB, noLag );
							}
						}, noLag ? 0 : ( next.meta.delay != undefined ? next.meta.delay : getDelay ( next.line ) ) );
					} else {
						if ( !noLag && chat._contacts[next.actor].startWriting )
							chat._contacts[next.actor].startWriting();
						if ( next.actor == "_user_" ) {
							if ( noLag || document.location.search.substr(1) === "test" ) {
								chat.showMessage ( next.actor, next.line );
								converse ( chat, conversation, choiceCB, eocCB, noLag );
							} else {
								var chars = [];
								var nodes = $('<div/>').html(next.line)[0].childNodes;
								for ( var i = 0; i < nodes.length; i ++ ) {
									if ( nodes[i].nodeType == 1 ) {
										chars.push ( nodes[i] );
									} else {
										chars.push.apply ( chars, nodes[i].nodeValue.split("") );
									}
								}
								var value = "";
								setTimeout ( function ( ) {
									var i = setInterval ( function ( ) {
										var c = chars.shift();
										if ( !c ) {
											setTimeout ( function ( ) {
												$('div.input textarea',chat._node).val("");
												chat.showMessage ( next.actor, next.line );
												if ( typeof next.meta.onSay == "function" ) {
													next.meta.onSay ( function ( ) {
														converse ( chat, conversation, choiceCB, eocCB, noLag );
													}, chat, conversation );
												} else {
													converse ( chat, conversation, choiceCB, eocCB, noLag );
												}
											}, 500 );
											clearInterval ( i );
										} else {
											value += c.nodeType ? ( c.title || c.alt || c.innerHTML ) : c;
											$('div.input textarea',chat._node).val ( value );
										}
									}, 100 );
								}, 250 );
							}
						} else {
							setTimeout ( function ( ) {
								if ( !noLag && chat._contacts[next.actor].stopWriting )
									chat._contacts[next.actor].stopWriting();
								//chat.status.innerHTML = "";
								chat.showMessage ( next.actor, next.line );
								if ( !noLag && typeof next.meta.onSay == "function" ) {
									next.meta.onSay ( function ( ) {
										converse ( chat, conversation, choiceCB, eocCB, noLag );
									}, chat, conversation );
								} else {
									converse ( chat, conversation, choiceCB, eocCB, noLag );
								}
							}, noLag ? 0 : ( (next.meta||{}).delay != undefined ? next.meta.delay : getDelay ( next.line ) ) );
						}
					}
				}, noLag ? 0 : getDelay() );
			}
		} else {
			if ( isFunction ( eocCB ) ) {
				eocCB ( conversation );
			}
		}
	}
	var $class = function ( title, client ) {
		title = title || "Untitled";
		this._title = title;
		this._waitPoints = {};
		var clone = null;
		if ( !client ) {
			client = new ChatClient ( this );
		} else if ( client.constructor == Chat ) {
			clone = client;
			client = client._client;
		} else if ( typeof client == "string" ) {
			client = new ChatClient ( client, this );
		}
		if ( !client || ( client.constructor != ChatClient ) ) return null;

		this._client = client;
		var chat = $('<li class="chat"></li>')
			.append($('<span class="drawer_toggle"></span>'))
			.append($('<div class="title"></div>'))
			.append($('<div class="messages"><ul></ul></div>'))
			.append($('<div class="input"><textarea></textarea><input type="button" class="button" value="Send"></div>'))
			.append($('<div class="drawer"></div>'))
			.appendTo ( $('ul.chats',client._node) )[0];
		$('.drawer_toggle',chat).click ( function ( ) {
			client.toggleDrawer();
		} );
		$('.input',chat).css('paddingRight',$('input.button',chat).outerWidth() + 10).click(function(){
			$('textarea',this)[0].focus();
		});
		$('input.button',chat).attr('disabled',true);
		$('textarea',chat).attr('readonly','readonly');
		this._node = chat;
		$('div.drawer',chat)[0].innerHTML = '<div class="drawerInner"><div class="people"><ul></ul><div class="user"><div><img src="/images/chat/im-icon.jpg"> Anonymous</div></div></div></div></div>';
		client.addChat ( this );
		this.setTitle ( title );
		if ( clone ) {
			var user = clone.getUser();
			this.setUser ( user.name, user.icon );
		}
	}

	$class.stop = function ( ) {
		stopped = true;
	}

	$class.prototype.getWaitPoint = function ( key ) {
		return this._waitPoints[key];
	}
	$class.prototype.setWaitPoint = function ( key, value ) {
		this._waitPoints[key] = value;
	}
	$class.prototype.getClient = function ( ) {
		return this._client;
	}
	$class.prototype.setTitle = function ( title ) {
		$('.title',this._node).html ( title );
		if ( title.length > 10 )
			title = title.substr(0,7) + "&hellip;";
		$(this._client._tabs[this.id]).html ( title );
	}
	$class.prototype.getTitle = function ( ) {
		return $('.title',this._node).html();
	}
	$class.prototype.setUser = function ( name, image ) {
		this.addContact ( "_user_", {
			name : name,
			icon : image || Portal.getMetaData('user').image
		}, true );
	}
	$class.prototype.getUser = function ( ) {
		return {
			icon : $('.people .user div img',this._node).attr("src"),
			name : $('.people .user div',this._node).text()
		}
	}
	$class.prototype.addContact = function ( id, info, noNotification ) {
		if ( !this._contacts ) this._contacts = {};
		if ( !info ) info = {};
		if ( id == "_user_" ) {
			info.icon = info.icon || Portal.getMetaData('user').image;
			info.name = info.name || Portal.getUserName();
			$('.people .user div',this._node).html ( '<img src="' + ( info.icon || "/images/chat/im-icon.jpg" ) + '" width="48" height="48"> ' + info.name );
			info.startWriting = info.stopWriting = info.startAudio = info.stopAudio = function ( ) { }
		} else {
			if ( !info.hidden ) {
				var c = document.createElement('li');
				c.innerHTML = '<img src="' + ( info.icon || "/images/chat/im-icon.jpg" ) + '"> ' + ( info.name || id ); // + '<div class="audio"><span><span></span></span></div>';
				$('.people ul',this._node)[0].appendChild(c);
				if ( info.audio ) c.className = "has-audio";
				if ( !this._contacts ) this._contacts = {};
				info.node = c;
				if ( this._contacts[id] && this._contacts[id].node && this._contacts[id].node.parentNode )
					this._contacts[id].node.parentNode.removeChild ( c, this._contacts[id].node );
				info.startWriting = function ( ) {
					$(c).addClass ( "writing" );
				}
				info.stopWriting = function ( ) {
					$(c).removeClass ( "writing" );
				}
				var eqInterval;
				info.startAudio = function ( ) {
					$(c).addClass ( "audio-active" );
					eqInterval = setInterval ( function ( ) {
						$('.audio span span',c).css('width',(15+parseInt(70*Math.random()))+"%");
					}, 100 );
				}
				info.stopAudio = function ( ) {
					$(c).removeClass ( "audio-active" );
					clearInterval ( eqInterval );
				}
			}
		}
		this._contacts[id] = info;
		if ( !noNotification )
			this.showMessage ( '_system_', info.name + ' joined the chat' );
	}
	$class.prototype.removeContact = function ( id, noNotification ) {
		if ( !this._contacts ) this._contacts = {};
		var info = this._contacts[id];
		delete this._contacts[id];
		if ( !info || !info.node ) return;
		if ( info.node.parentNode )
			info.node.parentNode.removeChild ( info.node );
		if ( !noNotification )
			this.showMessage ( '_system_', info.name + ' left the chat' );
	}
	$class.prototype.setContacts = function ( contacts ) {
		contact = contacts || {};
		this._contacts = {};
		$('.people ul li',this._node).remove();
		for ( var id in contacts ) {
			try {
				this.addContact ( id, contacts[id], true );
			} catch ( e ) {
				/*
				var a = [];
				for ( var i in e )
					a.push ( i + ": " + e[i] );
				alert ( a.join("\n") );
				*/
			}
		}
	}
	$class.prototype.getContacts = function ( ) {
		return this._contacts;
	}
	$class.prototype.select = function ( ) {
		this._client.selectChat ( this );
	}
	$class.prototype.showMessage = function ( author, message ) {
		var is_user = ( author == "_user_" );
		var is_system = ( author == "_system_" );
		author = (this._contacts||{})[author] || {name:author};
		var messages = $(".messages",this._node)[0];
		var atBottom = ( messages.scrollTop >= ( messages.scrollHeight - messages.offsetHeight + 1 ) );
		//console.log ( messages.scrollTop + ", " + messages.scrollHeight + ", " + messages.offsetHeight + ", " + ( messages.scrollHeight - messages.offsetHeight + 1 ) + ", " + atBottom );
		var now = new Date();
		var timestamp = sprintf ( "%d:%02d", now.getHours(), now.getMinutes() );

		var item = document.createElement("li");
		if ( is_user ) item.className = "user";
		if ( is_system ) item.className = "system";

		var a = document.createElement("p");
		if ( is_system ) {
			a.innerHTML = '<span>' + message + '</span><span class="split"> - </span><span class="timestamp">' + timestamp + '</span>';
		} else {
			a.innerHTML = '<span class="name">' + author.name + '</span><span class="split"> - </span><span class="timestamp">' + timestamp + '</span>';
		}
		item.appendChild ( a );

		if ( !is_system ) {
			var m = document.createElement("blockquote");
			$(m).html ( message );
			//m.appendChild ( document.createTextNode ( message ) );
			item.appendChild ( m );
		}

		$('.messages ul',this._node).append ( item );

		if ( this._client && ( this._client._currentChat != this ) )
			$(this._client._tabs[this.id]).addClass("flash");

		if ( atBottom ) {
			messages.scrollTop += messages.scrollHeight + 9999;
		}
	}
	$class.prototype.play = function ( conversation, onEnd ) {
		this.setContacts ( conversation.actors() );
		if ( conversation.getTitle() )
			this.setTitle ( conversation.getTitle() );
		var self = this;
		var textarea = $('textarea',this._node)[0];
		var button = $('input.button',this._node)[0];
		var messages = $('.messages',this._node)[0];
		converse ( this, conversation, function ( choices ) {
			var blur_timer;
			var flasher = flash ( self );

			if ( self._client && ( self._client._currentChat != self ) ) {
				imMessageAttention.play();
			}

			textarea.blur();
			var options = document.createElement("div");
			options.onmousedown = function ( ) {
				setTimeout ( function ( ) {
					clearTimeout ( blur_timer );
				}, 10 );
				textarea.focus();
			}
			options.className = "chatInputOptions";
			var list = document.createElement("ul");
			options.appendChild ( list );
			for ( var i = 0; i < choices.length; i ++ ) {
				( function ( choice, i ) {
					var item = document.createElement("li");
					var value = choice.toString().replace(/<\/?(a)[^>]*>/g,'');
					var value_notags = value.replace(/<[^>]*>/g,'').replace(/&(\w+|#\d+);/ig,'');
					item.onmousedown = function ( ) {
						setTimeout ( function ( ) {
							clearTimeout ( blur_timer );
						}, 10 );
						return false;
					}
					item.onmouseover = function ( ) {
						this.className = "hover";
					}
					item.onmouseout = function ( ) {
						this.className = "";
					}
					item.onclick = function ( ) {
						textarea.value = value_notags;
						textarea.style.height = null;
						var heightDiff = textarea.scrollHeight - textarea.offsetHeight;
						if ( heightDiff > 0 ) {
							textarea.style.height = textarea.scrollHeight + "px";
							messages.style.height = null;
							messages.style.height = ( messages.offsetHeight - heightDiff ) + "px";
						} else {
							messages.style.height = null;
						}
						options.parentNode.removeChild ( options );
						button.disabled = false;
						button.focus();
						button.onclick = function ( ) {
							textarea.style.height = null;
							messages.style.height = null;
							textarea.onfocus = null;
							this.disabled = true;
							this.blur();
							textarea.value = "";
							choice.choose();
						}
						this.className = "";
						return false;
					}
					item.innerHTML = value;
					list.appendChild ( item );
				} ) ( choices[i], i );
			}
			textarea.onfocus = function ( ) {
				flasher.stop();
				$('.input',self._node)[0].parentNode.appendChild ( options );
			}
			textarea.onblur = function ( ) {
				blur_timer = setTimeout ( function ( ) {
					if ( options.parentNode )
						options.parentNode.removeChild ( options );
				}, 50 );
			}
		}, function ( ) {
			if ( typeof onEnd == "function" )
				onEnd();
		} );
	}
	$class.prototype.resume = function ( key ) {
		// call the function to resume if there is one
		// if there isn't, then this wait point will just be skipped when it's found
		if ( typeof this._waitPoints[key] == "function" ) {
			var fn = this._waitPoints[key];
			this._waitPoints[key] = false;
			//delete this._waitPoints[key];
			fn();
		} else {
			this._waitPoints[key] = true;
		}
	}
	$class.prototype.load = function ( conversation, cb ) {
		if ( !conversation ) return;
		this.setContacts ( conversation.actors() );
		if ( conversation.getTitle() )
			this.setTitle ( conversation.getTitle() );
		var self = this;
		$('.messages ul',this._node).css('display','none');
		converse ( this, conversation, function ( choices ) {
			choices[Math.floor(choices.length*Math.random())].choose();
		}, function ( ) {
			setTimeout ( function ( ) {
				$('.messages ul',self._node).css('display','');
				var m = $('.messages',self._node)[0];
				m.scrollTop = m.scrollHeight;
				if ( typeof cb == "function" ) cb();
			}, 250 );
		}, true );
	}
	$class.prototype.focus = function ( ) {
		this._client.selectChat ( this );
		var messages = $(".messages",this._node)[0];
		messages.scrollTop = messages.scrollHeight;
	}
	$class.prototype.close = function ( ) {
		this._client.removeChat ( this );
	}
	return $class;
} )();

