/********************************************/
/*		shopping cart handler				*/
/********************************************/
//  name fields according to item code:
//	code_p  for price
//	code_q  for quantity
//	code_v  for extended price
//  code_i  for increment
//	code_d	for decrement
//
//  IMPORTANT set id=[name] (and name=[name] if form is to be posted)

// variables are exchanged with server via cookie

/* steps:
server reads cookies
creates page which loads this script
page writes amended values
note: there is no need to read the values of the cookies in the JavaScript
the page values are pre-populated by the php script
Quantities are read at the time a change is made.
*/

var PRICE_FIELD_SUFFIX = '_p' ;				// same as in php server side file
var QUANTITY_FIELD_SUFFIX = '_q' ;			// same as in php server side file
var VALUE_FIELD_SUFFIX = '_v' ;				// same as in php server side file
var TOTAL_FORM_NAME = 'totals_form' ;		// same as in php server side file
var TOTAL_QTY_FIELD = 'total_qty';			// same as in php server side file
var TOTAL_VALUE_FIELD = 'total_value';		// same as in php server side file

var FORM_NAME =  'item_form' ;				// same as in php server side file
var CART_FORM_NAME =  'cart_form' ;			// same as in php server side file
var COKKIE_EXPIRY_SECONDS = 24 * 60 * 60 ;	// expire in one day
var DEFAULT_COOKIE_DOMAIN = ".replica.co.uk" ;

/********************************************/
/*		generic cookie handler				*/
/********************************************/

function Cookie(name)
{
	this.name = name ;
	this.value ;
	
	// internal variables

	this.date ;					// today's 'date for cookies
	this.expdate ;				// expiry date for cookies
		
	var that = this ;			// language specification bug fix

	that.date = new Date();		// Correct for Mac date bug - call only once for given Date object!
	_FixCookieDate(that.theDate);
	
	that.expdate = new Date ();
	_FixCookieDate(that.expdate); 
	that.expdate.setTime (that.expdate.getTime() + (COKKIE_EXPIRY_SECONDS * 1000)); // 24 hrs from now 
	
	// functions to set and retrieve cookie values

	this.Get = function()
	{
		that.value = this.GetCookie(that.name);
		return that.value ;
	}

	this.Set = function(value)
	{
		that.value = value ;
		this.SetCookie(that.value);
		return that.value ;
	}

	// st seems there is no way of having a protected metod in Javascript
	// so we cannot call _SetCookie and _GetCookie from descendants, without making them public
	// this is irritating as we are keeping the date internals private
	// solution:
	// provide a privileged method between Set and _SetCookie, likewise between Get and _GetCookie
	// this allows us to override Set and Get in descendants and still keep _SetCookie and _GetCookie private
	// (note ArrayCookie below is a descendant)

	this.SetCookie = function(value)
	{
		// alert ("calling _SetCookie for [" + that.name + "] with [" + value + "]")	
		_SetCookie(that.name,value,that.expdate);
	}

	this.GetCookie = function()
	{
		str = _GetCookie(that.name);
		// alert ("called _GetCookie for [" + that.name + "] returned [" + str + "]")	
		return str;
	}

	/********************************************************/
	/*	internals to write out and read in physical cookie	*/
	/********************************************************/

	//  Correct for 2.x Mac date bug.  Call once only to
	//  fix a date prior to passing it to SetCookie.

	function _FixCookieDate(date) 
	{
		var base = new Date(0);
		var skew = base.getTime(); // dawn of (Unix) time - should be 0
		if (skew > 0)  // Except on the Mac - ahead of its time
			date.setTime (date.getTime() - skew);
	}

	//  Return the value of the cookie specified by "name" as a string
	//  returns null if cookie does not exist.
	
	function _GetCookie(name) 
	{
		var arg = name + "=";
		var alen = arg.length;
		var clen = document.cookie.length;
		var i = 0;
		while (i < clen)
		{
			var j = i + alen;
			if (document.cookie.substring(i, j) == arg)
				return _GetCookieVal(j);
			i = document.cookie.indexOf(" ", i) + 1;
				if (i == 0) break; 
		}
	return null;
	}
	  
	function _GetCookieVal (offset) 
	{
		var endstr = document.cookie.indexOf (";", offset);
		if (endstr == -1)
			endstr = document.cookie.length;
		return unescape(document.cookie.substring(offset, endstr));
	}

	// SetCookie (myCookieVar, cookieValueVar, null, "/myPath", null, true);

	function _SetCookie(name,value,expires,path,domain,secure) 
	{
		document.cookie = name + "=" + escape (value) +
			((expires) ? "; expires=" + expires.toGMTString() : "") +
			((path) ? "; path=" + path : "") +
			((domain) ? "; " + DEFAULT_COOKIE_DOMAIN : "") +
			((secure) ? "; secure" : "");
	}
	
	function _DeleteCookie (name,path,domain)
	{
		if (GetCookie(name))
		{
			document.cookie = name + "=" + ((path) ? "; path=" + 
				path : "") + ((domain) ? "; " + DEFAULT_COOKIE_DOMAIN : "") + 
				"; expires=Thu, 01-Jan-70 00:00:01 GMT";
		}
	}

	//  Examples

	//var expdate = new Date ();
	//FixCookieDate (expdate); // Correct for Mac date bug - call only once for given Date object!
	//expdate.setTime (expdate.getTime() + (24 * 60 * 60 * 1000)); // 24 hrs from now 
	//SetCookie ("ccpath", "http://www.hidaho.com/colorcenter/", expdate);
	//SetCookie ("ccname", "hIdaho Design ColorCenter", expdate);
	//SetCookie ("tempvar", "This is a temporary cookie.");
	//SetCookie ("ubiquitous", "This cookie will work anywhere in this domain",null,"/");
	//SetCookie ("paranoid", "This cookie requires secure communications",expdate,"/",null,true);
	//SetCookie ("goner", "This cookie must die!");
	//document.write (document.cookie + "<br>");
	//DeleteCookie ("goner");
	//document.write (document.cookie + "<br>");
	//document.write ("ccpath = " + GetCookie("ccpath") + "<br>");
	//document.write ("ccname = " + GetCookie("ccname") + "<br>");
	//document.write ("tempvar = " + GetCookie("tempvar") + "<br>");

}

