$.extend(true, app, {

	nonce: null, // gets filled in later

	post: function(url, data, callback) {
		app.ajax('post', url, data, callback);
	},

	get: function(url, data, callback) {
		app.ajax('get', url, data, callback);
	},

	ajax: function(type, url, data, callback) {
		$.ajax({
			// first do data
			'type': type,
			'url': url,
			'data': $.extend({ajax: true, ajax_nonce: app.nonce}, data),
			'dataType': 'json',
			'success': function(response_data, code, xhr) {
				if (callback)
				{
					callback.call(this, response_data, code, xhr);
				}
				app.success(response_data, code, xhr);
				ajax_handlers.local.success(response_data, code, xhr);
			}
		});

	},

	// this has to be a method, otherwise the data bit doesn't resolve correctly
	get_ajax_form_options: function() {
		var data = {
			dataType: 'json',
			data: {ajax: true, ajax_nonce: app.nonce},
			success: function(response_data, code, xhr, form) {
				app.success(response_data, code, xhr, form);
				ajax_handlers.local.success(response_data, code, xhr, form);
			}
		};
		return data;
	},

	// give app a chance to act on success stuff.
	success: function(response_data, code, xhr, form) {
		// reset the nonce.
		app.nonce = response_data.nonce;
		app.cart.success(response_data, code, xhr, form);
	},

	init: function() {
		app.cart.init();
		app.checkout.init();
		app.js_ajax_forms.init();
		$("select[multiple] option").mousedown(function(){
			var $self = $(this);

			if ($self.attr("selected"))
				$self.attr("selected", "");
			else
				$self.attr("selected", "selected");

			return false;
		});

		// set up standard responders, but allow them to be overriden elsewhere.
		$.ajaxSetup({
			// now events
			'error': ajax_handlers.local.error,
			'complete': ajax_handlers.local.complete
				// success is above, in the global ajax thingo.
		})

		if (ajax_handlers && ajax_handlers.global && ajax_handlers.global.start)
		{
			$(this).ajaxStart(ajax_handlers.global.start);
		}

		if (ajax_handlers && ajax_handlers.global && ajax_handlers.global.stop)
		{
			$(this).ajaxStop(ajax_handlers.global.stop);
		}

		if (ajax_handlers && ajax_handlers.global && ajax_handlers.global.error)
		{
			$(this).ajaxError(ajax_handlers.global.error);
		}
	}

});

app.cart = {
	add_to_cart: function(product_id) {
		var url = app.cart.url;
		var data = {'add_to_cart': true, 'product_id': product_id, 'quantity': 1};
		app.post(app.cart.url, data, app.cart.add_to_cart_cb);
	},

	remove_from_cart: function(product_id) {
		var url = app.cart.url;
		var data = {'remove_from_cart': true, 'product_id': product_id};
		app.post(app.cart.url, data, app.cart.remove_from_cart_cb);
	},

	remove_from_cart_cb: function(json_data) {
		if (json_data.data.cart_quantity == 0)
		{
			$('.js_emptycart').show();
			$('.js_cart_form').hide();
		} else {
			$('.js_product_row_' + json_data.data.product_id).fadeOut('slow', function() { $(this).remove(); });
			$('.js_cart_subtotal').text(json_data.data.cart_subtotal);
		}
	},

	update_cart_quantity: function(qty) {
		$('.js_cart_quantity').fadeOut('slow', function() {
			$(this).text(qty);
			$(this).fadeIn('slow');
		});
	},

	update_cart_cb: function(data) {
		if (data.cart_quantity == 0)
		{
			$('.js_emptycart').show();
			$('.js_cart_form').hide();
		} else {
			for (var i = 0; i < data.row_data.length; i++)
			{
				var row = data.row_data[i];
				$('.js_rowprice_' + row.id).text(row.row_subtotal);
			}

			$('.js_cart_subtotal').text(data.cart_subtotal);
		}

	},

	success: function(response_data, code, xhr, form)
	{
		if (typeof(response_data.data.cart_quantity) == 'number')
		{
			app.cart.update_cart_quantity(response_data.data.cart_quantity);
		}

		if (response_data.data.action == 'update_cart')
		{
			app.cart.update_cart_cb(response_data.data);
		}
	},

	init: function() {
		// give some functionality to add to cart links
		// note that on the server side, this will add default options
		$('a.js_add_to_cart').click(function(e) {
			e.preventDefault();
			app.cart.add_to_cart($(this).attr('rel'));
		});

		$('form.js_cart_form').bind('submit', function(e) {
			e.preventDefault();

			// use the bind method, so that other places can also bind.
			// note we non-desctructively extend the main ajax form optoins.
			$(this).ajaxSubmit($.extend(true, {}, app.get_ajax_form_options(), { data: {update_cart: true} }));

		});
		// also, ajaxify the add to cart forms.
		$('form.js_add_to_cart').bind('submit', function(e) {
			e.preventDefault();

			// use the bind method, so that other places can also bind.
			// note we non-desctructively extend the main ajax form optoins.
			$(this).ajaxSubmit($.extend(true, {}, app.get_ajax_form_options(), { data: {add_to_cart: true} }));

		});

		// also, add the remove from cart links.
		$('a.js_remove_from_cart').click(function(e) {
			e.preventDefault();
			app.cart.remove_from_cart($(this).attr('rel'));
		});

	}
}

