/*

Author: Johnc
Start Date: 30/10/06

*/

var DESERILAIZER =
{
	id : "Da Deserializer",

	//Run-time properties
	stringToDeserialize : "",

	deserializedDataArray : new Array(),

	// Used to catergorize the data split up from the string.
	typeArray : new Array(),
	nameArray : new Array(),
	dataArray : 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,

	// Used to indicate the item has been used.
	USED_ITEM : CONST_USED_ITEM,
/* ============================= END CONSTANTS ============================= */



/* ============================= START BUILD PARAM's =========================== */
	// Used to switch on/off the unescaping of text based data.
	unescapeDataStrings : CONST_ESCAPE_STRING_BASED_DATA,
	// Used to switch on/off the deserializeing of any code found attached to an object.
	deserializeFunctions : CONST_SERIALIZE_FUNCTIONS,
/* ============================= END BUILD PARAM's ============================ */



/* ============================ START PUBLIC METHODS ============================ */
	// Takes a string and sets this.stringToDeserialize with it, then calls
	// this.deserializeData() to populate this.deserializedDataArray.
	setDataString : function (whatDataString)
	{
		//alert("setDataString")
		this.debugAlert("Set this.stringToDeserialize:\n\n" + whatDataString, 1, false, true)
		this.stringToDeserialize = whatDataString
		this.deserializeData()
	},


	// Returns the entire array of deserialized objects.
	getAllItems : function ()
	{
		this.debugAlert("Returning all deserialized items: ", 1, this.deserializedDataArray, true);
		var tmpArr = this.deserializedDataArray;
		this.deserializedDataArray = new Array();
		return tmpArr;
	},

	// Clears the entire array of deserialized objects.
	removeAllItems : function ()
	{
		this.debugAlert("Clearing all deserialized items: ", 1, this.deserializedDataArray, true);
		this.deserializedDataArray = new Array()
	},


	// Retrieves a named object from the this.deserializedDataArray or throws an error
	// if an object of that name can't be found.
	getItem : function (whatName)
	{
		// Check if it exists.
		if (this.deserializedDataArray[whatName])
		{
			this.debugAlert("RETRIEVED: " + this.deserializedDataArray[whatName], 1, false, false)
			var tmpArr = this.deserializedDataArray;
			this.deserializedDataArray = new Array();
			return tmpArr[whatName];
		}
		else
		{
			this.debugAlert("Failed to find data by the name of " + whatName + " in _getItem(), re_formated_deseriliazer.js", 3, this.deserializedDataArray, true)
			return false;
		}
	},
/* ============================= END PUBLIC METHODS ============================= */



/* ============================ START PRIVATE METHODS =========================== */
	// Called on object instantiation, parse's the string passed into the constructor that gets
	// set as this.stringToDeserialize into the re-constructed data objects.
	deserializeData : function ()
	{
		this.sortIntoArrays();
		this.parseArrays();
		this.checkUnused();
	},


	// Splits the tXX=, nXX= and dXX= (type, name and data) into the relivant arrays and compares the final length
	// of the three arrays, throwing an error if they don't match. It also throw's an error if the prefix (t, n, or d)
	// isn't understood.
	sortIntoArrays : function ()
	{
		// Split into one large array.
		var splitArray = this.stringToDeserialize.split("&")
		var count = 0;
		// Loop through ALL the items, sorting as needed.
		while (count < splitArray.length)
		{
			// Extract the t, n, or d prefix
			var prefix = splitArray[count].charAt(0);
			// Extract the ID e.g. 2.3.1
			var currID = splitArray[count].slice(1,splitArray[count].indexOf("="))
			// Sort into relivant array for future use in parseArrays()
			switch (prefix)
			{
				case "n":
					this.nameArray[currID] = splitArray[count].split("=")[1];
				break
				case "d":
					this.dataArray[currID] = splitArray[count].split("=")[1];
				break
				case "t":
					this.typeArray[currID] = splitArray[count].split("=")[1];
				break
				default:
					this.debugAlert("Unknown prefix of '" + prefix + "' found in '" + splitArray[count] + "', _deserializeData(), re_formated_deseriliazer.js", 3, splitArray, true)
				break;
			}
			count++;
		}
		// Check the array lengthes match.
		if (this.typeArray.length != this.nameArray.length || this.typeArray.length != this.dataArray.length)
		{
			this.debugAlert("Mismatched array length's may indicate missing / corrupt tracking data. _sortIntoArrays(), re_formated_deseriliazer.js", 3, false, true)
		}
	},


	// This function works by testing possible high level IDs until it fails. As the
	// IDs are auto-generated and should always be contiguous, when a possible high
	// level ID is not found, the end of the high level data objects can be assumed.
	//
	// Each valid ID is passed to sortTypeAndDeserialize() and the result is assigned
	// to this.deserializedDataArray[] using the objects name as the key.
	//
	// This function will only ever deal with the high level objects, any sub objects
	// will be handled by _deserializeObject() and _deserializeArray() which will then
	// call sortTypeAndDeserialize() independently.
	parseArrays : function ()
	{
		var count = 0;
		var currHighLevelName = this.nameArray[count];
		while (currHighLevelName)
		{
			this.debugAlert("***** NEW HIGH LEVEL OBJECT Name: " + currHighLevelName + " *****", 1, false, false);

			this.deserializedDataArray[currHighLevelName] = this.sortTypeAndDeserialize(count);

			this.debugAlert("***** FINISHED HIGH LEVEL OBJECT Name: " + currHighLevelName + " *****", 1, this.deserializedDataArray[currHighLevelName], false);
			count++;
			currHighLevelName = this.nameArray[count];
		}
	},


	// This function checks the type of the ID and then calls the relivant method (from below)
	// to re-construct it, blanks it from the raw arrays then returns the object.
	sortTypeAndDeserialize : function (whatIndex)
	{
		var returnObj = "";
		switch (this.typeArray[whatIndex])
		{
			case this.UNDEFINED:
				returnObj = this.deserializeUndefined(whatIndex)
			break;
			case this.STRING:
				returnObj = this.deserializeString(whatIndex)
			break;
			case this.NUMBER:
				returnObj = this.deserializeNumber(whatIndex)
			break;
			case this.BOOLEAN:
				returnObj = this.deserializeBoolean(whatIndex)
			break;
			case this.FUNCTION:
				if (this.deserializeFunctions)
					returnObj = this.deserializeFunction(whatIndex)
				else
					returnObj = this.deserializeNull(whatIndex)
			break;
			case this.OBJ_STRING:
				returnObj = this.deserializeStringObject(whatIndex)
			break;
			case this.OBJ_NUMBER:
				returnObj = this.deserializeNumberObject(whatIndex)
			break;
			case this.OBJ_BOOLEAN:
				returnObj = this.deserializeBooleanObject(whatIndex)
			break;
			case this.OBJ_OBJECT:
				returnObj = this.deserializeObject(whatIndex)
			break;
			case this.OBJ_ARRAY:
				returnObj = this.deserializeArray(whatIndex)
			break;
			case this.OBJ_DATE:
				returnObj = this.deserializeDate(whatIndex)
			break;
			case this.OBJ_NULL :
				returnObj = this.deserializeNull(whatIndex)
			break;
			default:
				this.debugAlert("Unknown type of " + this.typeArray[whatIndex] + ".\nwhatIndex: " + whatIndex + "\nthis.dataArray[whatIndex]: " + this.dataArray[whatIndex] + "\nthis.nameArray[whatIndex]: " + this.dataArray[whatIndex] + "\n\nYou can only deserialize strings, numbers, booleans, dates, objects, arrays, nulls, and undefined. _sortTypeAndDeserialize(), re_formated_deseriliazer.js\n\n", 3, this.typeArray[whatIndex], false);
			break;
		}
		this.typeArray[whatIndex] = this.USED_ITEM
		this.nameArray[whatIndex] = this.USED_ITEM
		this.dataArray[whatIndex] = this.USED_ITEM

		return returnObj;
	},


	// Simply returns the string "undefined".
	deserializeUndefined : function (whatIndex)
	{
		this.debugAlert("deserializeUndefined(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		return "undefined";
	},


	// Checks wether strings are being escaped and calls unescape() if required then returns the string.
	deserializeString : function (whatIndex)
	{
		this.debugAlert("deserializeString(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		if (this.unescapeDataStrings)
			return unescape(this.dataArray[whatIndex])
		else
			return this.dataArray[whatIndex]
	},


	// Same as above, however uses the relivant constructor. See serializer_constants.js for more infomation about the differences
	// between normal and constructed variables.
	deserializeStringObject : function (whatIndex)
	{
		this.debugAlert("deserializeStringObject(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		if (this.unescapeDataStrings)
			return new String(unescape(this.dataArray[whatIndex]))
		else
			return new String(this.dataArray[whatIndex])
	},


	// Calls parseInt to ensure type isn't string then returns the number.
	deserializeNumber : function (whatIndex)
	{
		this.debugAlert("deserializeNumber(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		return parseInt(this.dataArray[whatIndex])
	},


	// Same as above, however use's the relivant constructor. See serializer_constants.js for more infomation about the differences
	// between normal and constructed variables.
	deserializeNumberObject : function (whatIndex)
	{
		this.debugAlert("deserializeNumberObject(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		return new Number(parseInt(this.dataArray[whatIndex]))
	},


	// Checks for the string "true" and returns true if present or false if not.
	deserializeBoolean : function (whatIndex)
	{
		this.debugAlert("deserializeBoolean(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		if (this.dataArray[whatIndex] == "true")
			return true;
		else
			return false;
	},


	// Same as above, however use's the relivant constructor. See serializer_constants.js for more infomation about the differences
	// between normal and constructed variables.
	deserializeBooleanObject : function (whatIndex)
	{
		this.debugAlert("deserializeBooleanObject(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		if (this.dataArray[whatIndex] == "true")
			return new Boolean(true);
		else
			return new Boolean(false);
	},


	// Checks wether strings are being escaped and unescape()'s if required then create's a new date object
	// using the saved value and returns that.
	deserializeDate : function (whatIndex)
	{
		this.debugAlert("deserializeDate(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		if (this.unescapeDataStrings)
			return new Date(unescape(this.dataArray[whatIndex]))
		else
			return new Date(this.dataArray[whatIndex])
	},


	// Checks wether strings are being escaped and unescape()'s if required then creates a new function
	// using the constructor and returns that.
	deserializeFunction : function (whatIndex)
	{
		this.debugAlert("deserializeFunction(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		if (this.unescapeDataStrings)
			return new Function(unescape(this.dataArray[whatIndex]))
		else
			return new Function(this.dataArray[whatIndex])
	},

	// This function works by testing possible child ID's until it fails. As the
	// ID's are auto-generated and should always be contiguous, when a possible child
	// ID is not found, the end of the child data objects can be assumed.
	//
	// Each valid ID is passed to sortTypeAndDeserialize() and the result is assigned
	// to the array using the objects name as the key. Names are checked to see if they
	// are purely numbers and if so parseInt'd.
	deserializeArray : function (whatBaseIndex)
	{
		this.debugAlert("deserializeArray(whatBaseIndex:" + whatBaseIndex + ")", 1, false, false)
		var returnArray = new Array()
		var subItemCount = 1;

		// Loop through the sub items trying to find viable match's to add as the object values.
		// If a sub item match's (IE if this object is 5.3, 5.3.1, 5.3.2 etc would match) then send it off
		// for processing and then assign it to the object as needed.
		var nextTestItemName = this.nameArray[whatBaseIndex + "." + subItemCount]
		if (nextTestItemName)
		{
			while (nextTestItemName)
			{
				// Check if the name is a number ONLY, if so, parseInt it so
				// type is correct.
				if (nextTestItemName == parseInt(nextTestItemName).toString())
				{
					nextTestItemName = parseInt(nextTestItemName)
				}
				// Pass to sortTypeAndDeserialize() which while then call sub functions, possibly
				// even recusing into this one.
				returnArray[nextTestItemName] = this.sortTypeAndDeserialize(whatBaseIndex + "." + subItemCount)
				this.debugAlert("Added '" + nextTestItemName + "' to array with ID: " + whatBaseIndex + ", value was: " + returnArray[nextTestItemName], 1, false, false)
				// Increament the subItemCount
				subItemCount++
				// Create a reference to the next name to test for.
				nextTestItemName = this.nameArray[whatBaseIndex + "." + subItemCount]
			}
		}
		else
			this.debugAlert("An Array has been found with no entries, ID:" + whatBaseIndex + ", deserializeArray(), re_formated_deseriliazer.js", 2, false, false)
		return returnArray
	},

	// This function works by testing possible child ID's until it fails. As the
	// ID's are auto-generated and should always be contiguous, when a possible child
	// ID is not found, the end of the child data objects can be assumed.
	//
	// Each valid ID is passed to sortTypeAndDeserialize() and the result is assigned
	// to the array using the objects name as the key.
	deserializeObject : function (whatBaseIndex)
	{
		this.debugAlert("deserializeObject(whatBaseIndex:" + whatBaseIndex + ")", 1, false, false)
		var returnObj = new Object()
		var subItemCount = 1;

		// Loop through the sub items trying to find viable match's to add as the object values.
		// If a sub item match's (IE if this object is 5.3, 5.3.1, 5.3.2 etc would match) then send it off
		// for processing and then assign it to the object as needed.
		var nextTestItemName = this.nameArray[whatBaseIndex + "." + subItemCount]
		if (nextTestItemName)
		{
			while (nextTestItemName)
			{
				// Pass to sortTypeAndDeserialize() which while then call sub functions, possibly
				// even recusing into this one.
				returnObj[nextTestItemName] = this.sortTypeAndDeserialize(whatBaseIndex + "." + subItemCount)
				this.debugAlert("Added '" + nextTestItemName + "' to object with ID: " + whatBaseIndex + ", value was: " + returnObj[nextTestItemName], 1, false, false)
				// Increament the subItemCount
				subItemCount++
				// Create a reference to the next name to test for.
				nextTestItemName = this.nameArray[whatBaseIndex + "." + subItemCount]
			}
			this.debugAlert("STOPPED COLLECTING At: " + (whatBaseIndex + "." + subItemCount) + ", name for data was: " + nextTestItemName, 1, false, false)
		}
		else
			this.debugAlert("An object has been found with no properties or methods, ID:" + whatBaseIndex + ", deserializeObject(), re_formated_deseriliazer.js", 2, false, false)
		return returnObj
	},


	// Simply returns null.
	deserializeNull : function (whatIndex)
	{
		this.debugAlert("deserializeNull(whatIndex:" + whatIndex + "), this.dataArray[whatIndex]: " + this.dataArray[whatIndex], 1, false, false)
		return null;
	},


	// As ID's are passed to _sortTypeAndDeserialize() their entry in the relivant array's
	// is set too this.USED_ITEM. Once parseArrays() has finished, each Array is then inspected
	// for any entries that aren't == this.USED_ITEM and throw's an error if it finds any.
	checkUnused : function ()
	{
		for (var currIndex in this.typeArray)
		{
			if (this.typeArray[currIndex] != this.USED_ITEM)
			{
				this.debugAlert("Unused TYPE, ID: " + currIndex + ", name: " + this.nameArray[currIndex] + ", type: " + this.typeArray[currIndex] + ", data: " + this.dataArray[currIndex] + ", found by checkUnused() in re_formated_deseriliazer.js", 3, false, true)
			}
		}
		for (var currIndex in this.nameArray)
		{
			if (this.typeArray[currIndex] != this.USED_ITEM)
			{
				this.debugAlert("Unused NAME, ID: " + currIndex + ", name: " + this.nameArray[currIndex] + ", type: " + this.typeArray[currIndex] + ", data: " + this.dataArray[currIndex] + ", found by checkUnused() in re_formated_deseriliazer.js", 3, false, true)
			}
		}
		for (var currIndex in this.dataArray)
		{
			if (this.typeArray[currIndex] != this.USED_ITEM)
			{
				this.debugAlert("Unused DATA, ID: " + currIndex + ", name: " + this.nameArray[currIndex] + ", type: " + this.typeArray[currIndex] + ", data: " + this.dataArray[currIndex] + ", found by checkUnused() in re_formated_deseriliazer.js", 3, false, true)
			}
		}
	},

	// 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)
	},

	// 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, "DESERILAIZER")
	}
/* ============================ END PRIVATE METHODS ============================ */
}
DESERILAIZER.debugAlert("DESERILAIZER Initialized", 0)
DESERILAIZER.debugAlert("Object:", 1, DESERILAIZER, true)
