/* Copyright (c) 2007 Infoteria Corp USA. All rights reserved. */

ChatSystem = Class.create();
ChatSystem.prototype = {
	initialize: function (options)
	{
		this.options = options;
		this.chatserver = this.options.chatserver;
		this.prefix = Math.round(Math.random() * 100) + '.';
		this.emptyFunction = function() {};
		this.observer = new RPCTransport();
		this.saying = false;
		this.connected = false;
		this.msgId = 1;
	},

	enter: function()
	{
	  new Ajax.Request(this.options.paths.enter, {
			parameters: 'ticket=' + this.options.clientTicket + '&counter=' + this.options.counter + '&noCache=' + new Date().getTime(),
	    onSuccess: this.enterComplete.bind(this),
	    onFailure: this.enterFailure.bind(this),
	    onException: this.onException.bind(this)});
	},
	
	enterComplete: function(t)
	{
	  var responseObject = this.evalJSON(t);
	  
		if (responseObject.reload)
		{
			(this.options.notifyReload || this.emptyFunction)();
			return;
		}
		
	  this.connected = true;
	  (this.options.notifyConnected || this.emptyFunction)();
	  
		if (responseObject.redir)
		{
			this.chatserver = responseObject.redir;
			this.enter();
		}
		else
		{
			if (responseObject.server)
			{
				this.chatserver = responseObject.server;
			}
			
			(this.options.enterComplete || this.emptyFunction)(responseObject);

			if (responseObject.roster)
			{
				(this.options.updateRoster || this.emptyFunction)(responseObject.roster);
			}
			
			if (responseObject.msgs)
			{
				this.updateMessages(responseObject.msgs, false, false);
			}
			
			if (responseObject.handle)
			{
				this.options.handle = responseObject.handle;
			}

			(this.options.notifyInitialHandle || this.emptyFunction)(responseObject.handle);
			
			if (this.options.paths.ping)
			{
			  if (this.pinger)
			  {
			    this.pinger.start();
			  }
			  else
			  {
			    this.pinger = new StoppablePeriodicalExecuter(this.ping.bind(this), this.options.pingTime);
			  }
			}
						
			this.observe();
		}
	},
	
	enterFailure: function(t)
	{
	  this.rpcFailure(t);
	},

	get: function()
	{
	  new Ajax.Request(this.options.paths.get, {
			parameters: 'ticket=' + this.options.clientTicket + '&counter=' + this.options.counter + '&noCache=' + new Date().getTime(),
	    onSuccess: this.getComplete.bind(this),
	    onFailure: this.getFailure.bind(this),
	    onException: this.onException.bind(this)});
	},
	
	getComplete: function(t)
	{
	  this.processEvents(this.evalJSON(t));
	  this.observe();
	},
	
	getFailure: function(t)
	{
	  this.rpcFailure(t);
	},
	
	say: function(message)
	{
	  var id = this.msgId++;
		this.updateMessages([{"client" : this.options.clientId, "id" : 0, "localId" : id, "hdl" : this.options.handle, "txt" : message, "type" : "user", "ts" : new Date().toISO8601String()}], true, false);
		this.doSay(id, message);
	
	},
	
	pm: function(message, target_client_id, target_handle, target_image)
	{
	  var id = this.msgId++;
    this.updateMessages([{"client" : this.options.clientId, "id" : 0, "localId" : id, "hdl" : this.options.handle, "txt" : message, "type" : "private", "ts" : new Date().toISO8601String(), "target_client_id" : target_client_id, "target_handle" : target_handle, "target_image" : target_image}], true, false);	  
    this.doSay(id, message, target_client_id, target_handle);
	},
	
	doSay: function(id, message, target_client_id, target_handle)
	{
	  if (this.saying)
	  {
	    this.sayQueue = this.sayQueue || [];
	    this.sayQueue.push({ "id" : id, "txt" : message, "target_client_id" : target_client_id, "target_handle" : target_handle });
	  }
	  else
	  {
	    this.saying = true;
	    this._say(id, message, target_client_id, target_handle);
    }
	},
	
	_say: function(id, message, target_client_id, target_handle)
	{
	  var parameters = 'ticket=' + this.options.clientTicket + '&id=' + this.uencode(id) + '&msg=' + this.uencode(message) + '&noCache=' + new Date().getTime();
	  
	  if (target_client_id != null)
	  {
	    parameters += "&occupant_id=" + target_client_id + "&target_handle=" + target_handle;
	  }
	  
	  new Ajax.Request(this.options.paths.say, {
      parameters: parameters,
      onSuccess: this.sayComplete.bind(this),
      onFailure: this.sayFailure.bind(this),
	    onException: this.onException.bind(this)});
	},
	
	sayComplete: function(t)
	{
	  var responseObject = this.evalJSON(t);
	  
		(this.options.sayComplete || this.emptyFunction)(responseObject);
		
		if (this.sayQueue && this.sayQueue.length > 0)
		{
		  var queueItem = this.sayQueue.shift();
		  this._say(queueItem.id, queueItem.txt, queueItem.target_client_id, queueItem.target_handle);
		}
		else
		{
		  this.saying = false;
		}
	},
	
	sayFailure: function(t)
	{
	  this.saying = false;
	  this.rpcFailure(t);
	},
	
	sethandle: function(handle)
	{
	  new Ajax.Request(this.options.paths.sethandle, {
      parameters: 'ticket=' + this.options.clientTicket + '&handle=' + this.uencode(handle) + '&noCache=' + new Date().getTime(),
	    onSuccess: this.setHandleComplete.bind(this),
	    onFailure: this.setHandleFailure.bind(this),
	    onException: this.onException.bind(this)});
	},
	
	setHandleComplete: function(t)
	{
	  var responseObject = this.evalJSON(t);
	  
	  this.options.handle = responseObject.handle;
	  (this.options.setHandleComplete || this.emptyFunction)(responseObject);
	},
	
	setHandleFailure: function(t)
	{
	  this.rpcFailure(t);
	},
	
	observe: function()
	{
		this.observer.request(this.getChatserverUrl(this.options.paths.observe), {
				parameters: 'ticket=' + this.options.clientTicket + '&counter=' + this.options.counter,
				onSuccess: this.observeComplete.bind(this),
				onFailure: this.observeFailure.bind(this),
				timeout: this.options.observeTimeout || 100000});
	},
	
	observeComplete: function(t)
	{
	  if (this.processEvents(this.observer.responseObject))
	  {
	    this.get();
	  }
	  else if (this.connected)
	  {
	    this.observe();
	  }
	  else
	  {
	    this.resetConnection();
	  }
	},
	
	observeFailure: function(t)
	{
	  this.rpcFailure(t);
	},
	
	ping: function()
	{
    new Ajax.Request(this.options.paths.ping, {
      parameters: 'ticket=' + this.options.clientTicket + '&noCache=' + new Date().getTime(),
      onFailure: this.pingFailure.bind(this),
      onException: this.onException.bind(this)});
	},
	
	pingFailure: function(t)
	{
	  this.rpcFailure(t);
	},
	
	setsound: function(enabled)
	{
	  new Ajax.Request(this.options.paths.setsound, {
      parameters: 'ticket=' + this.options.clientTicket + '&enabled=' + enabled + '&noCache=' + new Date().getTime(),
	    onSuccess: this.setSoundComplete.bind(this),
	    onFailure: this.setSoundFailure.bind(this),
	    onException: this.onException.bind(this)});
	},
	
	setSoundComplete: function(t)
	{
	},
	
	setSoundFailure: function(t)
	{
	},
	
	exit: function()
	{
	  if (this.connected)
	  {
      this.observer.abort();

  	  new Ajax.Request(this.options.paths.exit, {
        parameters: 'ticket=' + this.options.clientTicket + '&noCache=' + new Date().getTime(),
        onSuccess: this.exitComplete.bind(this)
		  });
	  }
	  else
	  {
	    this.exitComplete();
	  }
	},
	
	exitComplete: function(t)
	{
	  var responseObject = this.evalJSON(t);
	  
		(this.options.exitComplete || this.emptyFunction)(responseObject);
		
		if (this.connected)
		{
		  this.connected = false;
		  (this.options.notifyDisconnected || this.emptyFunction)();
		}
	},
	
	processEvents: function(responseObject)
	{
	  	  if (responseObject.reload)
		{
		  			(this.options.notifyReload || this.emptyFunction)();
			return;
		}
		
		if (responseObject.get)
	  {
		  	    return true;
	  }
	  
	  if (responseObject.roster)
		{
		  			(this.options.updateRoster || this.emptyFunction)(responseObject.roster);
		}
		
		if (responseObject.msgs)
		{
			this.updateMessages(responseObject.msgs, true, true);
		}
		
		if (responseObject.left)
		{
		  		  this.connected = false;
		  (this.options.notifyDisconnected || this.emptyFunction)();
		}
		
		if (responseObject.counter)
		{
		  		  this.options.counter = responseObject.counter;
		}

	  		return false;
	},
	
	rpcFailure: function(t)
	{
	  var wasConnected = this.connected;
	  this.connected = false;
		(this.options.rpcFailure || this.emptyFunction)(this.rpc);
		
				
		if (wasConnected)
	  {
  	  this.observer.abort();
	    (this.options.notifyDisconnected || this.emptyFunction)();
	  }

    this.resetConnection();
	},
	
	updateMessages: function(msgs, highlight, ignoreLocal)
	{
		if (msgs)
		{
		  			(this.options.beginNotifyMessages || this.emptyFunction)();
			
			var lowId = this.options.counter;

			for (var i = 0; i < msgs.length; i++)
			{
				if (msgs[i].id == null || msgs[i].id == 0 || msgs[i].id > lowId)
				{
				        		  if (!(this.options.suppressSysmsg && msgs[i].type == 'system'))
      		  {
					    (this.options.notifyMessage || this.emptyFunction)(msgs[i], !(ignoreLocal && msgs[i].client == this.options.clientId) || msgs[i].type == "system", highlight);
  				  }
				}
							}

			(this.options.endNotifyMessages || this.emptyFunction)();
		  		}
	},
	
	resetConnection: function()
	{
	  if (this.pinger)
	  {
	    this.pinger.stop();
	  }
	  
	  this.timeout = setTimeout(this.enter.bind(this), 5000);
	},
	
	uencode: function(str) 
	{
	    return encodeURIComponent(str).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F');
	},
	
	getChatserverUrl: function(path)
	{
		return 'http://' + (this.options.useRandomPrefix ? this.prefix : "") + this.chatserver + path;
	},
	
	evalJSON: function(t)
	{
	  return eval('(' + t.responseText + ')');
	},
	
	onException: function(t, exc)
	{
	  this.rpcFailure('onException', t);
	}
}