/********************************************/
/*		array cookie handler				*/
/********************************************/
// Method (class) to handle a cookie which is an array of values
// cookie is stored as name:value pairs separated by &

function ArrayCookie(name)
{
	this.inheritFrom = Cookie;
	this.inheritFrom(name);
	
	this.value = new Array ;
	var that = this ;
	
	this.Set = function(code,value)
	{
		that.value[code]=value ;
		str = EncodeToStr();
		that.SetCookie(str);
		return that.value ;
	}
	
	this.Get = function (code)
	{
		str = that.GetCookie();
		that.value = DecodeFromStr(str);
		if ( that.value[code] )
			return that.value[code];
		else
			return null ;
	}
	
	this.Clear = function()
	{
		that.value = new Array ;
		str = EncodeToStr();
		that.SetCookie(str);
		// alert( 'debug: setting cookie ' + that.name + ' to ' + '[' + str + ']');
		return that.value ;
	}
	
	// function to encode selections array into cookie

	function EncodeToStr()
	{
		var str = '';
		for (var index in that.value )
		{
			if (that.value[index])
			{
				if (str != '')
					str += '&' ;
				str += index + ':' + escape(that.value[index])
			}
		}
		return str;
	}

	// function to decode selections array from cookie

	function DecodeFromStr(str)
	{
		var data = new Array();
		if (str)
		{
			var a = str.split('&');		// split the data into an array
			for (var i=0; i < a.length; i++) 
				a[i] = a[i].split(':');	// split again on the : delimiter and overwrite value with array
			for (var i=0; i < a.length; i++)
				data[a[i][0]] = unescape(a[i][1]); // create a set of members of cookie, with appropriate values
		}
		return data ;	
	}
}

