/***************************************************
 * Scribendi QuoteWidget
 * 
 * This widget is designed to add AJAX functionality to
 * the Scribendi Quote Form page
 * 
 * @author Kurt Moyst
 * @version 1.5
 * @requires Scribendi
 * @requires Scribendi.EventManager
 * @requires Prototype 1.6
 * @requires Scriptaculous 1.8.1
 * @requires Protoload 02b
 * 
 ***************************************************/
if(!window.Scribendi) {Scribendi = {};}

Scribendi.QuoteWidget = Class.create({
	options: { 	// customizable options	
		error_block_class: 'ferror',
		error_element_class: 'error',
		loading_message: 'Calculating your quote...'
	},
	
	current_price_request: 0, // tracks number of price requests, also used to check the relevance of an ajax response 
	el: {}, // hold element objects
	obs: {}, // holds observers
	category_id: 0,
	service_id: 0,
	quiet_timer: null,
	
	/**
	 * Initialize the Widget
	 * 
	 * Should be run after page has loaded
	 * 
	 * @param {Object}[optional] options - options (ids, classes and copy)
	 */
	initialize: function(options) {
		Object.extend(this.options, options); // set user options		
		this.el = {
			form: 		$('quoteform'),
			word_count:	$('word_count'),
			currency: 	$('currency_id'),
			upsell: 	$('upsell'),
			upsell_box:	$('upsell_box'),
			services:	$('quote_services'),
			category:	$('category_id'),
			form_mode:	$('form_mode'),
			modify_btn:	$('modify_quote'),
			save_btn:	$('save_quote'),
			quote_btn:	$('create_quote'),
			order_btn:	$('create_order'),
			esl_check:  $('esl_upsell_id')
		};

		
		this.original_services = this.el.services.cloneNode(true);
		// get category once
		if (!!this.el.category) {
			this.category_id = this.el.category.getValue();
		}
		// bind event listeners once
		this._rowClickListener = this._onRowClick.bindAsEventListener(this);
		this._submitListener = this._onSubmit.bindAsEventListener(this);
		this.initObservers(); // initialize observers
		
		// if in quote mode, prepare the form
		if (this.el.form_mode.value === 'create_quote') {		
			// hide radio bottons and upsell
			$$('.service_radio').invoke('hide');
			if (this.el.upsell_box) { 
				this.el.upsell_box.hide();
			}
		}
		// if there is a word count already, or none is needed, retrieve prices immediately
		if(!this.el.word_count || ( parseInt(this.el.word_count.getValue().replace(/\D/g,''), 10) > 0 ) ) {
			this.retrievePrices();
		}
	},	
	/**
	 * Initialize Observers
	 * 
	 * This method sets up all the event observers for the quote widget
	 * 
	 */
	initObservers: function() {
		/* 
		if (this.el.word_count) {
			this.obs.word_count = new Form.Element.Observer(this.el.word_count, 0.3, this.retrievePricesOnceQuiet.bind(this));
		} 
		*/
		if (this.el.currency) {
			this.obs.currency = new Form.Element.Observer(this.el.currency, 0.3, this.onChangeCurrency.bind(this));
		}
		if (this.el.upsell) {
			this.obs.upsell = new Form.Element.Observer(this.el.upsell, 0.2, this.retrievePrices.bind(this));
		}
		this.obs.modify = this.el.modify_btn.observe('click', this.retrievePricesEvent.bindAsEventListener(this) );
		this.obs.submit = this.el.form.observe('submit', this._submitListener );
		// ESL upsell listener
		if (this.el.esl_check) {
			this.el.esl_check.observe('click', function(ev){
				if ( ev.element().checked ) {
					location.href = '/service/'+ev.element().getValue()+'#quoteform';
				}
			});
		}
		/* fix IE6 button bug */
		if (this.el.save_btn) {
			this.el.save_btn.observe('click', function(event){
				['create_order', 'create_quote', 'modify_quote'].each(function(item){
					if ( $(item) !== null ) {
						$(item).disabled = true;
					}
				});
			});
		}
	},
	
	/**
	 * Wait for input to stop changing, only retrive in order now mode
	 */
	onChangeCurrency: function() {
		if (this.el.form_mode.value.toLowerCase() === 'create_order') {
			window.clearTimeout( this.quiet_timer );
			this.quiet_timer = window.setTimeout( this.retrievePrices.bind(this), 400 );
		}
	},
	
	retrievePricesEvent: function(event) {
		event.findElement('button').blur();
		event.stop();
		this.retrievePrices();
	},
	
	/**
	 * Retrieve prices and update
	 */
	retrievePrices: function() {
		var price_request_id = ++this.current_price_request; // invalidate older requests
		this.service_id = this.getSelectedService(); // remember current choice 
		if ($('warn_recalc')) {
		    $('warn_recalc').remove();
		}
		// @todo TJ: Timeout detector 
		var ajax_params = {
			ajax_action: 'create_quote',
			category_id: this.category_id,
			service_id: this.service_id
		};
		if (this.el.currency) { 
			ajax_params.currency_id = this.el.currency.getValue();
		}
		if (this.el.upsell) { 
			ajax_params.upsell = this.el.upsell.getValue();
		}
		if (this.el.word_count) {
			ajax_params.word_count = parseInt(this.el.word_count.getValue().replace(/\D/g,''), 10); 
			// Clear quotes if word_count was zeroed
			if(isNaN(ajax_params.word_count) || ajax_params.word_count <= 0) { // if the word count is invalid
				this.el.services.hideLoading();
				this.resetForm();
				return;
			}
			this.clearWordCountError();
		}
		this.el.services.showLoading({ message: this.options.loading_message });
		var req = new Ajax.Request('/quote', {
			method: 'get',
			parameters: ajax_params,
			onSuccess: function(response) {
				if(price_request_id == this.current_price_request) {  
					this.el.services.hideLoading();
					this.el.services.update(response.responseText);
					this.orderNowMode();
				}
			}.bind(this),
			onFailure: function(response) {
				if(price_request_id == this.current_price_request) {  
					this.el.services.hideLoading();
					this.resetForm();
					this.el.form.submit();
				}
			}.bind(this)
		});
	},

	getSelectedService: function() {
		try {
			return $$('.service_radio').find(function(radio) { return radio.checked; }).value;
		} catch(e) {
			return null;
		}
	},
	
	_onRowClick: function(event) {
		if (event.element().tagName.toLowerCase() !== 'input') {
			var radio = event.findElement('tr').down('input');
			if (radio.checked === false) { 
				radio.checked = true; 
			}
		}
	},
	
	/**
	 * Listen for row clicks on enabled services
	 */
	orderNowMode: function() {
		this.clearServiceError();
		this.el.form_mode.value = 'create_order';
		this.el.form.method = 'post';
		this.el.quote_btn.hide();
		this.el.order_btn.show();
		this.el.modify_btn.show();
		if (this.el.save_btn) { this.el.save_btn.show(); }
		if (this.el.upsell_box) { this.el.upsell_box.show(); }
		
		var rows = $$('tr.service');
		if (rows.any()) {
			rows.each( function(row) {
				row.observe('click', this._rowClickListener  );
			}.bind(this) );
		} else {
			this.displayWordCountRangeError();
		}
	},

	resetForm: function() {
		this.el.services.replace(this.original_services.cloneNode(true));
		$$('.service_radio').invoke('hide');
		this.el.services = $('quote_services');
		this.el.form_mode.value = 'create_quote';
		this.el.form.method = 'get';
		if (this.el.upsell_box) { this.el.upsell_box.hide(); }
		if (this.el.save_btn) { this.el.save_btn.hide(); }
		this.el.modify_btn.hide();
		this.el.order_btn.hide();
		this.el.quote_btn.show();
		this.clearServiceError();
	},
	
	
	/**
	 * Ensures the form fields are valid for a successful quote or order
	 * It not, stops submission and displays an error
	 * @param {Event} event
	 */
	_onSubmit: function(event) {
		if (this.el.word_count) {
			var word_count = parseInt(this.el.word_count.getValue().replace(/\D/g,''), 10);
			if (isNaN(word_count) || word_count <= 0) {
				event.stop();
				this.displayWordCountError();
				this.resetForm();
				return false;
			}
		}
		if (this.el.form_mode.value == 'create_quote') {
			event.stop();
			this.retrievePrices();
			return false;
		} else {
			if (this.getSelectedService() === null) {
				event.stop();
				this.displayServiceError();
				return false;
			}
		}
		this.el.form.showLoading({ message: 'Please wait...' });
	},
	
	displayWordCountRangeError: function() {
		if ($('wc_tool_after') == undefined) { 
			this._displayError("Your page count appears too high for this service. Please check your page count or <a href=\"/contact\">contact customer service</a> for assistance.", "wordcount_error", this.el.word_count); }
		else {
			this._displayError("Your word count appears too high for this service. Please check your word count or <a href=\"/contact\">contact customer service</a> for assistance.", "wordcount_error", this.el.word_count); }
	},
	/**
	 * Display a word count error
	 */
	displayWordCountError: function() {
		if ($('wc_tool_after') == undefined) { 
			this._displayError("Please enter a page count", "wordcount_error", this.el.word_count); }
		else { 
			this._displayError("Please enter a word count", "wordcount_error", this.el.word_count); }
	},
	/**
	 * Clear the word count error
	 */
	clearWordCountError: function() {
		this._clearError("wordcount_error", this.el.word_count);
	},
	/**
	 * Display a service error
	 */
	displayServiceError: function() {
		this._displayError("Please select a service", "service_error", this.el.services);
	},
	/**
	 * Clear the service error
	 */
	clearServiceError: function() {
		this._clearError("service_error", this.el.services);
	},
	/**
	 * Display an error above the form row containing the invalid field
	 * 
	 * @param {string} message - Message to display
	 * @param {string} error_id - Id to track for clearing
	 * @param {Element} element - Element to which to add the error class
	 */
	_displayError: function(message, error_id, element) {
		$(element).addClassName(this.options.error_element_class); // Prototype takes care of non-duplication
		// if error with same id exists, update it.
		if($(error_id)) {
			$(error_id).update(message);
		} else {
			var error = new Element("div", {id: error_id}).addClassName(this.options.error_block_class).update(message);		
			if (element.hasClassName('fr')) {
				element.insert({before: error});
			} else {
				element.up('.fr').insert({before: error});
			}
		}
	},
	/**
	 * Clear the error from the quote fieldset
	 * 
	 * @param {string} error_id
	 * @param {Element} invalid_element
	 */
	_clearError: function(error_id, invalid_element) {
		error_id = $(error_id);
		$(invalid_element).removeClassName(this.options.error_element_class);
		if (error_id) {
			error_id.remove();
		}
	}
	
});
