// Server Sync Class
function LSSync(inClient)
{
this.request_queue = new Queue();
var client;								// reference to the client to which this object instance belongs.
var that = this;
var is_ready;
var request_counter;

var sendRequestTimerID;
var connectedTimerID;

var that = this;

	this.init=function(client)
	{
		this.request_queue = new Queue();
		this.client = client;
		this.is_ready = true;
		this.request_counter = 0;
	};
	
	this.IsQueueEmpty=function()
	{
		return that.request_queue.isEmpty();
	};
	
	this.CreateRequest=function(action, row)
	{
		var req = new LSSyncRequest(this.request_counter, action, row);
		this.request_counter++;
		
		if (typeof row !== 'undefined')
		{
			row_id = row.id;
			row_text = row.getAttribute('text');
		}
		else
		{
			row_id = '';
			row_text = '';
		}
		this.request_queue.enqueue(req);
	};
	
	this.SendRequestToServer=function()
	{
		if (that.is_ready && that.request_queue.getSize())
		{
//			that.ResetRequestTimeOut('clear');
//			that.ResetRequestTimeOut('set');

			that.SendRequest();
		}

		if ( that.request_queue.getSize())		// queue is NOT empty
			HasUnsavedChanges();
		else DidSaveChanges();
		
		this.sendRequestTimerID = setTimeout(that.SendRequestToServer, 1000);	// try to send next request
	};
	
	this.ResetRequestTimeOut=function(state)
	{
		switch (state)
		{
		case 'set':	this.connectedTimerID = setTimeout(that.DisconnectedFromServer, 10000);
					break;
					
		case 'clear': clearTimeout(this.connectedTimerID);
					break;
		}
	};
	
	this.DisconnectedFromServer=function()
	{
		that.is_ready = true;
		this.didShowDisconnectedMsg = true;
		ShowUserOfflineMsg();
	};
	
	this.SendRequest=function()
	{
		var required_in_play = false;									// some calls are required in Play also.
		
		if (typeof gFromPlayPage == 'undefined')
			gFromPlayPage = false;
		
		var req = this.request_queue.getOldestElement();				// get the next request to be sent to the server
		
		var params = "{}";
		var q_method = "POST";
		
		var outline_id = this.client.outliner.GetOutlineId();
		if (typeof req.GetRow() !== 'undefined')
		{
			var row = req.GetRow();
			var row_sid = row.getAttribute('_sid');
		}
			
		switch (req.GetAction())
		{
		case 'addRow':		prev_row = this.client.outliner.GetPreviousSibling(row);
							if (prev_row == false)
								prev_str = "";
							else prev_str = ", previous_row_id: '"+prev_row.getAttribute("_sid")+"'";
							
							var parent_row = row.getParent();
							if (parent_row.getId() == 'body')
								parent_row_id = -1;
							else parent_row_id = parent_row.getAttribute("_sid");
							
							endpoint = "/api/outlines/"+outline_id+"/rows/addRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "', parent_id: '" + parent_row_id + "' " + prev_str + ", text: '" + addslashes(row.getAttribute('text')) + "', status: '" + row.getAttribute('_status') + "', color: '" + row.getAttribute('_color') + "', notes: '" + addslashes(row.getAttribute('_notes')) + "', amount: '" + row.getAttribute('_amount') + "', due_date: '" + row.getAttribute('_duedate') + "', assigned_to: '" + row.getAttribute('_assigned_to') + "'}";
							break;
		
		case 'updateRow':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/updateRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "', text: '" + addslashes(row.getAttribute('text')) + "', status: '" + row.getAttribute('_status') + "', color: '" + row.getAttribute('_color') + "', notes: '" + addslashes(row.getAttribute('_notes')) + "', amount: '" + row.getAttribute('_amount') + "', due_date: '" + row.getAttribute('_duedate') + "', assigned_to: '" + row.getAttribute('_assigned_to') + "'}";
							break;
		
		case 'deleteRow':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/deleteRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
		
		case 'moveRow':		prev_row = this.client.outliner.GetPreviousSibling(row);
							if (prev_row == false)
								prev_str = "";
							else prev_str = ", previous_row_id: '"+prev_row.getAttribute("_sid")+"'";
							
							var parent_row = row.getParent();
							if (parent_row.getId() == 'body')
								parent_row_id = -1;
							else parent_row_id = parent_row.getAttribute("_sid");
							
							endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/moveRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "', parent_id: '" + parent_row_id + "' " + prev_str + "}";
							break;
		
		case 'expandRow':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/expandRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
		
		case 'collapseRow':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/collapseRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
							
	case 'expandChildren':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/expandAll.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
		
		case 'expandAll':	endpoint = "/api/outlines/"+outline_id+"/rows/expandAll.json";
							params = "{transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
		
	case 'collapseChildren':endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/collapseAll.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
		
		case 'collapseAll':	endpoint = "/api/outlines/"+outline_id+"/rows/collapseAll.json";
							params = "{transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;

		case 'setDueDate':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/updateRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "', due_date: '" + row.getAttribute('_duedate') + "'}";
							break;
		
	case 'setAttachment':	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/updateRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "', attachment: '" + row.getAttribute('_file_id') + "'}";
							break;
		
		case 'bleach':		endpoint = "/api/outlines/"+outline_id+"/rows/bleach.json";
							params = "{last_modified_on: '" + this.client.GetLastModifiedOn() + "'}";
							break;
							
		case 'assignRow'://	endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/assignRow.json";
							endpoint = "/api/outlines/"+outline_id+"/rows/"+row_sid+"/updateRow.json";
							params = "{cid: '" + row.getId() + "', transaction_id: '"+req.id+"', last_modified_on: '" + this.client.GetLastModifiedOn() + "', assigned_to: '" + row.getAttribute('_assigned_to') + "'}";
							break;
		
		default:			alert("invalid request with action: "+req.GetAction());
							break;
		}

		if (!gFromPlayPage || required_in_play)
		{
			that.ResetRequestTimeOut('clear');
			that.ResetRequestTimeOut('set');

			that.is_ready = false;
			new Ajax.Request(endpoint,
			{
				method: q_method,
				parameters: { 'params': JSON.stringify(params.evalJSON()) },
				onFailure: function() { /* do nothing */ },
				onSuccess: function(transport) {
					if (transport.getStatus() != 0)
						that.SendRequestCallBack(transport);
					else HandleDisconnection();
				}
			});
		}
		else that.request_queue.dequeue();					// remove the request from the queue as we are not going to send it
	};
	
	// handles the response from the server
	this.SendRequestCallBack=function(t)
	{
	var response = t.responseText.evalJSON();
		
		that.ResetRequestTimeOut('clear');
		HideUserOfflineMsg();
		
		if (response.status == 'error')
		{
			if (response.error == 'out of sync')
				ShowOutOfSyncMsg();
			else ShowErrorMsg('Sorry, an error has occurred. '+response.error+'.');		// alert("Sorry, an error has occurred. "+response.error);
			
			clearTimeout(this.sendRequestTimerID);			// error has happened. stop sending data to the server
		}
		else
		{
			that.request_queue.dequeue();
			
			if (response.last_modified)
				that.client.SetLastModifiedOn(response.last_modified);
			
			switch (response.method)
			{
			case 'addRow':		row_cid = response.cid;
								row_sid = response.sid;
								row = that.client.outliner.GetRowById(row_cid);
								row.setAttribute('_sid', row_sid);
								break;
			}
		}
		that.is_ready = true;
	};
	
	this.GetRowsForOutline=function()
	{
		var outline_id = this.client.outliner.GetOutlineId();
		var params = "{touch: 'yes'}";
		
		new Ajax.Request("/api/outlines/"+outline_id+"/getAllRows.json",
		{
			method: 'GET',
			parameters: { 'params': JSON.stringify(params.evalJSON()) },
			onFailure: function() { /* do nothing */ },
			onSuccess: function(transport) {
				that.GetRowsForOutlineCallBack(transport);
			}
		});
	};
	
	this.GetRowsForOutlineCallBack=function(t)
	{
		var response = t.responseText.evalJSON();
		var xmlobject;
		if (window.DOMParser)
			xmlobject = (new DOMParser()).parseFromString(response.opml, "text/xml");
		else // Internet Explorer
		{
			xmlobject=new ActiveXObject("Microsoft.XMLDOM");
			xmlobject.async="false";
			xmlobject.loadXML(response.opml); 
		}
		
		that.client.SetLastModifiedOn(response.last_modified);			// we set the last modified timestamp so that we can send it with the next API call
		that.client.outliner.LoadOPML(xmlobject.documentElement);

		this.SendRequestToServer();										// kick off the timer to send modifications to the server
		
		that.GetFilesForOutline();										// get files from server
	};
	
	this.GetFilesForOutline=function()
	{
		var outline_id = this.client.outliner.GetOutlineId();
		var params = "{outline_id: '"+outline_id+"'}";
		
		new Ajax.Request("/api/files/getInOutline.json",
		{
			method: 'GET',
			parameters: { 'params': JSON.stringify(params.evalJSON()) },
			onFailure: function() { /* do nothing */ },
			onSuccess: function(transport) {
				that.GetFilesForOutlineCallBack(transport);
			}
		});
	};
	
	this.GetFilesForOutlineCallBack=function(t)
	{
		var response = t.responseText.evalJSON();
		that.client.StoreFiles(response.files);
		
		that.client.outliner.Render();
		that.client.OutlineDidFinishLoading();

//		that.GetInvitesForOutline();									// now get outline invitees from the server
	};
	
	this.GetInvitesForOutline=function()
	{
		var outline_id = this.client.outliner.GetOutlineId();
		var params = "{}";
		
		new Ajax.Request("/api/outlines/"+outline_id+"/getInvites.json",
		{
			method: 'GET',
			parameters: { 'params': JSON.stringify(params.evalJSON()) },
			onFailure: function() { /* do nothing */ },
			onSuccess: function(transport) {
				that.GetInvitesForOutlineCallBack(transport);
			}
		});
	};
	
	this.GetInvitesForOutlineCallBack=function(t)
	{
		var response = t.responseText.evalJSON();
	//	that.client.StoreInvites(response.invites);
		that.client.OutlineDidFinishLoading();
	};
	
	this.init(inClient);
};

// Request Class
function LSSyncRequest(inId, inAction, inRow)
{
var id;
var row;
var action = '';

	this.init = function(inId, inAction, inRow)
	{
		this.id = inId;
		this.row = inRow;
		this.action = inAction;
	};
	
	this.GetAction=function()
	{
		return this.action;
	};

	this.GetRow=function()
	{
		return this.row;
	};
		
	this.init(inId, inAction, inRow);
};
