function isSet(variable) {
	return (typeof(variable) != 'undefined');
}

if (!isSet(CART_DATA)) {
	var CART_DATA = null;
}

if (!isSet(CURRENCY)) {
	var CURRENCY = null;
}

if (!isSet(STORE_HANDLE)) {
	var STORE_HANDLE = null;
}

if (!isSet(SERVER)) {
	var SERVER = null;
}


/* TEMPLATING ============================================================================== */

(function() {
	var cache = {};

	this.tmpl = function tmpl(str, data) {

		// Figure out if we're getting a template, or if we need to
		// load the template - and be sure to cache the result.
		var fn = !/\W/.test(str) ?
		cache[str] = cache[str] ||
			tmpl(str) :

		// Generate a reusable function that will serve as a template
		// generator (and which will be cached).
		new Function("obj",
			"var p=[],print=function(){p.push.apply(p,arguments);};" +
	
			// Introduce the data as local variables using with(){}
			"with(obj){p.push('" +
	
			// Convert the template into pure JavaScript
			str
			  .replace(/[\r\t\n]/g, " ")
			  .split("<%").join("\t")
			  .replace(/((^|%>)[^\t]*)'/g, "$1\r")
			  .replace(/\t=(.*?)%>/g, "',$1,'")
			  .split("\t").join("');")
			  .split("%>").join("p.push('")
			  .split("\r").join("\\'")
		  + "');}return p.join('');");

		// Provide some basic currying to the user
		return data ? fn( data ) : fn;
	};
})();

/* SHOPPING CART ============================================================================== */


Cart = function() {
	
	// Configuration	
	// this.standardServer = "enstoresecured.appspot.com";
	this.standardCurrency = "USD"
	
	// Cart instance variables
	this.items = [];
	this.script = null; 
	this.element = null;
	this.uuidFormat = /^[A-Fa-f0-9]{32}$/;
	
	// Subsribed events
	this.listeners = {'preFetch': [], 'postFetch': [], 'preRender': [], 'postRender': []};

	/* POPERTIES ======================================================================== */
	
	this._updateCounter = 0;
	this.products = [];
	this.totalProducts = 0;
	this.totalQuantity = 0;
	this.tax = 0;
	this.discount = 0;
	this.subtotal = 0;
	this.total = 0;
	this.shippingMethodUUID = null;
	this.paymentMethodUUID = null;
	this.couponCode = null;
	this.customer = null;
	this._formatter = null;
	this.noTax = false;

	/* URLS ============================================================================= */
	
	this.storeName = function() {
		/*
		Return the store name. The store name should be set in the template head by 
		defining STORE_HANDLE. If it was not set we notify the user (let's hope the 
		developer) with a warning.
		*/
		
		if (STORE_HANDLE != null) {
			return STORE_HANDLE;
		} else {
			alert('The store handle has not been set. Set it in the header of the page by defining: STORE_HANDLE');
		};
	};

	// this.server = function() {
	// 	
	// 	return "http://enstoresecured.appspot.com";
	// 	
	// 	var protocol = (("https:" == document.location.protocol) ? "https://" : "http://");
	// 	
	// 	if (SERVER != null) {
	// 		return protocol + SERVER;
	// 	} else {
	// 		return protocol + "enstoresecured.appspot.com";
	// 	};
	// };

	this.orderPreviewURL = function() {
		return DATA_URL + "/cart.jsonp?";
	};

	this.checkoutURL = function() {
		return CHECKOUT_URL;
	};
	
	this.cookieName = function() {
		return "cart." + STORE_HANDLE;
	};

	
	/* CURRENCY FORMATTING ================================================================= */ 

	this.currencyCode = function() {
		
		if (CURRENCY != null) {
			return CURRENCY;
		} else {
			return this.standardCurrency;
		};
	};
	
	this.formatter = function() {
		/*
		Returns a formatter object to use for number formatting. Uses the default currencyCode if
		no other was defined in the html document.
		*/
				
		if (this._formatter == null) {
			
			var selectedCurrencyCode = POSIX_LC_MONETARY[this.currencyCode()];
			
			if (!selectedCurrencyCode) {
				alert("Currency formatting for " + this.currencyCode() + " not available. Please contact support.")
			}

			this._formatter = new MonetaryFormatter(selectedCurrencyCode);
		}
		
		return this._formatter;
	}
	
	this.formatCurrencies = function() {
		/*
		Format all the currencies in the page. Currencies should be embedded in a span
		with the class currency. After formatting it we change the class to formattedCurrency
		so we don't process it twice.
		*/
		
		$(".currency").each(function() {
			$(this).text(enstore.cart.formatter().format($(this).text()));
			$(this).removeClass("currency");
			$(this).addClass("formattedCurrency");
		});		
	}
	
	/* COOKIES ============================================================================= */

	this.fromCookie = function() {
		
		cookieValue = $.cookie(this.cookieName());
		
		// If we did not find a cookie by now, we can forget it
		if (cookieValue == null || cookieValue == '') {
			this.fireEvent('preFetch')
			this.render({"lines": [], "subtotal": 0, "tax": 0, "total": 0});
		
		// But now we have a valid cookie, we can set the contents of the cart
		} else {
			this.loads(cookieValue);
			this._fetch();
		};
	};

	this.toCookie = function() {
		$.cookie(this.cookieName(), this.dumps(), {expires: 30, path: '/'});
	};
	
	/* CART ACTIONS ========================================================================== */

	this.isUpdating = function() {
		/*
		Check if the cart is currently updating.
		*/
		
		if (enstore.cart._updateCounter == 0) {
			return false;
		} else {
			return true;
		}
		
	}

	this.addItem = function(uuid) {
		/*
		Add the item with given uuid to the shopping cart.
		*/
		
		if (uuid.match(this.uuidFormat) == null) {
			return;
		};
		
		var itemExists = false;
		
		// Check if the item is already in the cart, update it's quantity by 1
		for (index in this.items) {
			if (this.items[index][0] == uuid) {
				this.items[index][1] += 1;
				itemExists = true;
			};
		};
		
		// If the item was not yet in the cart, add it now
		if (itemExists == false) {
			this.items.push([uuid, 1]);
		};
			
		this.toCookie();
		this._fetch();
	};

	this.decreaseItem = function(uuid) {
		/*
		Decrease item with given uuid to the shopping cart.
		*/
		
		if (uuid.match(this.uuidFormat) == null) {
			return;
		};
		
		// Check if the item is already in the cart, decrease it's quantity by 1
		for (index in this.items) {
			if (this.items[index][0] == uuid) {
				this.updateItem(uuid, this.items[index][1] - 1);
			};
		};
	};
	
	this.updateItem = function(uuid, quantity) {
		/*
		Update the given items quantity.
		*/
		
		// We don't do anything if the quantity is below zero
		if (parseInt(quantity) < 0) {
			return;
		}
		
		// Loop through all the items
		for (index in this.items) {
			
			if (this.items[index][0] == uuid) {
				
				// Remove the item if the new quantity is set to 0
				if (quantity == 0) {
					this.items.splice(index, 1);
					
				// Otherwise set the quantity for this item to the new quantity
				} else {
					this.items[index][1] = parseInt(quantity);
				};
			};
		};
		
		this.toCookie();
		this._fetch();
	};
	
	this.removeItem = function(uuid) {
		/*
		Remove an item from the cart.
		*/
		
		this.updateItem(uuid, 0);
	};
	
	this.clear = function() {
		/*
		Clear all the items in the cart.
		*/
		
		// Clear all the items
		this.items = [];

		this.tax = 0;
		this.discount = 0;
		this.subtotal = 0;
		this.total = 0;		
		this.products = [];
		this.totalProducts = 0;
		this.totalQuantity = 0;
		
		// Update the cart
		this.toCookie();
		
		// Render the cart
		this.render({"lines": [], "subtotal": 0, "tax": 0, "discount": 0, "total": 0});
		
	};
	
	this.checkout = function() {
		/*
		Redirects the user to the checkout page by generating a post request
		including the product data. To do so we create a form tag with a hidden
		field and submitting it.
		*/
		
		// Post the product data to the store checkout page
		var form = document.createElement("form");
		
		// Create the form
		form.setAttribute("method", "POST");
		form.setAttribute("action", this.checkoutURL());
		
		// Add the products as a hidden input field
		var postData = document.createElement("input");
		postData.setAttribute("type", "hidden");
		postData.setAttribute("name", "cartData");
		postData.setAttribute("value", this.dumps());
		
		form.appendChild(postData);
		
		// Submit the form
		document.body.appendChild(form);

		try {
			_gaq.push(['t2._linkByPost', form]);
		} catch(err) {}
		
		form.submit()
		
	};
	
	this.isEmpty = function() {
		/*
		Returns wether this cart has products.
		*/
		if (this.items.length == 0) {
			return true;
		} else {
			return false;
		};
	};
	
	/* SERIALIZATION ========================================================================== */
	
	this.dumps = function() {
		/*
		Dump the cart data to a string.
		*/
		
		var data = '';
		
		// Convert the products to a single string
		for (var i = 0; i < this.items.length;	i++) {
			data += this.items[i][0] + ':' + this.items[i][1] + ',';
		};
		
		return data;
	
	};

	this.loads = function(data) {
		/*
		Load the cart data from a serialized string.
		*/
		
		var dataItems = data.split(',');
		
		for (index in dataItems) {
			
			var uuid = dataItems[index].split(':')[0];
			var quantity = parseInt(dataItems[index].split(':')[1]);
			
			// We have to make sure this is data we can actually use, so we check the uuid format
			if (uuid.match(this.uuidFormat) != null) {
				this.items.push([uuid, quantity]);
			} 
		};
	
	};
	
	/* RENDERING ========================================================================== */
	
	this._fetch = function() {
		
		// Notify we're updating
		enstore.cart._updateCounter += 1;
		
		// Fire the custom event for pre fetching 
		this.fireEvent('preFetch');
		
		url = this.orderPreviewURL();
		url += "products=" + this.dumps();
		
		// If the customer was set for this order, we'd want to get the shipping rate
		if (this.customer != null) {
			
			if (this.shippingMethodUUID) {
				url += "&shippingMethodUUID=" + this.shippingMethodUUID;
			};
			
			if (this.couponCode) {
				url += "&couponCode=" + this.couponCode;
			}
			
			url += "&destinationCountryCode=" + this.customer.shippingCountryCode;
			url += "&destinationStateCode=" + this.customer.shippingStateCode;
			url += "&destinationZipCode=" + this.customer.shippingZipCode;

		}
		
		if (this.noTax == true) {
			url += "&noTax=1";
		}
		
		if (this.paymentMethodUUID)
			url += "&paymentMethodUUID=" + this.paymentMethodUUID;
		
		url += "&callback=?";
		
		$.getJSON(url,
			function(data) {
				enstore.cart.render(data);
				enstore.cart._updateCounter -= 1;
			}
		);
	}
	

	this.render = function(order) {
		/*
		Renders the cart on the page based on the given order data. It either uses
		the inline defined template, or one that is overriden by the user on the html
		page by defining a <script id="cart_tmpl"> having template data.
		*/

		this.tax = order.tax;
		this.discount = order.discountTotal;
		this.subtotal = order.subtotal;
		this.total = order.total;
		this.products = order.lines;
		this.totalProducts = this.products.length;
		this.totalQuantity = 0;
		
		for (i = 0; i < this.products.length; i++) {
			this.totalQuantity += parseInt(this.products[i].quantity);
		}
		
		this.fireEvent("postFetch");
		
		// Fire the custom event for pre rendering 
		this.fireEvent("preRender");
		
		if (this.isEmpty()) {
			$("#empty_cart").show();
			$("#empty_cart p").show();
			$("#cart").hide();

		} else {
			
			$("#empty_cart").hide();
			$("#cart").show();
			
			$("#cart").item(order).chain();
			$("#lines").items(order.lines).chain();
			$("#tax_totals").items(order.taxes).chain();
			
			// Bind cart actions
			$("#cart_checkout").click(function() {enstore.cart.checkout()});
			$("#cart_clear").click(function() {enstore.cart.clear()})
			
			// Quantity field updates
			$("#cart :input").each(function(i) {	
				$(this).change(function() {
					enstore.log(order.lines[i].productUUID + " -> " + $(this).attr("value"));
					enstore.cart.updateItem(order.lines[i].productUUID, $(this).attr("value"));
				});
			});
			
			// Alternating row background
			$("#cart table.alternating tr:even").addClass("odd");
			$("#cart table.alternating tr:odd").addClass("even");
			
			// Format all the currencies
			$("#cart .formattedCurrency").addClass("currency");
			$("#cart .formattedCurrency").removeClass("formattedCurrency");
			this.formatCurrencies();
			
		}


		// Fire the custom event for post rendering 
		this.fireEvent('postRender');


		
	};
	
	/* EVENTS  ============================================================================ */
	
	this.addListener = function(name, handler) {
		/*
			Adds a callback for an event with the given name. 
		*/
		
		// Make sure the event exists
		if (this.listeners[name]) {
			
			// Add the handler
			this.listeners[name].push(handler);
		} 
	}
	
	
	this.removeListener = function(name, handler) {
		/*
			Removes the given callback for the event with the given name.
		*/
		
		// Iterate over all callbacks for the event
		for (i = 0; i < this.listeners[name].length; i++) {
			
			// Get the current handler
			currentHandler = this.listeners[name][i];
			
			// Determine if this is the callback to remove
			if (currentHandler === handler) {
								
				// Remove the callback
				this.listeners[name].splice(i, 1);
			}
		}
	}
	
	this.fireEvent = function(name) {
		/*
			Fires the event with the given name.
		*/
		
		// Iterate over all callbacks for the event
		for (i = 0; i < this.listeners[name].length; i++) {
			
			// Get the current handler
			currentHandler = this.listeners[name][i];
			
			// Make sure the handler is a function
			if (typeof currentHandler === 'function') {
				
				// Call the handler
				this.listeners[name][i]();
				
			}
		}
		
	}
};

/* INITIALIZATION ========================================================================== */


enstore = {
	'cart': new Cart(),
	'log': function(msg) {
		if (window.console) {
			window.console.log(msg);
		} else {
			return;
		};
	}
};

$(document).ready(function () {
	
	enstore.log("Initializing cart for: " + enstore.cart.storeName() + " with currency " + enstore.cart.currencyCode());
	
	if (CART_DATA == null) {
		
		if (window.location.href.indexOf('clear-cart=true') != -1) {
			enstore.log("Clearing cart");
			enstore.cart.clear();
		}
		
		enstore.cart.fromCookie();
	} else {
		enstore.cart.loads(CART_DATA);
		enstore.cart._fetch();
	};
	
	enstore.cart.formatCurrencies();
	
});


