/*

Author: Johnc
Start Date: 30/10/06

*/

var SERILAIZER =
{
	id : "Da Serializer",

	//Run-time properties
	dataToSerializeArray : new Array(),

/* ============================ START CONSTANTS ============================ */
	// Constant base values can be found in serializer_constants.js

	// typeof variables
	UNDEFINED : CONST_UNDEFINED,
	STRING : CONST_STRING,
	NUMBER : CONST_NUMBER,
	BOOLEAN : CONST_BOOLEAN,
	FUNCTION : CONST_FUNCTION,

	// instanceof vaiables
	OBJ_NULL : CONST_OBJ_NULL,
	OBJ_DATE : CONST_OBJ_DATE,
	OBJ_ARRAY : CONST_OBJ_ARRAY,
	OBJ_OBJECT : CONST_OBJ_OBJECT,
	OBJ_STRING : CONST_OBJ_STRING,
	OBJ_NUMBER : CONST_OBJ_NUMBER,
	OBJ_BOOLEAN : CONST_OBJ_BOOLEAN,
/* ============================= END CONSTANTS ============================= */




/* ============================= START BUILD PARAMS =========================== */
	// Used to switch on/off the escaping of text based data.
	escapeDataStrings : CONST_ESCAPE_STRING_BASED_DATA,
	// Used to switch on/off the serializeing of any code found attached to an object.
	serializeFunctions : CONST_SERIALIZE_FUNCTIONS,

	// Used to check the length of the returned data string.
	maxStringLength : MAX_STRING_LENGTH,
/* ============================= END BUILD PARAM's ============================ */




/* ============================ START PUBLIC METHODS ============================ */
	// Adds the named object to the this.dataToSerializeArray or updates it if
	// it found an item with that name. The update produces a warning as it may
	// indicate data being over-written.
	addItem : function (whatName, whatItem)
	{
		if (!this.dataToSerializeArray[whatName])
		{
			this.dataToSerializeArray[whatName] = whatItem;
			this.debugAlert("ADDED new high level item to repository, name: " + whatName , 1, whatItem, false);
		}
		else
		{
			this.dataToSerializeArray[whatName] = whatItem;
			this.debugAlert("UPDATED new high level item to repository, name: " + whatName , 2, whatItem, false);
		}
	},

	// Removes the specified item from the data repository, useful for serializing
	// an object then getting rid of it afterwards so it doesn't appear in future
	// data streams.
	removeItem : function (whatName, whatItem)
	{
		if (!this.dataToSerializeArray[whatName])
		{
			this.debugAlert("FAILED to remove name: " + whatName + ", item was not found in the repository." , 1, whatItem, false);
		}
		else
		{
			var tempArray = new Array()
			for (var currObj in this.dataToSerializeArray)
			{
				if (this.dataToSerializeArray[currObj] != this.dataToSerializeArray[whatName])
					tempArray[currObj] = this.dataToSerializeArray[currObj]
			}
			this.dataToSerializeArray = tempArray;
			this.debugAlert("REMOVED new high level item to repository, name: " + whatName , 2, whatItem, false);
		}
	},

	// Removes all items from the data repository.
	removeAllItems : function ()
	{
		this.debugAlert("REMOVED all repository items.", 1, false, false);
		this.dataToSerializeArray = new Array();
	},


	// Loops through this.dataToSerializeArray sending each entry to this.sortTypeAndSerialize()
	// to be encoded, adding each return value to dataString with an '&' seperator.
	serializeData : function ()
	{
		var dataString = new String();
		var dataIndex = 0;
		for (var currName in this.dataToSerializeArray)
		{
			this.debugAlert("***** NEW HIGH LEVEL OBJECT Name: " + currName + " *****", 1, this.dataToSerializeArray[currName], true);

			dataString += this.sortTypeAndSerialize(dataIndex, this.dataToSerializeArray[currName], currName) + "&"
			dataIndex++;

			this.debugAlert("***** FINISHED HIGH LEVEL OBJECT Name: " + currName + " *****", 1);
		}
		// Remove the trailing '&'
		dataString = dataString.slice(0, dataString.length - 1)

		this.checkDataLength(dataString)

		this.dataToSerializeArray = new Array();

		return dataString;
	},
/* ============================= END PUBLIC METHODS ============================= */




/* ============================ START PRIVATE METHODS =========================== */
	// Called by serializeData() to ensure the tracking string doesn't exceed the this.maxStringLength
	// set in the serializer_constants.js (currently 3k).
	//
	// If the string length is over the this.maxStringLength the function throws an error stating by how
	// much and what portion of the string is under threat of truncation.
	checkDataLength : function (whatDataString)
	{
		if (whatDataString.length > this.maxStringLength)
		{
			var overRun = whatDataString.slice(this.maxStringLength, whatDataString.length);
			var overAmount = whatDataString.length - this.maxStringLength;
			this.debugAlert("The tracking data is " + overAmount + " bytes to large. Some LMS may not save a string this long and thus data may be cropped. Data at risk of truncation:\n\n\n" + overRun + "\n\nUnescaped:\n\n" + unescape(overRun), 3, true)
		}
		else
		{
			this.debugAlert("Tracking string is within the " + this.maxStringLength + " bytes limit", 1)
		}
	},


	// This function checks the type of the ID and then call's the relivant method (from below)
	// to encode it.
	sortTypeAndSerialize : function (whatIndex, whatArgument, whatName)
	{
		var returnString = "";
		switch(typeof whatArgument)
		{
			case 'undefined':
				returnString += this.serializeUndefined(whatIndex, whatArgument, whatName)
			break;
			case 'string':
				returnString += this.serializeString(whatIndex, whatArgument, whatName)
			break;
			case 'number':
				returnString += this.serializeNumber(whatIndex, whatArgument, whatName)
			break;
			case 'boolean':
				returnString += this.serializeBoolean(whatIndex, whatArgument, whatName)
			break;
			case 'function':
				if (this.serializeFunctions)
					returnString += this.serializeFunction(whatIndex, whatArgument, whatName)
				else
					returnString += this.serializeNull(whatIndex,whatArgument, whatName)
			break;
			case 'object':
				if (whatArgument == null)
				{
					returnString += this.serializeNull(whatIndex,whatArgument, whatName)
				}
				else if (whatArgument instanceof Date)
				{
					returnString += this.serializeDate(whatIndex,whatArgument, whatName)
				}
				else if (whatArgument instanceof Array)
				{
					returnString += this.serializeArray(whatIndex,whatArgument, whatName)
				}
				else if (whatArgument instanceof String)
				{
					returnString += this.serializeStringObject(whatIndex,whatArgument, whatName)
				}
				else if (whatArgument instanceof Number)
				{
					returnString += this.serializeNumberObject(whatIndex,whatArgument, whatName)
				}
				else if (whatArgument instanceof Boolean)
				{
					returnString += this.serializeBooleanObject(whatIndex,whatArgument, whatName)
				}
				else
				{
					returnString += this.serializeObject(whatIndex,whatArgument, whatName)
				}
			break;
			default:
				this.debugAlert("You can only serialize strings, numbers, booleans, dates, objects, arrays, nulls, and undefined. _sortTypeAndSerialize(), re_formated_seriliazer.js", 3, true);
			break;
		}
		return returnString
	},


	// Encodes the entry in the format tXX=this.UNDEFINED&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// The decode function will simply return "undefined" when it finds the type is 'this.UNDEFINED' so
	// in this case, the actual whatArgument is superflous.
	serializeUndefined : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeUndefined(whatBaseIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		return	't' + (whatIndex) + '=' + this.UNDEFINED + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.STRING&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// Checks wether to escape the string based on this.escapeDataStrings which is set from serializer_constants.js
	serializeString : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeString(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		if (this.escapeDataStrings)
			return 't' + (whatIndex) + '=' + this.STRING + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + this.flashEscape(whatArgument);
		else
			return 't' + (whatIndex) + '=' + this.STRING + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.OBJ_STRING&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// Checks wether to escape the string based on this.escapeDataStrings which is set from serializer_constants.js
	// Differ's from the above in that this.OBJ_STRING is set as the type and not this.STRING, which will make a difference
	// as to how the object is deseriliazed.
	serializeStringObject : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("_serializeStringObject(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		if (this.escapeDataStrings)
			return 't' + (whatIndex) + '=' + this.OBJ_STRING + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + this.flashEscape(whatArgument);
		else
			return 't' + (whatIndex) + '=' + this.OBJ_STRING + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.NUMBER&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	serializeNumber : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeNumber(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		return 't' + (whatIndex) + '=' + this.NUMBER + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.OBJ_NUMBER&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// Differ's from the above in that this.OBJ_NUMBER is set as the type and not this.NUMBER, which will make a difference
	// as to how the object is deseriliazed.
	serializeNumberObject : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("_serializeNumberObject(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		return 't' + (whatIndex) + '=' + this.OBJ_NUMBER + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.BOOLEAN&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	serializeBoolean : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeBoolean(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		return 't' + (whatIndex) + '=' + this.BOOLEAN + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.OBJ_BOOLEAN&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// Differ's from the above in that this.OBJ_BOOLEAN is set as the type and not this.BOOLEAN, which will make a difference
	// as to how the object is deseriliazed.
	serializeBooleanObject : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("_serializeBooleanObject(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		return 't' + (whatIndex) + '=' + this.OBJ_BOOLEAN + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},

	// Encodes the entry in the format tXX=this.OBJ_DATE&nXX=whatName&dXX=whatArgument.getTime() (where XX is the whatIndex).
	// Note the use of the .getTime() method, this is so the information can be passed to a newly created date object by the
	// relivant deserialize function.
	// Checks wether to escape the string based on this.escapeDataStrings which is set from serializer_constants.js
	serializeDate : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeDate(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		if (this.escapeDataStrings)
			return 't' + (whatIndex) + '=' + this.OBJ_DATE + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + this.flashEscape(whatArgument.getTime());
		else
			return 't' + (whatIndex) + '=' + this.OBJ_DATE + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument.getTime();
	},


	// Encodes the entry in the format tXX=this.FUNCTION&nXX=whatName&dXX=this.trimFunctionContents(whatArgument) (where XX is the whatIndex).
	// Note the use of the this.trimFunctionContents() method.
	// Checks wether to escape the string based on this.escapeDataStrings which is set from serializer_constants.js
	serializeFunction : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeFunction(whatIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		if (this.escapeDataStrings)
			return 't' + (whatIndex) + '=' + this.FUNCTION + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + this.flashEscape(this.trimFunctionContents(whatArgument.toString()));
		else
			return 't' + (whatIndex) + '=' + this.FUNCTION + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + this.trimFunctionContents(whatArgument.toString());
	},


	// Used by the above function to extract the inner portion of a function. Its find the first { and the last }
	// and returns any text in-between.
	trimFunctionContents : function (whatFunctionString)
	{
		var startTrim = whatFunctionString.indexOf("{") + 1
		var endTrim = whatFunctionString.lastIndexOf("}")
		var trimmedText = whatFunctionString.slice(startTrim, endTrim)
		return trimmedText;
	},


	// Encodes the entry in the format tXX=TYPE&nXX=this.OBJ_NULL&dXX=whatArgument (where XX is the whatIndex).
	// The decode function will simply return null when it finds the type is 'this.OBJ_NULL' so
	// in this case, the actual whatArgument is superflous.
	serializeNull : function (whatIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeNull(whatBaseIndex:" + whatIndex + ", whatArgument: " + whatArgument + ")", 1)
		return	't' + (whatIndex) + '=' + this.OBJ_NULL + '&n' + whatIndex + '=' + whatName + '&d' + (whatIndex) + '=' + whatArgument;
	},


	// Encodes the entry in the format tXX=this.OBJ_ARRAY&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// Then the function loops through all the cell's within the array, sending each to this.sortTypeAndSerialize()
	// with an ID based on the whatBaseIndex parameter with ".X" added, where X is the current cell count.
	// Those return values are appended to the returnString.
	serializeArray : function (whatBaseIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeArray(whatBaseIndex:" + whatBaseIndex + ", whatArgument: " + whatArgument + ")", 1)
		var returnString = "t" + whatBaseIndex + "=" + this.OBJ_ARRAY + "&n" + whatBaseIndex + "=" + whatName + "&d" + whatBaseIndex + "=&";
		var subItemCount = 1;
		for (currPointer in whatArgument)
		{
			returnString += this.sortTypeAndSerialize((whatBaseIndex + "." + subItemCount), whatArgument[currPointer], currPointer) + "&"
			subItemCount++;
		}
		return returnString.slice(0,(returnString.length - 1));;
	},


	// Encodes the entry in the format tXX=this.OBJ_OBJECT&nXX=whatName&dXX=whatArgument (where XX is the whatIndex).
	// Then the function loops through all the properties/methods within the object, sending each to this.sortTypeAndSerialize()
	// with an ID based on the whatBaseIndex parameter with ".X" added, where X is the current propertie/method count.
	// Those return values are appended to the returnString.
	serializeObject : function (whatBaseIndex, whatArgument, whatName)
	{
		this.debugAlert("serializeObject(whatBaseIndex:" + whatBaseIndex + ", whatArgument: " + whatArgument + ")", 1)
		var returnString = "t" + whatBaseIndex + "=" + this.OBJ_OBJECT + "&n" + whatBaseIndex + "=" + whatName + "&d" + whatBaseIndex + "=&";
		var subItemCount = 1;
		for (currPointer in whatArgument)
		{
			returnString += this.sortTypeAndSerialize((whatBaseIndex + "." + subItemCount), whatArgument[currPointer], currPointer) + "&"
			subItemCount++;
		}
		return returnString.slice(0,(returnString.length - 1));
	},








	// Diagnostic function that alerts all properties of the object its passed.
	// Functions and Object's are not fully listed, but instead have " = A Function"
	// of " = A Object" added.
	alertAll : function ()
	{
		var names = "Object properties:\n"

		var functionsArray = new Array();
		var objectsArray = new Array();
		var variablesArray = new Array();

		for (var propCount in this)
		{
			//alert(typeof propCount)
			evalProp = eval("this." + propCount)
			propType = typeof evalProp
			switch (propType)
			{
				case "function":
					functionsArray[functionsArray.length] =  propCount + " = A Function\n"
				break;
				case "object":
					//objectsArray[objectsArray.length] =  propCount + " = A Object\n"
					objectsArray[objectsArray.length] =  propCount + " = " + evalProp + "\n"
				break;
				default:
					variablesArray[variablesArray.length] =  propCount + " = " + evalProp + "\n"
				break;
			}
		}

		names += "\nFUNCTIONS\n\n"
		var count = 0;
		functionsArray.sort()
		while (count < functionsArray.length)
		{
			names += functionsArray[count]
			count++
		}

		names += "\nOBJECTS\n\n"
		var count = 0;
		objectsArray.sort()
		while (count < objectsArray.length)
		{
			names += objectsArray[count]
			count++
		}

		names += "\nVARIABLES\n\n"
		var count = 0;
		variablesArray.sort()
		while (count < variablesArray.length)
		{
			names += variablesArray[count]
			count++
		}
		alert(names)
		//this.debugAlert(names)
	},

	// A replacement for the normal escape() function as Javascript and Actionscript disagree on what to do about _'s.
	// Oddly enough, there is no need to write a unescape extension as Javascript will unescape suspend%5Fdata as
	// suspend_data just fine.
	flashEscape : function (whatString)
	{
		whatString = escape(whatString)
		whatString = whatString.replace("_","%5F")
		return whatString
	},


	// Pipe's through directly to the DEBUG object. See debugger.js for more info.
	debugAlert : function (msg, priority, whatObj, showCallee)
	{
		if (DEBUG)
			DEBUG.lert(msg, priority, whatObj, showCallee, "SERILAIZER ")
	}
/* ============================ END PRIVATE METHODS ============================ */
}

SERILAIZER.debugAlert("SERILAIZER Initialized", 0)
SERILAIZER.debugAlert("Object:", 1, SERILAIZER, true)