/********************************************/
/*		array cookie handler				*/
/********************************************/
// Method (class) to handle a cookie which is an array of values
// cookie is stored as name:value pairs separated by &

function xArrayCookie(name)
{
	this.inheritFrom = Cookie;
	this.inheritFrom(name);
	
	this.value = new Array ;
	var that = this ;
	
	this.Set = function(code,value)
	{
		that.value[code]=value ;
		str = EncodeToStr();
		that.SetCookie(str);
		return that.value ;
	}
	
	this.Get = function (code)
	{
		str = that.GetCookie();
		that.value = DecodeFromStr(str);
		if ( that.value[code] )
			return that.value[code];
		else
			return null ;
	}
	
	this.Clear = function()
	{
		that.value = new Array ;
		str = EncodeToStr();
		that.SetCookie(str);
		// alert( 'debug: setting cookie ' + that.name + ' to ' + '[' + str + ']');
		return that.value ;
	}
	
	// function to encode selections array into cookie

	function EncodeToStr()
	{
		var str = '';
		for (var index in that.value )
		{
			if (that.value[index])
			{
				if (str != '')
					str += '&' ;
				str += index + ':' + escape(that.value[index])
			}
		}
		return escape(xcrypt(str));
	}

	// function to decode selections array from cookie

	function DecodeFromStr(str)
	{
		var data = new Array();
		if (str)
		{
			str = dxcrypt(unescape(str));
			var a = str.split('&');		// split the data into an array
			for (var i=0; i < a.length; i++) 
				a[i] = a[i].split(':');	// split again on the : delimiter and overwrite value with array
			for (var i=0; i < a.length; i++)
				data[a[i][0]] = unescape(a[i][1]); // create a set of members of cookie, with appropriate values
		}
		return data ;	
	}

	function xcrypt(str)
	{
		var xor_key = 6 ;
		var result = "" ; 
		for(i=0;i<str.length;++i)
		{
			result += String.fromCharCode(xor_key^str.charCodeAt(i));
		}
		return ( result ) ;
	}
 
	function dxcrypt(str)
	{
		var xor_key = 6 ;
		result = "" ;
		for(i=0;i<str.length;i++)
		{
			result += String.fromCharCode(xor_key^str.charCodeAt(i));
		}
		return ( result ) ;
	}
}

function SelArrayCookie(name)
{
	this.inheritFrom = ArrayCookie;
	this.inheritFrom(name);

	var that = this ;
		

}

/****************************************************************/
/*		wrapper to hold all the cookie data for the page		*/
/*		and to hold the functions to modify the data			*/
/****************************************************************/

