	/*
	 * BasketManagement API
	 *
	 * Javascript functions to be used by pages requiring basket management functions
	 *
	 */

	/*
	 * Client side cache of basket content
	 */
	var ORDER_ITEMS = new OrderItems();

	/**
	 * This array should be populated by any page using BasketManagement
	 * if additional URL parameters are required.
	 *
	 * For example the CachedItemDisplay page currently needs to add productId
	 * and cateEntryId parameters for redisplaying the Product page with the
	 * ProductDisplay action
	 */
	var BasketExtraUrlParams = new Array(); 
	

	function controlBasketView(basketSection)
	{
		addFormElement( createBasketViewFlagElement(basketSection) );

		orderItemUpdate();
		
	}
	/*
	 * Buys a product by creating a new OrderItem.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 *
	 */
	function buyProduct(productId, quantity, forwardAction, purchaseLocation) 
	{
		ORDER_ITEMS.createItem(quantity, '0', productId, false, purchaseLocation);
		if( forwardAction != null && forwardAction.length > 0 ) {
			recalculateOrder(forwardAction);
		}
	}
	
	/*
	 * Buys a pre-order product by creating a new OrderItem and notifies the
	 * user that this is a pre-order item.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 *
	 */
	function buyPreOrderProduct(productId, quantity, forwardAction, purchaseLocation) 
	{
		alert("This title is due into the warehouse soon, but please be aware it may delay your order");
		buyProduct(productId, quantity, forwardAction, purchaseLocation);
	}
	/*
	 * Buys a product using QuickShop.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 */
	function quickShopProduct(priceListId, productId, forwardAction) {

		var frm = getShopCartForm();
		
		frm.action="QuickShop";
		addFormElement( createURLElement(forwardAction) );
		addFormElement( createQuickShopProductCodeElement(productId) );
		addFormElement( createQuickShopPriceListIdElement(priceListId) );
		addFormElement( createQuickShopPurchaseLocationElement() );
		
		updateForm(forwardAction);

		if( !busy ) {
			busy = true;
			frm.submit();
		}
	}

	/*
	 * Buys a set of selected products by creating a new OrderItem for each.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 */
	function buyProducts(productSelectionList, forwardAction, purchaseLocation) {

		if( productSelectionList != null ) 
		{
			var numSelections = productSelectionList.length;
			if( numSelections != null )
			{
				for( var i=0 ; i < numSelections ; i++ ) {
					if( productSelectionList[i].checked ) {
						ORDER_ITEMS.createItem(1, '0', productSelectionList[i].value, false, purchaseLocation);
					}
				}
			}
			else
			{
				ORDER_ITEMS.createItem(1, '0', productSelectionList.value, false, purchaseLocation);
			}
		}

		if( forwardAction != null && forwardAction.length > 0 ) {
			recalculateOrder(forwardAction);
		}

	}

	/*
	 * Buys a product with book points by creating a new OrderItem.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 */
	function buyProductWithBookPoints(productId, quantity, forwardAction, purchaseLocation) {
		ORDER_ITEMS.createItem(quantity, '1', productId, false, purchaseLocation);
		addFormElement( createProductIdElement(productId) );
		if( forwardAction != null && forwardAction.length > 0 ) {
			recalculateOrder(forwardAction);
		}
	}
	
	/*
	 * Changes the quantity of an existing OrderItem.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 */
	function updateQuantity(orderItemId, quantity, forwardAction) 
	{

		var item = ORDER_ITEMS.getItemById(orderItemId);
		
		if( item != null ) 
		{

			item.setQuantity(quantity);
		}

		if( forwardAction != null && forwardAction.length > 0 ) 
		{
			recalculateOrder(forwardAction);
		}
	}
	
	/*
	 * Updates the quantity of a pre-ordered product whilst notifying the use
	 * of this fact at the same time.
	 *
	 */
	function updatePreOrderQuantity(orderItemId, quantity, forwardAction) 
	{
		alert("This title is due into the warehouse soon, but please be aware it may delay your order");
		updateQuantity(orderItemId, quantity, forwardAction);
	}
	
	/*
	 * Subtracts one from the quantity amount for a particular orderItemId
	 *
	 * If an action is not supplied then just the client side cache will
	 * be updated and changes not commited to the server.
	 */
	function decrementOrderItemQuantityByOne(orderItemId, forwardAction)
	{
		var item = ORDER_ITEMS.getItemById( orderItemId );
		
		if( item.getQuantity() > 2 && item.getQuantity() != 0 )
		{
			item.setQuantity( item.getQuantity() - 1 );
		}
		else
		{
			//if there is only one item left then we will delete it
			item.setDeleted( true );
		}
		
		if( forwardAction != null && forwardAction.length > 0 ) {
			recalculateOrder(forwardAction);
		}		
	}
		
	/*
	 * Deletes an existing OrderItem.
	 *
	 * If an action is not supplied then just the client side
	 * cache will be updated and changes not committed to the server
	 */
	function deleteOrderItem(orderItemId, forwardAction) {
		var item = ORDER_ITEMS.getItemById(orderItemId);
		
		if( item != null ) {
			item.setDeleted(true);
		}
		
		if( forwardAction != null && forwardAction.length > 0 ) {
			recalculateOrder(forwardAction);
		}
	}

	/*
	 * Undeletes a cached OrderItem.
	 */
	function undeleteOrderItem(orderItemId) {
		var item = ORDER_ITEMS.getItemById(orderItemId);
		
		if( item != null ) {
			item.setDeleted(false);
		}
	}

	/*
	 * Adds a promotion code to the order.
	 *
	 * A promotion code can be added by using the default
	 * shopping cart form and changing the initial action.
	 */
	function addPromotionCode(promotionCode, forwardAction) {

		var frm = getShopCartForm();	
		
		frm.action = "PromotionCodeManage";
		handleFreeItems();
		
		addFormElement( createTaskTypeElement("A") );
		addFormElement( createPromotionCodeElement(promotionCode) );
		
		updateForm(forwardAction);
		
		if( !busy ) 
		{
			busy = true;
			frm.submit();
		}
	}

	/*
	 * Removes a promotion code from the order.
	 *
	 * A promotion code can be removed by using the default
	 * shopping cart form and changing the initial action.
	 */
	function removePromotionCode(promotionCode, forwardAction) {
		var frm = getShopCartForm();
		
		frm.action="PromotionCodeManage";
		handleFreeItems();
		
		addFormElement( createTaskTypeElement("R") );
		addFormElement( createPromotionCodeElement(promotionCode) );

		updateForm(forwardAction);

		if( !busy ) {
			busy = true;
			frm.submit();
		}
	}

	/*
	 * Takes user to checkout.
	 *
	 */
	function checkout() {
		var frm = getShopCartForm();
		
		frm.action="BillingAddressView";
		
		if( !busy ) {
			busy = true;
			frm.submit();
		}
	}
	
	/*
	 * Adds a product to the WishList.
	 *
	 * Adding an item to the WishList uses the same parameters
	 * as OrderItemUpdate when adding a new product so we can
	 * use the same form with a different action
	 */
	function addProductToWishList(productId, forwardAction) {
		
		getShopCartForm().action="InterestItemAdd";
		addFormElement( createURLElement(forwardAction) );
		addFormElement( createAccountDisplayOptionElement("interestItem") );
		addFormElement( createWishListCatEntryElement(productId) );
		addFormElement( createErrorViewNameElement("ProductDisplayErrorView") );
		addFormElement( createProductIdElement(productId) );

		
		updateForm(forwardAction);

		if( !busy ) {
			busy = true;
			getShopCartForm().submit();
		}

	}
	
	// flag set to stop duplicate form submissions 
	var busy = false;

	/*
	 * Commits any changes in the client to the server
	 * Action is the chained command after OrderItemUpdate and
	 * OrderCalculate have been exectuted 
	 */
	function recalculateOrder(forwardAction) {
		if( !busy ) {
			busy = true;
			if(ORDER_ITEMS.count() > 0) {
				getShopCartForm().action="OrderItemUpdate";
				handleFreeItems();
				updateForm(forwardAction);
			}
			getShopCartForm().submit();
		}
	}



	/*
	 * Javascript objects to hold client side cache of basket data
	 */

	var MAX_QUANTITY = 200;
	
	function OrderItem(id, quantity, redeem, productId, free, purchaseLocation) {
		this.id = id;
		this.quantity = quantity <= MAX_QUANTITY ? quantity : MAX_QUANTITY;
		this.redeem = redeem;
		this.productId = productId;
		this.free = free;
		this.deleted = false;
		this.field2 = purchaseLocation;
		

		
		this.getField2 = function getField2()
		{
			return this.field2;
		}
		this.setField2 = function setField2( field2 )
		{	
			this.field2 = field2;
		}
		
		this.isRedeemed = function isRedeemed() {
			return (this.redeem != null && this.redeem == '1');
		}

		this.isFree = function isFree() {
			return this.free != null && this.free;
		}

		this.getRedeemed = function getRedeemed() {
			return this.redeem;
		}
		
		this.getQuantity = function getQuantity() {
			if( this.deleted ) {
				return 0; // exit early with a quantity of 0 when item has been deleted
			}
			
			if( isNaN(this.quantity) ) {
				this.quantity = 1;
			}
			
			return this.quantity;
		}
		
		this.setQuantity = function(newQuantity) {
			if( newQuantity > MAX_QUANTITY ) {
				this.quantity = MAX_QUANTITY;
			}
			else {
				this.quantity = newQuantity;
			}
		}
		
		this.getId = function getId() {
			return this.id;
		}
		
		this.getProductId = function getProductId() {
			return this.productId;
		}
		
		this.setDeleted = function setDeleted(deleteFlag) {
			this.deleted = deleteFlag;
		}

		this.isDeleted = function isDeleted() {
			return this.deleted;
		}

	}
	
	function OrderItems() 
	{
		this.itemsByIndex = new Array();
		this.itemsById = new Array();
		
		this.addItem = function addItem(id, quantity, redeem, productId, isFree, purchaseLocation) {
			var newItem = new OrderItem(id, quantity, redeem, productId, isFree, purchaseLocation);
			
//			this.itemsByIndex.push(newItem);
			this.itemsByIndex[this.itemsByIndex.length]= newItem;	
			this.itemsById[id] = newItem;
		}
	
		this.createItem = function createItem(quantity, redeem, productId, isFree, purchaseLocation) {
			var newItem = new OrderItem("-1", quantity, redeem, productId, isFree, purchaseLocation);
			//this.itemsByIndex.push(newItem);
			this.itemsByIndex[this.itemsByIndex.length] = newItem;
		}
		
		this.getItemById = function getItemById(id) {
			return this.itemsById[id];
		}
		
		this.getItemByIndex = function getItemByIndex(index) {
			return this.itemsByIndex[index];
		}
		
		this.count = function count() {
			return this.itemsByIndex.length;
		}
		
		this.getRedeemedItems = function getRedeemedItems() {
			var redeemedItems = new Array();
			var numItems = this.count();
			
			for(var i=0 ; i < numItems ; i++) {
				if( this.itemsByIndex[i].isRedeemed() ) {
					// redeemedItems.push(this.itemsByIndex[i]);
					redeemedItems[redeemedItems.length] =  this.itemsByIndex[i];
				}
			}
			
			return redeemedItems;
		}

		this.getUnredeemedItems = function getUnredeemedItems() {
			var unredeemedItems = new Array();
			var numItems = this.count();
			
			for(var i=0 ; i < numItems ; i++) {
				if( !this.itemsByIndex[i].isRedeemed() ) {
					unredeemedItems[unredeemedItems.length] = this.itemsByIndex[i];
				}
			}
			
			return unredeemedItems;
		}

		this.getFreeItems = function getFreeItems() {
			var freeItems = new Array();
			var numItems = this.count();
			
			for(var i=0 ; i < numItems ; i++) 
			{
				if( this.itemsByIndex[i].isFree() && !this.itemsByIndex[i].isRedeemed() ) 
				{
					freeItems[freeItems.length] = this.itemsByIndex[i];
				}
			}
			
			return freeItems;
		}

		this.getItemByProductId = function getItemByProductId(productId) {
			var numItems = this.count();
			var foundItem = null;
			
			if( productId != null && productId.length > 0 ) {
				for(var i=0 ; i < numItems ; i++) {
					var currentItem = this.itemsByIndex[i];
					if( currentItem.getProductId() == productId && !currentItem.isFree() ) {
						foundItem = currentItem;
					}
				}
			}
			
			return foundItem;
		}
		
		/*
			Finds a matching order item
			The item to be matched is tested for it's redeem value
			A matched item is one that has the opposite redeem value but the
			same product id and is not flagged as free
		*/
		this.getMatchingItem = function getMatchingItem(itemToMatch) {
			var matchItems = null;
			var matchedItem = null;
			
			if( itemToMatch.isRedeemed() )
				matchItems = this.getUnredeemedItems();
			else {
				matchItems = this.getRedeemedItems();
			}
			
			var numItems = matchItems.length;
			
			for(var i=0 ; i < numItems ; i++) {
				if( (matchItems[i].getProductId() == itemToMatch.getProductId()) &&
					!matchItems[i].isFree() ) {
					matchedItem = matchItems[i];
					break;
				}
			}
			
			return matchedItem;
		}
	}

	

	/*
	 * Utility functions for basket management
	 */
	function getSelectedValueFromList(optionList) {
		var selectedValue;
		for( var i=0 ; i < optionList.length ; i++ ) {
			if( optionList[i].selected ) {
				selectedValue = optionList[i].value;
				break;
			}	
		}
		
		return selectedValue;
	} 
	 
	function getOrderCalculateAction(forwardAction) {
		return BASE_CALCULATE_URL + getExtraUrlParamString() + "&URL=" + forwardAction;
	}	
	
	function getExtraUrlParamString() {
		var extraParams = "";
		var numParams = BasketExtraUrlParams.length;
		
		if( numParams > 0 ) {
			extraParams += "&";
			for(var i=0 ; i < numParams ; i++ ) {
				extraParams += BasketExtraUrlParams[i];
				if( (i + 1) < numParams ) {
					extraParams += "&";
				}
			}
		}
				
		return extraParams;
	}
	
	function getShopCartForm() {
	
		return document.ShopCartForm; 
	}

	function updateForm(forwardAction) {
		var numUpdates = ORDER_ITEMS.count();
		
		for( var i = 0 ; i < numUpdates ; i++ ) {
			addFormElement( createOrderItemElement(i) );
			addFormElement( createQuantityElement(i) );
			addFormElement( createRedeemElement(i) );
			addFormElement( createCatEntryElement(i) );
			addFormElement( createPurchaseLocationElement(i) );
		}
		
		addFormElement( createURLElement(forwardAction) );
	}

	/*
		To allow eligibility for free items to be reassessed on each
		order calculate we will mark them as deleted and allow the 
		promotion engine to reallocate as necessary 
	*/
	function handleFreeItems() 
	{
		var freeItems = ORDER_ITEMS.getFreeItems();

		for(var i=0 ;  i < freeItems.length ; i++) 
		{
			if( !freeItems[i].isRedeemed() ) {

				freeItems[i].setDeleted(true);
			}
		}
	}
	
	function addFormElement(element) {
		if( element != null ) {
			getShopCartForm().appendChild( element );
		}
	}
		
	function createOrderItemElement(index) {
		var inputElement = null;
		var orderItemId = ORDER_ITEMS.getItemByIndex(index).getId();
		
		if( orderItemId != "-1" ) {
			inputElement = document.createElement("input");
			inputElement.type = "HIDDEN";
			inputElement.name = "orderItemId_" + (index+1);
			inputElement.value = orderItemId;
		}
		
		return inputElement;
	}
	
	function createQuantityElement(index) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "quantity_" + (index+1);
		inputElement.value = ORDER_ITEMS.getItemByIndex(index).getQuantity();

		return inputElement;
	}
	
	function createRedeemElement(index) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "field1_" + (index+1);
		inputElement.value = ORDER_ITEMS.getItemByIndex(index).getRedeemed();
		
		return inputElement;
	}
	
	function createCatEntryElement(index) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "catEntryId_" + (index+1);
		inputElement.value = ORDER_ITEMS.getItemByIndex(index).getProductId();

		return inputElement;
	}
	
	function createWishListCatEntryElement(index) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "catEntryId";
		inputElement.value = index;
		
		return inputElement;
	}
	
	function createURLElement(forwardAction) {
		var inputElement = null;
		
		if( forwardAction != null && forwardAction.length > 0 ) {
			inputElement = document.createElement("input");
			inputElement.type = "HIDDEN";
			inputElement.name = "URL";
			inputElement.value = getOrderCalculateAction(forwardAction);
		}

		return inputElement;
	}

	// promotion code elements
	function createTaskTypeElement(taskCode) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "taskType";
		inputElement.value = taskCode;
		
		return inputElement;
	}

	function createPromotionCodeElement(promotionCode) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "promoCode";
		inputElement.value = promotionCode.toUpperCase();
		
		return inputElement;
	}

	function createQuickShopProductCodeElement(productId) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "productCode";
		inputElement.value = productId;
		
		return inputElement;
	}

	function createProductIdElement(productId) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "productId";
		inputElement.value = productId;
		
		return inputElement;
	}
	
	function createQuickShopPriceListIdElement(priceListId) {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "priceListId";
		inputElement.value = priceListId;
		
		return inputElement;
	}	
	function createPurchaseLocationElement(index) 
	{
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "field2_" + (index +1);
		inputElement.value = ORDER_ITEMS.getItemByIndex(index).getField2();
		
		return inputElement;
	}
	function createQuickShopPurchaseLocationElement() {
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "field2";
		inputElement.value = "QuickShop";
		
		return inputElement;
	}
	function createBasketViewFlagElement(value)
	{
		var inputElement   = document.createElement("input");
		inputElement.type  = "HIDDEN";
		inputElement.name  = "basketView";
		inputElement.value = value; 
		
		return inputElement;
	}
	function createAccountDisplayOptionElement(value)
	{
		var inputElement   = document.createElement("input");
		inputElement.type  = "HIDDEN";
		inputElement.name  = "accountDisplayOption";
		inputElement.value = value; 
		
		return inputElement;
	}
	function createErrorViewNameElement(errorViewName)
	{
		var inputElement = document.createElement("input");
		inputElement.type = "HIDDEN";
		inputElement.name = "errorViewName";
		inputElement.value = errorViewName;
		
		return inputElement;
	}