app.checkout = {
	init: function() {

		if ($('#shipping_country').length > 0)
		{
			$('#shipping_country').change(app.checkout.update_address_based_shipping);
			$('#shipping_manyoptions_input').change(app.checkout.update_total_cost);
			app.checkout.update_shipping_options();
		}

		$('#postcode').keyup(app.checkout.update_address_based_shipping);

		// finally, if the postcode already has a value, then we should automatically calculate.
		if (($('#postcode').length > 0) && ($('#postcode').val() != ''))
		{
			app.checkout.update_address_based_shipping();
		}

		function toggle_extra_address()
		{
			if($('#different_address_checkbox input').attr('checked')){
				$('#different_address_details').slideDown();
			} else {
				$('#different_address_details').slideUp();
			}
		}

		$('#different_address_checkbox').click(toggle_extra_address);
		// and on first load, make sure it's in the right state.
		toggle_extra_address();

		// disable double checkouts
		$('.js_checkout').submit(function(e) {
			$('.js_checkout input[type=submit]').attr('disabled', 'disabled');
		});
	},

	// called when the postcode box changes.
	update_address_based_shipping: function() {

		$('.js_shipping_info select, .js_shipping_info input').attr('disabled', true);

		app.post(
			app.checkout.url,
			{
				shipping: {
					address: $('input[name=shipping[address]]').val(),
					city: $('input[name=shipping[city]]').val(),
					postcode: $('input[name=shipping[postcode]]').val(),
					country: $('select[name=shipping[country]]').val()
				},
				'get_shipping_options': true
			},
			function(data, textStatus, xhr) {
				$('.js_shipping_info select, .js_shipping_info input').attr('disabled', false);

				app.checkout.options = data.data.options;
				app.checkout.update_shipping_options();
			}
		);
	},

	update_shipping_options: function() {
		// okay load up shipping options based on the currently selected shipping country. First get it.
		var options = [];
		if (app.checkout.options[$('#shipping_country').val()])
		{
			options = jQuery.merge(options, app.checkout.options[$('#shipping_country').val()]);
		}

		if (app.checkout.options.ANY)
		{
			options = jQuery.merge(options, app.checkout.options.ANY);
		}

		$('.js_shipping_nooptions, .js_shipping_oneoption, .js_shipping_manyoptions').hide();

		if (options == null || options.length == 0)
		{
			$('.js_shipping_nooptions').show();
		} else if (options.length == 1) {
			$('.js_shipping_oneoption').show();
			$('.js_shipping_oneoption_name').html(options[0].name);
			$('.js_shipping_oneoption_cost').html(options[0].cost);

			// we still populate
			app.checkout.populate_options_select(options);
		} else {
			options = $(options).sort('cost', 'asc');
			// many options.
			$('.js_shipping_manyoptions').show();
			app.checkout.populate_options_select(options);
		}

		app.checkout.update_total_cost();
	},

	round_and_pad: function(num, places) {
		var result = String( Math.round(num * Math.pow(10, places)) / Math.pow(10, places) );
		if (result.indexOf('.') < 0)
		{
			result += '.';
		}

		while( result.length - result.indexOf('.') <= places )
		{
			result+= '0';
		}

		return result;
	},

	update_total_cost: function() {
		var shipping = parseFloat($('#shipping_manyoptions_input :selected').attr('cost'));
		if (isNaN(shipping))
		{
			shipping = '...';
			var total = app.checkout.subtotal + ' + shipping';
		} else {
			var total = app.checkout.subtotal + shipping;
		}

		$('.js_shipping_cost').html(shipping);
		$('.js_total_cost').html(app.checkout.round_and_pad(total, 2));
	},

	populate_options_select: function(options) {
		// if we are populating the options box, we should preserve the previously selected item if we can.
		var current = $('#shipping_manyoptions_input').val();

		$('#shipping_manyoptions_input').empty();
		for(var i = 0; i < options.length; i++)
		{
			var el = $('<option></option>')
				.attr({ 'value': options[i].id, 'cost': options[i].cost })
				.text(options[i].name + ': ' + '$' + options[i].cost)

			// mmm actually this is kinda annoying
			// if (options[i].id == current)
			// {
			// 	el.attr({'selected': true});
			// }

			$('#shipping_manyoptions_input').append(el)
		}

	}
}

app.js_ajax_forms = {
	before_submit: function(formData, jqForm, options) {
		// not yet needed.
	},

	after_success: function(responseText, statusText, xhr, form) {

	},

	add_to_js_ajax_forms: function(forms) {
		$(forms).ajaxForm(this.form_options);
	},

	init: function() {
		$('.ajax_app_forms').ajaxForm(this.form_options);
	}
}

// we have to define the options after we have already created the object.
app.js_ajax_forms.form_options = {
	beforeSubmit:  app.js_ajax_forms.before_submit,  // pre-submit callback
	success:       app.js_ajax_forms.after_success,  // post-submit callback
	dataType:      'json'
};

$(document).ready(function() {
	app.init();
});