function clientData()
{
	// this.itemCode;			// last item clicked
	this.search;				// last search term used
	// this.category;			// current item category
	this.currencyId = 0;		// current currency &pound
	this.xrate = 1;				// exchange rate to pound eg $/£
	this.selections;			// item codes and quantities - array needs to be encoded and decoded when stored in the cookie
								// if variables added here: 
								// add handlers under global values below add to set and get functions
								// and mirror in php serverData class
	this.carriageId = 1 ;		// default to UK delivery

	
	var that = this ;			// language specification bug fix
			
	/********************************************/
	/*			initialise on instantiation		*/
	/********************************************/

	this.itemCode = new Cookie('itemCode');
	this.search = new Cookie('search');
	this.category = new Cookie('category');
	this.currencyId = new Cookie('currencyId');
	this.xrate = new Cookie('xrate');					// set by server, not client side
	this.selections = new SelArrayCookie('selections');
	this.carriageId = new Cookie('carriageId');
	this.sortColumn = new Cookie('sortColumn');
	// this.details = new xArrayCookie('details') ;
	this.details = new ArrayCookie('details') ;
	
	function Get()
	{
		that.itemCode.Get() ;
		that.search.Get() ;
		that.category.Get;
		that.currencyId.Set( that.currencyId.Get() ? that.currencyId.Get() : 0 );
		that.xrate.Set( that.xrate.Get() ? that.xrate.Get() : 1 );
		that.selections.Get() ;
		that.carriageId.Get() ;
		that.sortColumn.Get() ;
	}

	function Set()
	{
		that.itemCode.Set() ;
		that.search.Set() ;
		that.category.Set();
		that.currencyId.Set();
		// that.xrate.Set();					// set by server, not client side
		that.selections.Set() ;
		that.carriageId.Set() ;
		that.sortColumn.Set() ;
	}
	

	/********************************************/
	/*			 Global values					*/
	/********************************************/

	this.SetItemCode = function (str)
	{
		that.itemCode.Set(str);
	} 

	this.SetSearch = function (str)
	{
		that.search.Set(str);
	} 

	this.SetCategory = function (str)
	{
		that.category.Set(str);
	}
	
	this.SetCurrencyId = function (currencyId)
	{
		that.currencyId.Set(currencyId) ;
	}

	this.Setselections = function (code,qty)
	{
		// alert("code:" + code + " qty:" + qty);
		that.selections.Set(code,qty);
		// note: if calling with a form visible call RefreshFields(itemCode,qty) afterwards
	}
	
	this.SetDetails = function (field,value)
	{
		that.details.Set(field,value);
	}

	this.ClearSelections = function(forceClear)
	{
		if (forceClear)
		{
			that.selections.Clear() ;
		}
		else
		{
			var answer = confirm ("Click 'OK' to clear your cart.  Click 'Cancel' to keep your selections")
			if (answer)
			{
				that.selections.Clear() ;
			}
		}
	}

	this.ClearDetails = function()
	{
		that.details.Clear();
	}

	this.SetCarriageId = function (carriageId)
	{
		that.carriageId.Set(carriageId) ;
	}

	/*********************************************/
	/*		cart content handling functions  	 */
	/*********************************************/

	this.Search = function(str)
	{
		this.SetSearch(str);
		location.replace('search.php');
	}

	this.BuyNow = function (itemCode) 
	{
		// that.SetItemCode(itemCode);
		
		var qty = that.selections.Get(itemCode);
   		qty = qty ? parseInt(qty) : 0 ;
  		qty = isNaN(qty) ? 0 : qty ;
		qty = ( qty <= 0 ? 1 : qty );
					
		that.Setselections(itemCode, qty);
		RefreshFields(itemCode, qty, true);

		return qty;
	}

	this.Incr = function (itemCode)
	{
		// that.SetItemCode(itemCode);

		var qty = that.selections.Get(itemCode);
  		qty = qty ? parseInt(qty) : 0 ;
  		qty = isNaN(qty) ? 0 : qty ;
		qty = ( qty <= 0 ? 1 : ++qty );
		
		that.Setselections(itemCode, qty);
		RefreshFields(itemCode, qty, true);

		return qty;
	}

	this.Decr = function (itemCode)
	{
		// that.SetItemCode(itemCode);

		var qty = that.selections.Get(itemCode);
  		qty = qty ? parseInt(qty) : 0 ;
  		qty = isNaN(qty) ? 0 : qty ;
		qty = ( qty <= 0 ? 0 : --qty );

		that.Setselections(itemCode, qty);
		RefreshFields(itemCode, qty, true);

		return qty;
	}

	this.SetQty = function (itemCode, qty)
	{
		// that.SetItemCode(itemCode);
  		qty = qty ? parseInt(qty) : 0 ;
  		qty = isNaN(qty) ? 0 : qty ;
		qty = ( qty <= 0 ? 0 : qty );
		that.Setselections(itemCode, qty);
		RefreshFields(itemCode, qty, true);

		return qty;
	}

	this.SetValue = function (itemCode, value)
	{
		// that.SetItemCode(itemCode);
		value = parseFloat(value);
		value = isNaN(value) ? 0 : value ;
		var price = GetPrice(itemCode);
		var qty = Math.floor(1.0*value/price);

		qty = ( qty <= 0 ? 0 : qty );

		that.Setselections(itemCode, qty);	
		RefreshFields(itemCode, qty, true);

		return qty;
	}
	
	this.SetSortColumn = function (sortId)
	{
		that.sortColumn.Set(sortId) ;
	}
	
	/********************************************/
	/*			 Private functions				*/
	/********************************************/

	function GetPrice(itemCode)
	{
		// alert('['+itemCode+']'+'['+document.forms[FORM_NAME][itemCode+PRICE_FIELD_SUFFIX].value+']');
		return GetField(FORM_NAME, itemCode+PRICE_FIELD_SUFFIX) ;
	}

	function GetQty(itemCode)
	{
		return GetField(FORM_NAME, itemCode+QUANTITY_FIELD_SUFFIX) ;
	}

	function SetQtyField(itemCode, qty)
	{
		return SetField(FORM_NAME ,itemCode+QUANTITY_FIELD_SUFFIX, qty) ;
	}

	function GetValue(itemCode)
	{
		return GetField(FORM_NAME, itemCode+VALUE_FIELD_SUFFIX) ;
	}

	function SetValueField(itemCode, value)
	{
		return SetField(FORM_NAME, itemCode+VALUE_FIELD_SUFFIX, value) ;
	}

	function GetTotalQuantity()
	{
		var qty = 0;
		for ( var itemCode in that.selections.value){
			qty += Math.round(1.0 * that.selections.value[itemCode]);
			// alert('in GetTotalQuantity qty=['+qty+']');
		}
		// alert('in GetTotalQuantity qty=['+qty+']');
		return Math.round(qty);
	}


	function GetTotalValue()
	{
		// only works for the displayed items - as not all prices are available in all forms
		// workaround is to rewrite the cookie or re write all the selections values as hidden fields to the form 
		var qty = 0;
		for ( var itemCode in that.selections.value)
		{
			qty += Math.round( 1.0 * that.selections.value[itemCode] * GetPrice(itemCode) * 100) / 100 ;
		}
		return ( Math.round(qty*100)/100 );
	}
	
	function SetTotalsFields()
	{
		var qty = GetTotalQuantity();
		// alert('in SetTotalsFields qty=['+qty+']');
		var value = '' ; // GetTotalValue(); does not work as not all prices are available here
		// alert("[" + TOTAL_FORM_NAME + "][" + TOTAL_QTY_FIELD + "] total qty [" + qty + "] total value [" + value + "]");
		SetElementValueById(TOTAL_QTY_FIELD, qty);
		// SetElementValueById(TOTAL_VALUE_FIELD, value) ;
		SetField(TOTAL_FORM_NAME, TOTAL_QTY_FIELD, qty) ;
		SetField(FORM_NAME, TOTAL_QTY_FIELD, qty) ;
		SetField(FORM_NAME, TOTAL_VALUE_FIELD, qty) ;
	}

	function SetField(form_name, field_name, value)
	{
		if (document.forms[form_name])
			if (document.forms[form_name][field_name])
				return document.forms[form_name][field_name].value = value ;
		// alert ("document.forms[" + form_name + "][" + field_name + "].value not found") ;
		return null ;
	}

	function GetField(form_name, field_name, value)
	{
		if (document.forms[form_name])
			if (document.forms[form_name][field_name])
				return value = document.forms[form_name][field_name].value ;
		return null ;
	}

	RefreshFields = function(itemCode, qty, boolSetTotalsFields )
	{
		var price = GetPrice(itemCode);
		var value = CurrencyFormatted(Math.round(qty * price*100)/100);		

		SetQtyField(itemCode, qty);
		SetValueField(itemCode,value);
		
		if (boolSetTotalsFields) SetTotalsFields() ;
		
		// alternative method :		
		// var price = document.forms[FORM_NAME][itemCode+'_p'].value; // if price is set as a field
		// var value = Math.round(qty * price*100)/100;		
		// SetElementValueById(itemCode+'_q',qty);
		// SetElementValueById(itemCode+'_v',value);

		return qty;
	}

	// call this from the <body> tag as <BODY onLoad="c.RefreshPage();">
	// this recalculates the values from the cookie so a back gets the right result
	this.RefreshPage = function()
	{
		// return ; // this is not working yet
		var index, len, suffix, itemCode, qty ;
		for (index in document.forms[FORM_NAME] )
		{
			if (typeof(index)!= 'string')
			{
				alert ("type of ["+index+"] = ["+typeof(index)+"]");
				continue;
			}
			len = index.length;
			if (len <= 2)
			{
				continue ;
			}
			suffix = index.substring(len-2);

			if (suffix == QUANTITY_FIELD_SUFFIX)
			{
				if(! document.forms[FORM_NAME][index].disabled)
				{
					itemCode = index.substring(0,len-2);
					qty = that.selections.Get(itemCode);
					qty = qty ? qty : 0 ;
					// alert ("itemCode ["+itemCode+"] qty ["+qty+"]");
					RefreshFields(itemCode, qty, false);

				}
			}
		}
		SetTotalsFields() ;
	}

	/********************************************/
	/*			Access to DOM values			*/
	/********************************************/

	function GetElementValueById(id)
	{
		var val;
		var element = document.getElementById(id);
		// writeConsole(element,'get '+id);
		if (element.nodeName == 'INPUT')
		{
			val = element['value'];
			return val;
		}
		if (element.nodeName == 'P')
		{
			var child = element.firstChild 
			// writeConsole(child,'get '+id+' firstChild');
			if(child.nodeType==3)
			{
				val = child.data;
				return val;
			} 
			if(child.nodeType==1)
			{
				val = child.firstChild.data;
				return val;
			}
		if (element.nodeName == 'SPAN')
		{
			val = element.innerHTML;
			return val;
		}
		}
		// no return value otherwise to force error
	}

	function SetElementValueById(id,val)
	{
		var element = document.getElementById(id);
		// writeConsole(element,'set '+id); 
		if (element.nodeName == 'INPUT')
		{
			element['value'] = val ;
			return val;
		}
		if (element.nodeName == 'P')
		{
			var child = element.firstChild 
			// writeConsole(child,'set '+id+' firstChild');
			if(child.nodeType==3)
			{
				child.data = val;
				return val;
			} 
			if(child.nodeType==1)
			{
				child.firstChild.data = val;
				return val;
			}
		}
		if (element.nodeName == 'SPAN')
		{
			element.innerHTML = val;
			return val;
		}
		// no return value otherwise to force error
	}

	/********************************************/
	/*			DOM debugging console			*/
	/********************************************/

	function writeConsole(element,id)
	{
		top.consoleRef=window.open('','myconsole',
			  'width=350,height=250'
			+',menubar=0'
			+',toolbar=1'
			+',status=0'
			+',scrollbars=1'
			+',resizable=1')

		top.consoleRef.document.writeln(
		  '<html><head><title>Console</title></head>'
		   +'<body bgcolor=white onLoad="self.focus()">');

		// write content here
		top.consoleRef.document.writeln("<h1>"+id+"</h1>");
		top.consoleRef.document.writeln("<table>");
		top.consoleRef.document.writeln( "<tr><td><b>element</b> property</td><td>value</td></tr>" );
		for ( var i in element )
		{
			top.consoleRef.document.writeln( "<tr><td>"+i+"</td><td>"+element[i]+"</td></tr>" );
		}
		top.consoleRef.document.writeln("</table>");
		
		top.consoleRef.document.writeln('</body></html>');

 		top.consoleRef.document.close()
	}

}

var c = new clientData();


/********************************************/
/*			useful functions				*/
/********************************************/

function CurrencyFormatted(amount)
{
	var i = parseFloat(amount);
	if(isNaN(i)) { i = 0.00; }
	var minus = '';
	if(i < 0) { minus = '-'; }
	i = Math.abs(i);
	i = parseInt((i + .005) * 100);
	i = i / 100;
	s = new String(i);
	if(s.indexOf('.') < 0) { s += '.00'; }
	if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
	s = minus + s;
	return s;
}


/*  end of cookiehandler */
