/**
 * @fileoverview OrderForm module
 * @author Josh Johnston josh@xhtmlized.com
 */

/**
 * OrderForm: handles behaviour for the order form
 * @namespace
 */
var OrderForm = function(){
	var _baseElm;
	var _updatePriceIntervalId;

	/*global $ */
	var self = /** @scope OrderForm */{
		/**
		 * Initializes the form
		 */
		start: function(baseElm){
			_baseElm = $(baseElm);

			// handle changes to the number of pages
			$('#num_pages')
				.bind('focus', self.startAutoUpdatePrice)
				.bind('blur', self.stopAutoUpdatePrice)
				.bind('blur', self.enforceMinPages);

			// handle changes to selected pricing option
			$('#pricing-options input[type=radio]').click(self.doPricingOptionClick);

			// allow clicking anywhere in the box
			$('#pricing-options li').click(function(event) {
												 $(this).find('input').click();
												 self.doPricingOptionClick();
											 });

			// handle changes to selected payment option
			$('#payment-options input[type=radio]')
				.bind('click', self.updatePaymentFields);

			// handle changes to country
			$('#country').bind('change', self.updatePriceOptions);

			// validate before submit
			_baseElm.bind('submit', self.handleSubmit);

			// make sure that we always exit submitting state before leaving the page
			$(window).bind('beforeunload', self.exitSubmittingState);

			// do an update to start
			self.updatePriceOptions();
			self.updateSelectedPriceOption();
		},

		doPricingOptionClick: function() {
			self.updatePaymentFields();
			self.updateSelectedPriceOption();
		},

		enterLoggedInState: function() {
			_baseElm.addClass('loggedin');
		},

		exitLoggedInState: function() {
			_baseElm.removeClass('loggedin');
		},

		/**
		 * Start auto-updating the price options
		 */
		startAutoUpdatePrice: function() {
			window.clearInterval(_updatePriceIntervalId);
			_updatePriceIntervalId = window.setInterval(
				self.updatePriceOptions, 100);
		},

		/**
		 * Stop auto-updating the price options
		 */
		stopAutoUpdatePrice: function() {
			window.clearInterval(_updatePriceIntervalId);
		},

		/**
		 * Enforce the minimum number of pages
		 */
		enforceMinPages: function() {
			var elm = $('#num_pages');
			var value = elm.val() * 1;
			if (value < 2) {
				elm.val(2);
			}
		},

		/**
		 * Set the 'enabled' state of a price option
		 * @param {jQueryElement} elm The li element
		 * @param {boolean} isEnabled
		 */
		setPriceOptionEnabled: function(elm, isEnabled) {
			var inputElm = elm.find('input');
			if (isEnabled) {
				elm.addClass('active');
				inputElm.removeAttr('disabled');
			}
			else {
				elm.removeClass('active');
				inputElm.attr('disabled', 'disabled');
			}
		},

		/**
		 * Set the displayed price value of a price option
		 * @param {jQueryElement} elm The li element
		 * @param {string} value
		 */
		setPriceOptionValue: function(elm, value) {
			var currencySymbol = self.getCurrencySymbol();
			var priceText = (value == 'variable')
				? '' : value;

			if (typeof priceText == 'number') {
				priceText = currencySymbol+priceText;
			}

			elm.find('.price').text(priceText);
		},

		/**
		 * Update the price options
		 */
		updatePriceOptions: function() {
			var currencySymbol = self.getCurrencySymbol();
			var priceOptions = self.getPriceOptions();
			var optionGroupElm = $('#pricing-options');
			optionGroupElm.find('li').each(function() {
												 var elm = jQuery(this);
												 var key = elm.find('input').attr('value');
												 var isEnabled = !!priceOptions[key];
												 self.setPriceOptionEnabled(elm, isEnabled);
																			 self.setPriceOptionValue(elm, isEnabled ? priceOptions[key].final_price_pre_discount : 'n/a');
											 });

			// refresh the options
			var inputElm = optionGroupElm.find('input[name=pricing_option]:checked');

			// if there is no currently selected option, or if it's disabled, pick the first enabled option
			if (!inputElm.length || inputElm.is(':disabled')) {
				inputElm = optionGroupElm.find('li.active:eq(0) input');
			}

			// set the selected radio button
			inputElm.attr('checked', 'checked');

			self.updatePaymentFields();
			self.updateSelectedPriceOption();
		},

		updateSelectedPriceOption: function() {
			var groupElm = $('#pricing-options');

			// remove selected marker
			groupElm.find('li.selected').removeClass('selected');

			// find the selected radio and make its container selected
			groupElm.find('input:checked').parent('li').addClass('selected');
		},

		updatePaymentFields: function() {
			var optionGroupElm = $('#pricing-options');
			var currentOptionName = $('input[name=pricing_option]:checked').val();
			var priceOptions = self.getPriceOptions();
			var currentOptionData = priceOptions[currentOptionName];
			var totalPaymentElm = $('#total-payment');
			var paymentOptionsElm = $('#payment-options');
			var selectedPriceElm = $('#price-selected');
			var upfrontOptionElm = $('#payment_option-upfront');
			var paylaterOptionElm = $('#payment_option-later');
			var basePrice;
			var discountedPrice;
			var currencySymbol = self.getCurrencySymbol();

			// variable price
			if (currentOptionData == 'variable') {
				// update markers
				totalPaymentElm.addClass('variable-price');
				paymentOptionsElm.addClass('variable-price');

				// disable the 'pay up front' option
				upfrontOptionElm.attr('disabled', 'disabled');
				upfrontOptionElm.parent().addClass('disabled');

				// select the 'pay later' option
				paylaterOptionElm.attr('checked', 'checked');

				selectedPriceElm.html('~');
			}
			// fixed price
			else {
				// update markers
				totalPaymentElm.removeClass('variable-price');
				paymentOptionsElm.removeClass('variable-price');

				// enable the 'pay up front' option
				upfrontOptionElm.removeAttr('disabled');
				upfrontOptionElm.parent().removeClass('disabled');

				// set the price
				basePrice = currentOptionData.final_price_pre_discount;
				discountedPrice = currentOptionData.final_price;
				if (basePrice != discountedPrice) {
					selectedPriceElm.html(['<del>'+currencySymbol, basePrice, '</del> '+currencySymbol, discountedPrice].join(''));
				}
				else {
					selectedPriceElm.html(currencySymbol + discountedPrice);
				}

			}
		},

		getCurrencySymbol: function() {
			var country = $('#country').val();
			var country_code = config.currency.country_defaults[country]
				? config.currency.country_defaults[country].currency_code
				: 'USD';
			return config.currency_info[country_code].symbol;
		},

		/**
		 * Get price factors
		 * @return {hash}
		 */
		getPriceFactors: function() {
			var upfrontOptionElm = $('#payment_option-upfront');
			var paymentOption = (!upfrontOptionElm.is(':disabled') && upfrontOptionElm.is(':checked'))
				? 'pay_upfront' : 'pay_later';

			return {
				num_pages: $('#num_pages').val(),
				country: $('#country').val(),
				payment_option: paymentOption
			};
		},

		/**
		 * Get price options
		 * @return {hash}
		 */
		getPriceOptions: function() {
			return PriceCalculator.getPriceOptions(self.getPriceFactors());
		},

		/**
		 * Handle the submit event
		 * @param {DOMevent} event
		 */
		handleSubmit: function(event) {
			event.preventDefault();

			// prevent submission of an invalid form
			var validationErrors = self.validateForm();
			self.updateValidState(validationErrors);

			if (self.isFormValid(validationErrors)) {
				var orderData = self.getFormValues();
				var url = '/app/orders/new/';
				self.enterSubmittingState();
				var request = new $.ajax({
					url: url,
					type:'post',
					 dataType: 'json',
					 data: orderData,
					 success: function(response, textStatus) {
						 if (response.result == 'success') {
							 if (orderData.payment_option == 'upfront') {
								 location.replace('/customer/invoices/view/?projects_id='+response.project_record.id);
							 }
							 else {
								 location.replace('/customer/projects/overview/?projects_id='+response.project_record.id);
							 }
						 }
						 else if (response.result == 'invalid_record') {
							 self.updateValidState(response.validation_errors);
						 }
					 },
					 error: function(XMLHttpRequest, textStatus, errorThrown) {
						 self.updateValidState([{field: 'host', result: 'nocontact'}]);
					 },
					 complete: function() {
						 self.exitSubmittingState();
					 }
					});
			}
		},

		/**
		 * Show that the form is submitting
		 */
		enterSubmittingState: function() {
			_baseElm.addClass('submitting');

			// disable the submit button
			$('#total-payment').find('button').attr('disabled', 'disabled');
		},

		/**
		 * Show that the form is not submitting
		 */
		exitSubmittingState: function() {
			_baseElm.removeClass('submitting');

			// re-enable the submit button
			$('#total-payment').find('button').removeAttr('disabled');
		},

		/**
		 * Validate the form
		 * @return {array}
		 */
		validateForm: function() {
			var data = self.getFormValues();
			var validationErrors = [];
			var isLoginMode = _baseElm.hasClass('login-mode');

			// not logged in
			if (!_baseElm.hasClass('loggedin')) {
				// login mode
				if (isLoginMode) {
					// check username
					if (!data.username) {
						validationErrors.push({field: 'username', result: 'missing'});
					}

					// check password
					if (!data.password) {
						validationErrors.push({field: 'password', result: 'missing'});
					}
				}
				// register mode
				else {
					// check email address
					if (!data.email.match(/^.+\@.+\..+$/)) {
						validationErrors.push({field: 'email', result: 'invalid'});
					}

					// check username
					if (!data.new_username) {
						validationErrors.push({field: 'new_username', result: 'missing'});
					}
					else if (!data.new_username.match(/^[a-z]{4,30}$/i)) {
						validationErrors.push({field: 'new_username', result: 'invalid'});
					}

					// check password
					if (!data.openid) {
						if (!data.new_password) {
							validationErrors.push({field: 'new_password', result: 'missing'});
						}
						else if (data.new_password.length < 6 || data.new_password.length > 30) {
							validationErrors.push({field: 'new_password', result: 'invalid'});
						}

						// check password is retyped exactly
						if (data.new_password != data.new_password_confirm) {
							validationErrors.push({field: 'new_password_confirm', result: 'mismatch'});
						}
					}

					// check country
					if (!data.country) {
						validationErrors.push({field: 'country', result: 'missing'});
					}

					// check first name
					if (!data.first_name) {
						validationErrors.push({field: 'first_name', result: 'missing'});
					}
				}
			}

			// check project name
			if (!data.project_name) {
				validationErrors.push({field: 'project_name', result: 'missing'});
			}

			// check number of pages
			if (!data.num_pages.match(/\d+/)) {
				validationErrors.push({field: 'num_pages', result: 'nan'});
			}

			return validationErrors;
		},

		/**
		 * Reset all validation markers
		 */
		resetValidState: function() {
			_baseElm.removeClass('validation-error');
			_baseElm.find('.invalid').removeClass('invalid');
			_baseElm.find('.validation li.active').removeClass('active');
		},

		/**
		 * Update the form's "valid" state
		 * @param {hash} validationErrors
		 */
		updateValidState: function(validationErrors) {
			var inputElm;
			var messageElm;
			var field;
			var result;

			self.resetValidState();
			if (!self.isFormValid(validationErrors)) {
				_baseElm.addClass('validation-error');

				// load validation error template
				var request = new $.ajax({
					url: '/customer/validateorder/',
					type:'get',
					 data: {errors: JSON.stringify(validationErrors)},
					 success: function(response, textStatus) {
						 // populate the error message
						 _baseElm.find('.validation').html(response);

						 // show invalid fields
						 for (var i=0, len=validationErrors.length; i<len; i++) {
							 field = validationErrors[i].field;
							 result = validationErrors[i].result;

							 // mark the field invalid
							 _baseElm.find('input[name='+field+'],select[name='+field+']').parent('li').addClass('invalid');
						 }

						 // scroll to the top of the form
						$.scrollTo($('#content'), 400);
					 },
					 error: function(XMLHttpRequest, textStatus, errorThrown) {
						 self.updateValidState([{field: 'host', result: 'nocontact'}]);
					 },
					 complete: function() {
						 self.exitSubmittingState();
					 }
					});
			}

		},

		/**
		 * Tell if form data is valid
		 * @param {hash} validationErrors
		 * @return {boolean}
		 */
		isFormValid: function(validationErrors) {
			return validationErrors.length == 0;
		},

		/**
		 * Extract all values from the form
		 * @return {hash}
		 */
		getFormValues: function() {
			var fields = {};
			_baseElm.find('input[type=text],input[type=password],input[type=hidden],select,textarea,input[type=radio]:checked,input[type=checkbox]:checked').each(function() {
											var elm = jQuery(this);
											fields[elm.attr('name')] = elm.val();
										});
			return fields;
		}

	};

	return self;
}();
