/**
 * TaxiCode Local sites JS library
 */


var TCL_Storage = {

	/**
	 * set item to local storage
	 * @param {string} key, the key of the new storage property
	 * @param {string, boolean} value, the value of the new storage property
	 */
	setItem : function(key, value) {
		localStorage.setItem(key, value);
	},

	/**
	 * fetch item from local storage based on key provided
	 * @param {string} key, the key that stores the value to be fetched
	 */
	getItem : function(key) {
		return localStorage.getItem(key);
	},

	/**
	 * remove item from local storage based on key provided
	 * @param {string} key, the key that stores the value to be removed
	 */
	removeItem : function(key) {
		localStorage.removeItem(key);
	},

	/**
	 * clear all local storage data
	 */
	reset : function() {
		localStorage.clear();
	}

};


var Validation = {

	// list of available types
	types : {
		LENGTH_CHECK : 'length_check',
		MIN_CHARS_CHECK : 'min_chars',
		MAX_CHARS_CHECK : 'max_chars_check',
		NUMERIC_CHECK : 'numeric_check',
		NAME_CHECK : 'name_check',
		EMAIL_CHECK : 'email_check',
		PHONE_CHECK : 'phone_check'
	},

	// list of regex
	regex: {
		name : /^[a-zA-ZÀ-ÿ\- ]+$/,
		telephone : /^[\d\s\+\x\(\)\-]+$/,
		email : /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i,
	},

	/**
	 * Method to do a specific type validation on value provided
	 * This method does not support error messages, either provide custom ones after check or use validateBulk method
	 * @param {string} type, the type of validation based on Validation.types object
	 * @param {string} value, the value to be checked
	 * @param {object | undefined} options, any extra options needed for validation i.e. min, max, etc
	 * @return {boolean}
	 */
	validate : function(type, value, options) {

		switch (type) {

			case this.types.LENGTH_CHECK:
				return value.length;

			case this.types.MIN_CHARS_CHECK:
				return value.length >= options.min;

			case this.types.MAX_CHARS_CHECK:
				return value.length <= options.max;

			case this.types.NUMERIC_CHECK:
				return !isNaN(value);

			case this.types.NAME_CHECK:
				return this.regex.name.test(value);

			case this.types.EMAIL_CHECK:
				return this.regex.email.test(value);

			case this.types.PHONE_CHECK:
				return this.regex.telephone.test(value);

			default:
				return true;

		}

	},

	/**
	 * Validate a data set instead of doing an individual check for every single field
	 * Useful in if conditions with AND operator i.e if (condition && condition && condition...) -> if (validateBulk(data))
	 * @param {object} data, the list to go through validation
	 *                      data = {
	 *                          object : {
	 *                              value : '',
	 *                              validate : {
	 *                                            types : [LENGTH_CHECK, NUMERIC_CHECK, ...],
	 *                                            errors : ['LENGTH_CHECK fails message', 'NUMERIC_CHECK fails message', ...]
	 *                                            options : { min : 3, max : 10, ....} | undefined,
	 *                                         } | false
	 *                          }
	 *                      }
	 * @return {object}
	 */
	validateBulk : function(data) {

		var valid = true,
			error = null;

		objectLoop:
		for (var obj in data) {

			// get current validate object
			var current = data[obj];

			// check if obj requires validation
			if (current['validate']) {

				// check value with all required types
				for (var i = 0; i < current['validate']['types'].length; i++) {

					// use type to determine if value is valid
					valid = this.validate(current['validate']['types'][i], current.value, current['validate']['options']);

					// if not valid, skip next iterations
					if (!valid) {
						error = current['validate']['errors'][i];
						break objectLoop;
					}

				}

			}

		}

		return {
			valid : valid,
			error : error
		};

	}

};

var TCL = {
	config : {
		form : {
			ONE_WAY : 'one-way',
			RETURN : 'return',
			MAP_CONTAINER : '.map-container',
			MAIN_ELEMENT : '.TCL-form',
			MODAL_MAIN_ELEMENT : '.TCL-form.modal-form',
			JOURNEY_CONTAINER : '.journey-type-container',
			INPUT_FIELD : '.TCL-input-field',
			QUOTES_BUTTON : '.quotes-button',
			ADVANCED_BUTTON : '.advanced-button',
			BUTTON_FIELD : '.TCL-button-field',
			ACTION_BUTTON : '.action-button',
			VEHICLES_CONTAINER : '.vehicles-container',
			VEHICLE_BUTTON : '.vehicle-action-card',
			SEARCH_ELEMENT : '.icon-search-content',
			SUGGESTION_CONTAINER : '.suggestions-list',
			ADVANCED_CONTAINER : '.advanced-settings',
			SEARCH_THRESHOLD : 1,
			SEARCH_TIMER : 0,
			TIMER_INTERVAL_ID : null,
			MIN_SEARCH_CHARS: 3,
			DISABLED_KEY_CODES : [
				37, // left key
				39, // right key
				32, // space
				36, // page up
				35, // page down
				34, // scroll down
				33, // scroll up
				46, // delete
				20, // Caps Lock
				16, // shift
				17, // ctrl
				18, // alt
				192, // `
				91, // cmd
				93, // cmd
				106, // *
				107, // +
				109, // -
				111, // /
				186, // ;
				187, // +=
				189, // -
				219, // [
				220, // \
				221 // ]
			],
			MAX_VIAS : 8,
			SALES_SEARCH : false,
			TODAY : new Date(),
			RESPONSE : {}
		},
		request : {
			GET_REQUEST : 'GET',
			POST_REQUEST : 'POST'
		},
		generic : {
			ERROR_TYPE : 'error',
			SUCCESS_TYPE : 'success',
			NOTIFICATION_TIMEOUT : 4000,
			MONTHS : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
			UP : 'up',
			DOWN : 'down'
		},
		icons : {
			STATION : '/imgs/taxicode-local/common/train_station.svg',
			AIRPORT : '/imgs/taxicode-local/common/airport.svg',
			TOWN : '/imgs/taxicode-local/common/town.svg',
			GOOGLE : '/imgs/taxicode-local/common/google.svg'
		}
	},
	journey : {
		type : 'one-way',
		pickup : '',
		destination : '',
		outbound : null,
		return : null,
		people : 1,
		vias : [],
		vias_elements : 0,
		location_suggestions : {}
	},

	/**
	 * Init setup for all user driven events
	 */
	init : function() {

		// generate ratings stars
		TCL.generateStars();

		// detect if criteria are met and show app banner
		TCL.showAppBanner();

		// determine if app store links can be shown
		TCL.showAppLinks();

		// detect user input and trigger the related actions
		$(TCL.config.form.MAIN_ELEMENT).on('keyup', 'input[data-type=search]', function (e) {

			// get pressed key
			var key = e.which;

			if (key === 38) { // up arrow pressed

				TCL.navigateSuggestions(TCL.config.generic.UP, $(this).closest(TCL.config.form.INPUT_FIELD).find(TCL.config.form.SUGGESTION_CONTAINER));

			} else if (key === 40) { // down arrow pressed

				TCL.navigateSuggestions(TCL.config.generic.DOWN, $(this).closest(TCL.config.form.INPUT_FIELD).find(TCL.config.form.SUGGESTION_CONTAINER));

			} else if (key === 13) { // enter key pressed

				TCL.pickNavigatedItem($(this));

			} else if (key === 27 || key === 9) { // escape key or tab key pressed

				TCL.resetSuggestions();

			} else if ($.inArray(key, TCL.config.form.DISABLED_KEY_CODES) === -1) { // if not in the list of disabled keys, initiate timer

				TCL.startTimer($(this));

			}

		});

		// user selected a suggestion
		$(TCL.config.form.MAIN_ELEMENT).on('click', '.suggestions-element', function() {
			TCL.pickSuggestion($(this));
		});

		// hide suggestions list
		$('body').on('click', function(e) {
			if (!$(e.target).is(TCL.config.form.SEARCH_ELEMENT) && !$(e.target).hasClass('suggestion-header')) {
				TCL.resetSuggestions();
			}
		});

		// user changes journey type
		$(TCL.config.form.MAIN_ELEMENT).find('#returnJourneyCheckbox').on("change", function() {

			// using plain js because jQuery does not seem to like what I'm doing to get checkbox value
			var checkbox = document.getElementById('returnJourneyCheckbox');

			TCL.journeyTypeChanged(checkbox.checked);

		});

		// user clicked on a date field
		TCL.showDatePicker();

		// user clicked on a time field
		TCL.showTimePicker();

		// initialise materialize components
        $(document).ready(function(){
            $('.tabs').tabs();
            $('.collapsible').collapsible();
        });

		// user clicks passengers buttons
		$('.people-action').on("click", function() {
			TCL.updatePassengers($(this).attr('data-action'));
		});

		// user changes value manually
		$('#people').on('input', function() {
			TCL.validatePassengers($(this).val());
		}).on('blur', function() {
			// if it does not have a value when user leaves passenger field, reset it to previous value
			if (!$(this).val().length) {
				TCL.updatePassengers('custom');
			}
		});

		// user clicked the Add Via button
		$('.add-vias').on("click", TCL.addVia);

		$('.via-container').on('click', '.remove-vias', function() {
			TCL.removeVia($(this));
		});

		//user enables complex journey
		$(TCL.config.form.ADVANCED_CONTAINER).find('#advancedCheckbox').on("change", function() {

			// using plain js because jQuery does not seem to like what I'm doing to get checkbox value
			var checkbox = document.getElementById('advancedCheckbox'),
				showAdvanced = checkbox.checked;

			if (showAdvanced) {
				TCL.showAdvancedFields();
			} else {
				TCL.hideAdvancedFields();
			}

		});

		// form submitted
		$(TCL.config.form.QUOTES_BUTTON).on("click", TCL.submitJourney);

		// advanced form submitted
		$(TCL.config.form.ADVANCED_BUTTON).on("click", TCL.submitAdvancedQuote);

		// clear form
		$('.clear-form').on("click", function() {
			TCL.resetForm();
			TCL.resetAdvancedForm();
		});

		if (_RECOVERED_JOURNEY) {
			// recover quote from recovered journey
			TCL.recoverQuote();
		} else {
			// load quote if it exists in storage
			TCL.loadQuote();
		}

        // user selects quote's vehicle
        /*
        $(TCL.config.form.VEHICLES_CONTAINER).on('click', TCL.config.form.VEHICLE_BUTTON, function(){
			TCL.vehicleSelected($(this));
		});
        */

		// user dismisses benefits popup
		$('body').on('click', '.benefits-close-button', function() {
			TCL.hideBenefits();
		});

	},

	/**
	 * Changes journey type value
	 */
	journeyTypeChanged : function(returnJourney) {

		var $returnContainer = $('.return-container');

		// set journey type
		TCL.journey.type = returnJourney ? TCL.config.form.RETURN : TCL.config.form.ONE_WAY;

		switch (TCL.journey.type) {
			case TCL.config.form.ONE_WAY:

				// reset return value
				TCL.journey.return = null;
				$(TCL.config.form.MAIN_ELEMENT).find('#return_date').val('');
				$(TCL.config.form.MAIN_ELEMENT).find('#return_time').val('');

				// hide return container
				$returnContainer.hide();

				break;
			case TCL.config.form.RETURN:
				$returnContainer.show();
				break;
			default:
		}

	},

	/**
	 * Start the search timer
	 * @param {Object} $input_field The input field that initiates the search timer
	 */
	startTimer : function($input_field) {
	    console.log($input_field);

        /*$(TCL.config.form.MAIN_ELEMENT).find('input[name=' + field_name + '_poi_reference]').val(reference);
        $(TCL.config.form.MAIN_ELEMENT).find('input[name=' + field_name + '_poi]').val(poi);*/

		// clear and reset interval if it is set
		if (TCL.config.form.TIMER_INTERVAL_ID !== null) {
			TCL.resetTimerInterval();
		}

		// reset timer every time method is called
		TCL.config.form.SEARCH_TIMER = 0;

		var $loading_element = $input_field.closest(TCL.config.form.INPUT_FIELD).find(TCL.config.form.SEARCH_ELEMENT);

		// if number of characters typed is equal or bigger than the minimum search characters criteria, start search process
		if ($input_field.val().length >= TCL.config.form.MIN_SEARCH_CHARS) {

			$loading_element.addClass('loading-gray');

			TCL.setTimerInterval($input_field);

		} else {

			$loading_element.removeClass('loading-gray');

		}

	},

	/**
	 * Set the timer interval
	 * @param {Object} $input_field The input field that initiates the search timer
	 */
	setTimerInterval : function($input_field) {

		TCL.config.form.TIMER_INTERVAL_ID = setInterval(function() {

			// increase by one second
			TCL.config.form.SEARCH_TIMER++;

			// if current timer value is equal to threshold value
			if (TCL.config.form.SEARCH_TIMER === TCL.config.form.SEARCH_THRESHOLD) {

				// clear and reset interval
				TCL.resetTimerInterval();

				// get suggestions
				TCL.getSuggestions($input_field);
			}

		}, 1000);

	},

	/**
	 * Reset the timer interval
	 */
	resetTimerInterval : function() {

		// clear interval
		clearInterval(TCL.config.form.TIMER_INTERVAL_ID);

		// reset it
		TCL.config.form.TIMER_INTERVAL_ID = null;

	},

	/**
	 * Appends list of suggestions on specified input field
	 * @param {Object} $input_field The input field that dictates where the suggestions are going to appear
	 */
	getSuggestions : function($input_field) {

		var term = $input_field.val().trim().toLowerCase();

		// if term exists in the location suggestions, no need to make an API call
		if (typeof TCL.journey.location_suggestions[term] !== 'undefined') {

			if (TCL.journey.location_suggestions[term]['count']) {
				TCL.showSuggestions($input_field, term);
			} else {
				TCL.showNotification(
					TCL.config.generic.ERROR_TYPE,
					'No suggestions found for your request',
					$(TCL.config.form.MAIN_ELEMENT)
				);
			}

			$input_field.closest(TCL.config.form.INPUT_FIELD).find(TCL.config.form.SEARCH_ELEMENT).removeClass('loading-gray');

		} else {

			Taxicode_API.get("places", {
				type: TCL.config.request.GET_REQUEST,
				data: {
					term: term
				},
				success: function(response) {
					if (response.status === 'OK') {

						// store the term - results pair to suggestions object for potential further use
						TCL.journey.location_suggestions[term] = TCL.formatSuggestions(response.results);

						// if suggestions has records, show suggestions
						// otherwise, show error
						if (TCL.journey.location_suggestions[term]['count']) {
							TCL.showSuggestions($input_field, term);
						} else {
							TCL.showNotification(
								TCL.config.generic.ERROR_TYPE,
								'No suggestions found for your request',
								$(TCL.config.form.MAIN_ELEMENT)
							);
						}
					} else {
						TCL.showNotification(
							TCL.config.generic.ERROR_TYPE,
							response.error,
							$(TCL.config.form.MAIN_ELEMENT)
						);
					}
				},
				complete : function() {
					$input_field.closest(TCL.config.form.INPUT_FIELD).find(TCL.config.form.SEARCH_ELEMENT).removeClass('loading-gray');
				}
			});

		}

	},

	/**
	 * Constructs a formatted suggestions array in a { string : '', icon : '' } objects format
	 * @param {Object} suggestions The suggestions object returned from the API call
	 * @returns {Object}
	 */
	formatSuggestions : function(suggestions) {
	    //console.log(suggestions);
		// initialise temp array
		var formatted = {
			count : 0,
			results : {}
		};

		// iterate through suggestions object to construct the formatted array
		for (var key in suggestions) {

			formatted['results'][key] = {
				icon : TCL.config.icons[key],
				list : []
			};

			// detect if content is array or object to process data accordingly
			if (suggestions[key] instanceof Array) {

				for (var i = 0; i < suggestions[key].length; i++) {
				    //console.log(suggestions[key][i]);
				    formatted['results'][key]['list'].push(typeof suggestions[key][i] === 'object' ? {"string": suggestions[key][i]['string'],"reference": suggestions[key][i]['reference'], "poi":suggestions[key][i]['poi']} : {"string":  suggestions[key][i],"reference":'',"poi":false});
					formatted.count++;
				}

			} else {

				for (var index in suggestions[key]) {
				    formatted['results'][key]['list'].push(typeof suggestions[key][index] === 'object' ? {"string": suggestions[key][index]['string'], "reference": suggestions[key][index]['reference'],  "poi": suggestions[key][index]['poi'] } : {"string": suggestions[key][index], "reference": '', "poi": false });
					formatted.count++;
				}

			}
		}

		return formatted;
	},

	/**
	 * Append the formatted list of suggestion to the specified input field
	 * @param {Object} $input_field The input field that dictates where the suggestions are going to appear
	 * @param {String} key The key of the location_suggestions object that contains the suggestions data
	 */
	showSuggestions : function($input_field, key) {
		$input_field.closest('.TCL-input-field').find('.suggestions-list').html(_.template($("#location_suggestions").html())({
			suggestions : TCL.journey.location_suggestions[key]['results'],
			field : $input_field.attr('name')
		})).show();
	},

	/**
	 * A location suggestion has been picked from the list
	 * @param {Object} $element The selected jquery list item object
	 */
	pickSuggestion : function($element) {
		var field_name = $element.attr('data-field'),
			text = $element.text();
		var reference= $element.attr('data-reference');
        var poi= $element.attr('data-poi');
		if(poi != 'false')
        {
            $(TCL.config.form.MAIN_ELEMENT).find('input[name=' + field_name + '_poi_reference]').val(reference);
            $(TCL.config.form.MAIN_ELEMENT).find('input[name=' + field_name + '_poi]').val(poi);
        }

		$(TCL.config.form.MAIN_ELEMENT).find('input[name=' + field_name + ']').val(text);
		TCL.resetSuggestions();
	},

	/**
	 * Navigate through suggestions list
	 * @param {String} direction The direction of navigation
	 * @param {Object} $suggestions_list The jquery suggestions list object
	 */
	navigateSuggestions : function(direction, $suggestions_list) {

		// check if suggestions exist by checking container's visibility
		if ($suggestions_list.is(':visible')) {

			// get current hovered suggestion
			var $current = $suggestions_list.find('.hovered'),
				$hovered = $current;

			switch (direction) {

				case TCL.config.generic.UP:

					// if there is already a hovered item, remove hovered class and add it to next sibling
					if ($current.length) {

						// only go to the previous one if current one is not the first item in the list
						if ($current.index() > 1) {
							$current.removeClass('hovered');
							$hovered = $current.prevAll('.suggestion-list-item').first().addClass('hovered');
						}

					}

					break;

				case TCL.config.generic.DOWN:

					// if there is already a hovered item, remove hovered class and add it to next sibling
					if ($current.length) {

						// only go to the next one if current one is not the last item in the list
						if (!$current.is(':last-child')) {
							$current.removeClass('hovered');
							$hovered = $current.nextAll('.suggestion-list-item').first().addClass('hovered');
						}

					} else {
						// else add hovered class on first item of list
						$hovered = $suggestions_list.find('.suggestion-list-item').first().addClass('hovered');
					}

					break;

			}

			TCL.shiftResultsScroll($suggestions_list, $hovered);

		}

	},

	/**
	 * Auto scroll suggestions container to show hovered result
	 * @param {Object} $suggestions_list The jquery suggestions list object
	 * @param {Object} $hovered The jquery hovered item object
	 */
	shiftResultsScroll : function($suggestions_list, $hovered) {

		var height = $suggestions_list.height(),
			li_height = $hovered.outerHeight(),
			scrollTop = $suggestions_list.scrollTop(),
			top = $hovered.position().top + scrollTop;

		if (top > height - li_height + scrollTop) {
			$suggestions_list.scrollTop(top - height + li_height);
		} else if (top < scrollTop) {
			$suggestions_list.scrollTop(top);
		}

	},

	/**
	 * Select suggestion item on Enter key press
	 * @param {Object} $input_field The input field the action was triggered on
	 */
	pickNavigatedItem : function($input_field) {

		// get suggestions list related to $input_field
		var $suggestions_list = $input_field.closest('.TCL-input-field').find('.suggestions-list');

		// if suggestions list is visible it means we can pick something
		if ($suggestions_list.is(':visible')) {

			// get hovered element
			var $hovered = $suggestions_list.find('.hovered');

			// is there an active hovered element?
			if ($hovered.length) {

				TCL.pickSuggestion($hovered.find('.suggestions-element'));

			}

		}

	},

	/**
	 * Reset suggestions lists
	 */
	resetSuggestions : function() {
		$(TCL.config.form.SUGGESTION_CONTAINER).empty().hide();
	},

	/**
	 * Show a notification to the user
	 * @param {String} type [success | error]
	 * @param {String} message
	 * @param {Object} $element The element in which the notification-container element exists
	 */
	showNotification : function(type, message, $element) {
		var notificationContainer = $element.find('.notification-container');

		notificationContainer.html(_.template($("#notification_alert").html())({
			message : message,
			type : type
		})).fadeIn('fast');
        
        TCL.scrollToElement(notificationContainer, false);
        
		setTimeout(function() {
			notificationContainer.fadeOut('fast', function () {
				$(this).empty();
			})
		}, TCL.config.generic.NOTIFICATION_TIMEOUT);
	},

	/**
	 * Show a toast notification to the user
	 * @param {string} type [success | error]
	 * @param {string} message, the message shown to the user
	 */
	showToast : function(type, message) {

		// create toast element
		var $toastContent = $(_.template($("#toast_alert").html())({
			message : message,
			type : type
		}));

		// show it
		Materialize.toast($toastContent, 6000);

	},

	/**
	 * Show datepicker modal
	 */
	showDatePicker : function() {
		$('.datepicker').datepicker({
			selectMonths: true, // Creates a dropdown to control month
			selectYears: 1,
			format : 'd mmm yyyy',
			minDate : new Date(TCL.config.form.TODAY.getFullYear(), TCL.config.form.TODAY.getMonth(), TCL.config.form.TODAY.getDate()),
			today: null,
			clear: 'Clear',
			close: 'Ok',
			closeOnSelect: false // Close upon selecting a date,
		});
	},

	/**
	 * Show timepicker modal
	 */
	showTimePicker : function() {
		$('.timepicker').timepicker({
			defaultTime : 'now',
			doneText : 'OK',
			cleartext : 'Clear',
            twelveHour : false,
			ampmclickable : true
		});
	},

	/**
	 * Validate passengers value
	 * @param value the passengers number
	 */
	validatePassengers : function(value) {
		// only update if there is a value
		if (value.length) {

			// is value a numeric one?
			if (!isNaN(value)) {
				TCL.journey.people = parseInt(value);
			}

			TCL.updatePassengers('custom');

		}

	},

	/**
	 * Increase or decrease number of passengers
	 * @param action defines the type of action to take place
	 */
	updatePassengers : function(action) {
		switch (action) {
			case 'subtract':
				// subtraction is valid only if current value of passengers is greater than 1
				if (TCL.journey.people > 1) {
					TCL.journey.people -= 1;
				}
				break;
			case 'add':
				TCL.journey.people += 1;
				break;
			default:
		}

		// remove disabled from subtract button if passengers is greater than 1
		if (TCL.journey.people > 1) {
			TCL.updateSubtractButton(false);
		} else {
			// add disabled class if passenger number is 1
			TCL.updateSubtractButton(true);
		}

		$(TCL.config.form.MAIN_ELEMENT).find('#people').val(TCL.journey.people);
	},

	/**
	 * Disable or enable the subtract button
	 * @param {Boolean} disabled defines the type of action to take place
	 */
	updateSubtractButton : function(disabled) {
		if (disabled) {
			$('.people-action[data-action=subtract]').addClass('disabled-action');
		} else {
			$('.people-action[data-action=subtract]').removeClass('disabled-action');
		}
	},

	/**
	 * Append a via block
	 */
	addVia : function() {
		if (TCL.journey.vias_elements !== TCL.config.form.MAX_VIAS) {

			// get the new via id
			var via_id = TCL.journey.vias_elements + 1;

			// if not the first via element we added, remove the subtraction element from the previous one
			// we only want the last inserted via element to have the option to be deleted
			if (via_id !== 1) {
				$('.via-block').find('.remove-vias').remove();
			}

			// append the new via input to form
			$('.via-container').append(_.template($("#via_block").html())({
				label : 'Via ' + via_id,
				name : 'via_' + via_id
			})).fadeIn();

			// increase value of vias_elements
			TCL.journey.vias_elements = via_id;

		} else {

		}
	},

	/**
	 * Remove last via block
	 * @param {Object} $this the current subtraction element
	 */
	removeVia : function($this) {
		// subtract from total vias_element value
		TCL.journey.vias_elements--;
		// remove element
		$this.closest('.row').remove();

		// if the element deleted wasn't the last via one, append the minus icon to the previous
		if (TCL.journey.vias_elements) {
			$('.via-block').last().find('.remove-container').html('<a class="action-icon icon-minus-content remove-vias"></a>');
		}
	},

	/**
	 * Show the advanced fields of the form
	 */
	showAdvancedFields : function() {

		// hide Get Quotes button
		$(TCL.config.form.QUOTES_BUTTON).hide();

		// show advanced options
		$(TCL.config.form.ADVANCED_CONTAINER).find('.advanced-fields').html(_.template($("#advanced_options").html()));

		// show Request Quote button
		$(TCL.config.form.ADVANCED_BUTTON).removeClass('hide');

	},

	/**
	 * Hide the advanced fields of the form
	 */
	hideAdvancedFields : function() {

		// hide Request Quote button
		$(TCL.config.form.ADVANCED_BUTTON).addClass('hide');

		// empty advanced options
		$(TCL.config.form.ADVANCED_CONTAINER).find('.advanced-fields').empty();

		// show Get Quotes button
		$(TCL.config.form.QUOTES_BUTTON).show();

	},

	/**
	 * Validate booking form
	 * @return {object}
	 */
	validateJourney : function() {

		var valid = true,
			error = null;

		// go through all search inputs and check if they have a value
		$(TCL.config.form.INPUT_FIELD).find('input[data-type="search"]').each(function () {
			// if one fails, set valid to false and skip remaining iterations
			if (!Validation.validate(Validation.types.LENGTH_CHECK, $(this).val())) {
				valid = false;
				error = 'All location fields need to be set';
			}
		});

		// check if outbound date is set
		if (valid && !Validation.validate(Validation.types.LENGTH_CHECK, $('#outbound_date').val())) {
			valid = false;
			error = 'Outbound date needs to be set';
		}

		// check if outbound time is set
		if (valid && !Validation.validate(Validation.types.LENGTH_CHECK, $('#outbound_time').val())) {
			valid = false;
			error = 'Outbound time needs to be set';
		}

		// check if journey type is return
		if (TCL.journey.type === TCL.config.form.RETURN) {

			// check if return date is set
			if (valid && !Validation.validate(Validation.types.LENGTH_CHECK, $('#return_date').val())) {
				valid = false;
				error = 'Return date needs to be set';
			}

			// check if return time is set
			if (valid && !Validation.validate(Validation.types.LENGTH_CHECK, $('#return_time').val())) {
				valid = false;
				error = 'Return time needs to be set';
			}

		}

		if (valid) {
			var $people = $('#people');
			// check if passengers number is set and value is a numeric one
			if (!Validation.validate(Validation.types.LENGTH_CHECK, $people.val())) {
				valid = false;
				error = 'Passengers is not set';
			} else if (!Validation.validate(Validation.types.NUMERIC_CHECK, $people.val())) {
				valid = false;
				error = 'Passengers needs to be a numeric value';
			}
		}

		return {
			valid : valid,
			error : error
		};
	},

	/**
	 * Validate advanced fields
	 * @return {object}
	 */
	validateAdvancedDetails : function() {

		var valid = true,
			error = null;

		if (!Validation.validate(Validation.types.NAME_CHECK, $('#advancedName').val())) {
			valid = false;
			error = 'The full name field is not in the right format';
		} else if (!Validation.validate(Validation.types.EMAIL_CHECK, $('#advancedEmail').val())) {
			valid = false;
			error = 'The email field is not in the right format';
		} else if (!Validation.validate(Validation.types.PHONE_CHECK, $('#advancedPhone').val())) {
			valid = false;
			error = 'The phone field is not in the right format';
		} else if (!Validation.validate(Validation.types.LENGTH_CHECK, $('#advancedNotes').val())) {
			valid = false;
			error = 'The notes field cannot be empty';
		}

		return {
			valid : valid,
			error : error
		};

	},

	/**
	 * Method to store journey details
	 */
	storeJourney : function() {

		// store pickup
		TCL.journey.pickup = $('#pickup').val();
        TCL.journey.pickup_poi = $('#pickup_poi').val();
        TCL.journey.pickup_poi_reference = $('#pickup_poi_reference').val();

		// store.destination
		TCL.journey.destination = $('#destination').val();
        TCL.journey.destination_poi = $('#destination_poi').val();
        TCL.journey.destination_poi_reference = $('#destination_poi_reference').val();

		// reset vias and store new ones if any
		TCL.journey.vias = [];
		if (TCL.journey.vias_elements) {
			$('.via-block').find('input[type=text]').each(function () {
				TCL.journey.vias.push($(this).val());
			});
		}

		// store outbound date
		TCL.journey.outbound = $('#outbound_date').val() + ', ' + $('#outbound_time').val();

		// store return date if type of journey is a return one
		if (TCL.journey.type === TCL.config.form.RETURN) {
			TCL.journey.return = $('#return_date').val() + ', ' + $('#return_time').val();
		} else {
			TCL.journey.return = null;
		}

		// store passengers
		TCL.journey.people = parseInt($('#people').val());

	},

	/**
	 * Method to get the journey POST data
	 */
	getJourneyPOST : function() {

		// initial journey data
		var journey = {
			pickup : TCL.journey.pickup,
            pickup_poi: TCL.journey.pickup_poi,
            pickup_poi_reference: TCL.journey.pickup_poi_reference.length ? TCL.journey.pickup_poi_reference : false,
            vias : TCL.journey.vias.length ? TCL.journey.vias.join("|") : false,
			destination : TCL.journey.destination,
            destination_poi: TCL.journey.destination_poi,
            destination_poi_reference: TCL.journey.destination_poi_reference.length ? TCL.journey.destination_poi_reference : false,
			date : TCL.journey.outbound,
			return : TCL.journey.type === TCL.config.form.RETURN ? TCL.journey.return : false,
			people : parseInt(TCL.journey.people),
			type : _SITE_VEHICLE
		};

		// specify if search is going to be a local or a sales one
		if (_INSTANCE_ID && !TCL.config.form.SALES_SEARCH) {
			journey["instances"] = _INSTANCE_ID;
		}

		// specify type
		if (_MAX_PASSENGERS < journey.people) {
			journey["instances"] = _MAX_PASSENGERS_BACKUP_INSTANCES.join(",");
			journey["type"] = "all";
		}

		// set call source type
		journey["call_source_type"] = 'local';

		return journey;

	},

	/**
	 * Method that fires off quotes API request
	 * @param {object} journey, the journey related data
	 */
	getQuotes : function(journey) {
	    //console.log(journey);
		//reset Quote Results
        TCL.resetQuotesResults();

		Taxicode_API.get("booking/quote", {
			type : TCL.config.request.GET_REQUEST,
			data : journey,
			beforeSend : function() {
				$(TCL.config.form.ACTION_BUTTON).addClass('disabled');
				$(TCL.config.form.QUOTES_BUTTON).empty().addClass('loading disabled');
			},
			success : function(response) {

				// store journey response
				TCL.config.form.RESPONSE['journey'] = response.journey;

				// default value of stop_loading is set to true
				var stop_loading = true;

				if (response.status === 'OK') {
                                    console.log(response);
                                    if (window._GTAG_ENABLED) {
                                        // reset the local vars
                                        var gtag_quote_items = [],
                                             // gtag quote result item position
                                            result_position = 0,
                                             // because only the first default 0 indexed vehicle is used
                                            selected_vehicle = 0;
                                        //set the gtag tracking search journey properties
                                        TC.gtag.tracking.search.setJourneyEventProps(TC.gtag.remapAPIQuotesJourneyProps(TCL.config.form.RESPONSE['journey']));
                                        // Process each quote rendering them on underscore template
                                        $.each(response.quotes, function(id, quote) {
                                            // increment at the start because be begining at 0
                                            result_position++;
                                            // make note to gtag track the quote result/product listing
                                            gtag_quote_items.push(TC.gtag.tracking.search.quoteItemProps(id, quote, selected_vehicle, result_position));
                                        });
                                        // track the quotes results page view
                                        TC.gtag.track.quote.viewResults(gtag_quote_items);
                                    }

					// render journey map and journey information
					TCL.renderJourneyInfo();

					// get the best quote id
					TCL.config.form.RESPONSE['quote_id'] = TCL.bestQuote(response.quotes);

					// if quote_id is found
					if (TCL.config.form.RESPONSE['quote_id']) {

						// store quote details
						TCL.config.form.RESPONSE['api_checkout_url'] = response.api_checkout_url;
						TCL.config.form.RESPONSE['payment_url'] = response.payment_url;
						TCL.config.form.RESPONSE['secureType'] = response.secureType;
						TCL.config.form.RESPONSE['quote'] = response.quotes[TCL.config.form.RESPONSE['quote_id']];

						// if quote is active start rendering process
						if (TCL.config.form.RESPONSE['quote']['active']) {

							// always reset SALES_SEARCH after a successful response
							TCL.config.form.SALES_SEARCH = false;

							// store quote
							TCL.storeQuote();

							// render vehicles information
							TCL.renderVehicles();

							// scroll to map after vehicle rendering is complete
							TCL.scrollToElement(TCL.config.form.MAP_CONTAINER, false);

							// show benefits popup
							TCL.showBenefits();

						} else {

							// don't stop loading if sales search is about to take place
							stop_loading = false;

							// start sales search
							TCL.initSalesSearch();

						}

					} else if (response.warnings.length) {

						// if warning's returned, initiate a sales search
						stop_loading = false;

						// start sales search
						TCL.initSalesSearch();

						// // Return first warning
						// TCL.showNotification(
						// 	TCL.config.generic.ERROR_TYPE,
						// 	response.warnings[0],
						// 	$(TCL.config.form.MAIN_ELEMENT)
						// );

					} else {

						// if quote id has not been found and there are no warnings
						// reset SALES_SEARCH and show error message
						TCL.config.form.SALES_SEARCH = false;

						TCL.showNotification(
							TCL.config.generic.ERROR_TYPE,
							"Apologies, no quotes were found. Please check the details you've entered and try using full postcodes.",
							$(TCL.config.form.MAIN_ELEMENT)
						);

					}

				} else {

					TCL.showNotification(
						TCL.config.generic.ERROR_TYPE,
						response.error,
						$(TCL.config.form.MAIN_ELEMENT)
					);

				}

				// remove loading based on stop_loading value
				if (stop_loading) {
					$(TCL.config.form.ACTION_BUTTON).removeClass('disabled');
					$(TCL.config.form.QUOTES_BUTTON).html('Get Quotes').removeClass('loading disabled');
				}

			},
			error : function() {},
			complete : function() {}
		});
	},

	/**
	 * Initiate a sales search
	 */
	initSalesSearch : function() {

		// activate SALES_SEARCH
		TCL.config.form.SALES_SEARCH = true;

		// construct POST data
		var journey = TCL.getJourneyPOST();

		// and re-run API call
		TCL.getQuotes(journey);

	},

	/**
	 * Method to return the best quote's ID
	 * @param {object} quotes, the quotes returned from API call
	 */
	bestQuote : function(quotes) {
		// If just one result (should always be the case apart from for sales sites) return that quotes ID
		switch (Object.keys(quotes).length) {
			case 0: return false;
			case 1: return Object.keys(quotes)[0];
		}

		// Variables to track
		var performance = 0;
		var feedback = 0;
		var ratings = 0;
		var price = false;
		var quote_id = false;

		// Iterate through quotes to find best
		$.each(quotes, function(id, quote) {
			if (quote.active && quote.price > 0) {
				var replace = false;

				// Check if quote is better than current best quote
				if (quote.price < price || price === false) {
					replace = true;
				} else if (quote.price === price && quote.performance > performance) {
					replace = true;
				} else if (quote.price === price && quote.performance === performance && quote.rating.score > feedback) {
					replace = true;
				} else if (quote.price === price && quote.performance === performance && quote.rating.score === feedback && quote.rating.ratings > ratings) {
					replace = true;
				}

				if (replace) {
					quote_id = id;
					performance = quote.performance;
					feedback = quote.rating.score;
					ratings = quote.rating.ratings;
					price = quote.price;
				}
			}
		});

		// Return Quote ID
		return quote_id;
	},

	/**
	 * Method to render best quote
	 */
	renderQuote : function() {
		console.log(TCL.config.form.RESPONSE.quote);
	},

	/**
	 * Method to handle submission of booking form
	 */
	submitJourney : function() {

		var validation = TCL.validateJourney();

		if (validation.valid) {

			// if validation succeeds, store form
			TCL.storeJourney();

			// construct POST data
			var journey = TCL.getJourneyPOST();

                        if (window._GTAG_ENABLED) {
                            // track the quotes search
                            //TC.gtag.remapQuoteBoxJourneyProps(journey)
                            TC.gtag.track.quote.search(TC.gtag.remapQuoteBoxJourneyProps(journey));
                        }
			// fire API request
			TCL.getQuotes(journey);

		} else {

			// else, show notification
			TCL.showNotification(
				TCL.config.generic.ERROR_TYPE,
				validation.error,
				$(TCL.config.form.MAIN_ELEMENT)
			);

		}

	},

	/**
	 * Method to save advanced quote
	 */
	submitAdvancedQuote : function() {

		// first validate booking form
		var validation = TCL.validateJourney();

		if (validation.valid) {

			// store journey
			TCL.storeJourney();

			// if successful, check if advanced fields are valid
			var advancedValidation = TCL.validateAdvancedDetails();

			if (advancedValidation.valid) {

				// replace double quotes with single ones and create a temporary element
				var notesText = $('#advancedNotes').val().replace(/"/g, "'"),
					$tempDiv = $('<div></div>');

				// append the replaced text to temporary element
				$tempDiv.html(notesText);
				// extract only text to avoid having html tags
				var notesFiltered = $tempDiv.text();

				var journey = {
					"name" : $('#advancedName').val(),
					"email" : $('#advancedEmail').val(),
					"tel" : $('#advancedPhone').val(),
					"notes" : notesFiltered,
					"pickup" : TCL.journey.pickup,
					"destination" :	TCL.journey.destination,
					"vias" : TCL.journey.vias,
					"date" : TCL.journey.outbound,
					"return" : TCL.journey.type === TCL.config.form.RETURN ? TCL.journey.return : false,
					"passengers" : TCL.journey.people,
					"vehicle" : "any",
					"quote_type" : "mileage",
					"type" : "all"
				};

				$.ajax({
					type: "POST",
					url: "/home/saveAdvancedQuote/",
					data: journey,
					dataType: "json",
					beforeSend : function() {
						$(TCL.config.form.ACTION_BUTTON).addClass('disabled');
						$(TCL.config.form.ADVANCED_BUTTON).empty().addClass('loading disabled');
					},
					success : function(response) {

						if (response.status === 'OK') {

							// if submission is successful, show toast message
							TCL.showToast(TCL.config.generic.SUCCESS_TYPE, "Your request has been submitted.");

							// reset form so they don't start spamming
							TCL.resetForm();
							TCL.resetAdvancedForm();

						} else {

							// else, show error notification
							TCL.showNotification(
								TCL.config.generic.ERROR_TYPE,
								response.errors,
								$(TCL.config.form.MAIN_ELEMENT)
							);

						}

					},
					complete : function() {
						$(TCL.config.form.ACTION_BUTTON).removeClass('disabled');
						$(TCL.config.form.ADVANCED_BUTTON).html('Request Quote').removeClass('loading disabled');
					}
				});

			} else {

				// else, show notification
				TCL.showNotification(
					TCL.config.generic.ERROR_TYPE,
					advancedValidation.error,
					$(TCL.config.form.MAIN_ELEMENT)
				);

			}

		} else {

			// else, show notification
			TCL.showNotification(
				TCL.config.generic.ERROR_TYPE,
				validation.error,
				$(TCL.config.form.MAIN_ELEMENT)
			);

		}

	},

	/**
	 * render journey info
	 */
	renderJourneyInfo : function() {

		// render journey map
        MapBox.init({
            journey: TCL.config.form.RESPONSE['journey'],
            center: _AREA_LOCATION,
            container: 'map-element',
            mapInitCallback: function() {
                $(TCL.config.form.MAP_CONTAINER).show();
            }
        });

		// render journey information
		$('.journey-info-container').html(_.template($("#journey_info_block").html())({
			journey : TCL.config.form.RESPONSE['journey']
		}));

	},

	/**
	 * render vehicles
	 */
	renderVehicles : function() {

		// render vehicles information
		$('.vehicles-container').html(_.template($("#vehicles_block").html())({
			vehicles : TCL.config.form.RESPONSE['quote']['vehicles'],
			touchable : true,
			showExtra : true,
			button : true
		}));

		// show reset button
		$('.clear-field').show();

	},

	/**
	 * show the Why Book With Us? modal
	 */
	showBenefits : function() {

		// show popup only if benefits is not in local storage and screen size is larger than 500px
		if (!TCL_Storage.getItem('benefits') && $(window).width() > 500) {

			// set local storage flag for benefits to stop appearing next time
			TCL_Storage.setItem('benefits', true);

			// append benefits popup to body
			$('body').append(_.template($("#benefits_block").html()));

			// show it after 0.5 second to avoid animations happening at the same time
			setTimeout(function() {
				$('.benefits-container').animate({bottom: "+=270"});
			}, 500);

		}

	},

	/**
	 * hides the benefits modal
	 */
	hideBenefits : function() {

		// first hide it
		$('.benefits-container').animate({bottom: "-=270"});

		// then remove it
		setTimeout(function() {
			$('.benefits-container').remove();
		}, 500);

	},

	/**
	 * scroll to specified element
	 * @param {string} element, the id or class of the element you want to scroll to
	 * @param {boolean} delay, dictates whether there is going to be a small delay to initiate the scrolling effect
	 */
	scrollToElement : function(element, delay) {

		setTimeout(function() {
			$('html, body').animate({
					scrollTop: $(element).offset().top
				},
				'slow');
		}, delay ? 500 : 0);

	},

	/**
	 * store the successful response to local storage
	 */
	storeQuote : function() {

		// store response
		TCL_Storage.setItem('response', JSON.stringify(TCL.config.form.RESPONSE));

		// we also store submitted journey in order to be able to pre-fill form fields on page reload
		TCL_Storage.setItem('journey', JSON.stringify(TCL.journey));

        // store time storage created
        TCL_Storage.setItem('created_at', new Date().valueOf().toString());

	},

	/**
	 * recover quote from journey
	 */
	recoverQuote : function() {

		// remove params from url
		var removed_params = TCL.removeURLParams("success", "edit", "recover", "error");

		// pre-fill form data
		TCL.journey.type = _RECOVERED_JOURNEY.return ? TCL.config.form.RETURN : TCL.config.form.ONE_WAY;
		TCL.journey.pickup = _RECOVERED_JOURNEY.pickup;
		TCL.journey.destination = _RECOVERED_JOURNEY.destination;
		TCL.journey.outbound = TCL.formatDateTime(_RECOVERED_JOURNEY.date, _RECOVERED_JOURNEY.time);
		TCL.journey.return = TCL.formatDateTime(_RECOVERED_JOURNEY.return, _RECOVERED_JOURNEY.return_time);
		TCL.journey.people = _RECOVERED_JOURNEY.passengers;

		if (_RECOVERED_JOURNEY.vias.length) {
			// if vias exist, loop through them and push them to journey vias array
			for (var i in _RECOVERED_JOURNEY.vias) {
				TCL.journey.vias.push(_RECOVERED_JOURNEY.vias[i]['string']);
			}
		}

		// build form
		TCL.buildForm();

		// trigger form submission
		$(TCL.config.form.QUOTES_BUTTON).trigger('click');
	},

	/**
	 * load quote from local storage
	 */
	loadQuote : function() {
		var loadResponse = false;
		var journey = TCL_Storage.getItem('journey');
        var nowTimestamp = new Date().valueOf();

		// check if journey exists in local storage
		if (journey) {

			// "un-string" it
			journey = JSON.parse(journey);

			// pre-fill form data
			TCL.journey.type = journey.type;
			TCL.journey.pickup = journey.pickup;
			TCL.journey.destination = journey.destination;

            //only load dates that are in the future

            var outTimestamp = new Date(journey.outbound).valueOf();
            var retTimestamp = new Date(journey.return).valueOf();
            if(outTimestamp > nowTimestamp && (journey.return == null || retTimestamp > nowTimestamp)){
                TCL.journey.outbound = journey.outbound;
                TCL.journey.return = journey.return;
                //allow stored response to load if exists
                loadResponse = true;
            }

			TCL.journey.people = journey.people;
			TCL.journey.vias = journey.vias;
			TCL.journey.location_suggestions = journey.location_suggestions;
			// TCL.journey.vias_elements not getting a value because it will mess up the vias block IDs / labels when the addVia method gets called during the buildForm process

			// build form
			TCL.buildForm();

            var validTimeSpan = 1000*60*60;
            var offsetCreatedAt = parseInt(TCL_Storage.getItem('created_at')) + validTimeSpan;

            var response = TCL_Storage.getItem('response');

			if(loadResponse &&
				nowTimestamp < offsetCreatedAt &&
				response ){
                    // "un-string" it
                    TCL.config.form.RESPONSE = JSON.parse(response);
                    
                    // trigger form submission
                    $(TCL.config.form.QUOTES_BUTTON).trigger('click');

                    // scroll to map after vehicle rendering is complete
                    TCL.scrollToElement(TCL.config.form.MAP_CONTAINER, true);
			}
		}
	},

	/**
	 * user has selected a vehicle from quote
	 */
	vehicleSelected : function($element) {

		// get id of selected vehicle
		var id = $element.attr('data-vehicle-id'),
			$bottomSheetContainer = $('.modal.bottom-sheet');

		// populate bottom sheet modal and show
		$bottomSheetContainer.html(_.template($("#vehicles_bottom_modal").html())({
			id : id
		})).modal({

			// modal opened
			ready : function() {

				$('.modal-button').on('click', function() {

					// get action
					var $this = $(this),
						action = $(this).attr('data-action'),
						vehicle_id = $(this).attr('data-vehicle-id');

					// trigger the related event
					switch (action) {
						case 'EMAIL':
							TCL.emailQuote(vehicle_id);
							break;
						case 'BOOK':
							TCL.bookQuote(vehicle_id);
							break;
					}
				});

			},

			// modal dismissed
			complete : function() {

				// empty modal sheet content as soon as modal is dismissed
				$bottomSheetContainer.empty();

			}

		}).modal('open');

	},

	/**
	 * user selected to email quote
	 * @param {integer} vehicle_id, the selected vehicle's id
	 */
	emailQuote : function(vehicle_id) {

		// close bottom sheet modal
		$('.modal.bottom-sheet').modal('close');

		var $centerSheetContainer = $('.modal.center-sheet');

		// get template card content of selected vehicle
		var vehicle_card = _.template($("#vehicles_block").html())({
			vehicles : [TCL.config.form.RESPONSE['quote']['vehicles'][vehicle_id]],
			touchable : false,
			showExtra : false,
			button : false
		});

		// populate center sheet modal and show
		$centerSheetContainer.html(_.template($("#email_quote_modal").html())({
			vehicle_card : vehicle_card
		})).modal({

			// modal opened
			ready : function() {

				$('.modal-button').on("click", function() {

					// get payment url
					var paymentURL = TCL.getPaymentURL(vehicle_id);

					var data = {
						name: {
							value: $(TCL.config.form.MODAL_MAIN_ELEMENT).find('#name').val(),
							validate: {
								types: [Validation.types.NAME_CHECK],
								errors: ["The name field is not in the right format"]
							}
						},
						email : {
							value: $(TCL.config.form.MODAL_MAIN_ELEMENT).find('#email').val(),
							validate: {
								types: [Validation.types.EMAIL_CHECK],
								errors: ["The email field is not in the right format"]
							}
						},
						notes : {
							value: $(TCL.config.form.MODAL_MAIN_ELEMENT).find('#notes').val(),
							validate: false
						}
					};

					var validation = Validation.validateBulk(data);

					if (validation.valid) {

						$.ajax({
							url: "/home/emailQuote/",
							type: "POST",
							data: {
								name : data.name.value,
								email : data.email.value,
								message : data.notes.value,
								quotes_id : TCL.config.form.RESPONSE['quote_id'],
								quoteURL : window.location.href,
								quote : TCL.config.form.RESPONSE['quote'],
								vehicle_id : vehicle_id,
								journey : TCL.getJourneyPOST(),
								booking_url : paymentURL,
								distance : TCL.config.form.RESPONSE['journey']['distance'],
								duration : TCL.config.form.RESPONSE['journey']['duration_text']
							},
							dataType: "json",
							beforeSend : function() {
								$(TCL.config.form.MODAL_MAIN_ELEMENT).find('.modal-button').empty().addClass('loading disabled');
							},
							success : function(response) {

								if (response.status === 'OK') {

									// close modal
									$('.modal.center-sheet').modal('close');

									// show toast with success message
									TCL.showToast(TCL.config.generic.SUCCESS_TYPE, 'Quote has been emailed to ' + data.email.value);

								} else {

									// show error notification
									TCL.showNotification(
										TCL.config.generic.ERROR_TYPE,
										response.message,
										$('.modal.center-sheet')
									);

								}

							},
							complete : function() {
								$(TCL.config.form.MODAL_MAIN_ELEMENT).find('.modal-button').html('Email Quote').removeClass('loading disabled');
							}
						});

					} else {

						// else, show notification
						TCL.showNotification(
							TCL.config.generic.ERROR_TYPE,
							validation.error,
							$('.modal.center-sheet')
						);

					}
				});

			},

			// modal dismissed
			complete : function() {

				// empty modal sheet content as soon as modal is dismissed
				$centerSheetContainer.empty();

			}

		}).modal('open');
	},

	/**
	 * user selected to book quote
	 * @param {integer} vehicle_id, the selected vehicle's id
	 */
	bookQuote : function(vehicle_id) {
		// add loading to Book Now button
        $('.modal-button[data-action="BOOK"]').empty().css('color', 'transparent').addClass('loading disabled');

        // get payment url
		var url = TCL.getPaymentURL(vehicle_id);

        var virtualElement = $('<a></a>');
        $(virtualElement).attr("href", url);
        // Send link with Google Analytics function
        ga('linker:decorate', virtualElement[0]);
        window.location = $(virtualElement).attr("href");
        return false;
	},

	/**
	 * construct the payment url and return it
	 * @param {integer} vehicle_id, the selected vehicle's id
	 * @return {string}
	 */
	getPaymentURL : function(vehicle_id) {

		// reset the secureType because of possible invalid value coming back from the API response
		var urlToParse = TCL.config.form.RESPONSE['payment_url'],
			urlParts = /^(?:\w+\:\/\/)?([^\/]+)(.*)$/.exec(urlToParse);

		TCL.config.form.RESPONSE['secureType'] = urlParts[1];

		return TCL.config.form.RESPONSE['payment_url']
			+ TCL.config.form.RESPONSE['quote_id']
			+ "/?v=" + vehicle_id
			+ "&key=" + Taxicode_API.api_key
			+ (typeof test !== "undefined" && test ? "&test=1" : "")
			+ "&secure_page_title=" + encodeURIComponent(_SITE_TITLE)
			+ "&callback=" + encodeURIComponent(window.location.origin)
			+ "&source_domain_hostname=" + encodeURIComponent(_SOURCE_DOMAIN_HOSTNAME)
			+ "&secureType=" + TCL.config.form.RESPONSE['secureType']
			+ "&refererType=local";

	},

	/**
	 * Method to reset form to original state
	 */
	resetForm : function() {

		// reset journey fields
		TCL.journey.type = TCL.config.form.ONE_WAY;
		TCL.journey.pickup = '';
		TCL.journey.destination = '';
		TCL.journey.outbound = null;
		TCL.journey.return = null;
		TCL.journey.people = 1;
		TCL.journey.vias = [];
		TCL.journey.vias_elements = 0;
		// TCL.journey.location_suggestions not getting reset because we want to keep having the cached results

		// build form
		TCL.buildForm();

		//reset Quote Results
		TCL.resetQuotesResults();

		// clear storage
		TCL_Storage.reset();

		// hide reset button
		$('.clear-field').hide();

	},

    /**
	 * Method to reset the Quote results
     */
	resetQuotesResults : function() {
        // reset journey map
        if($(TCL.config.form.MAP_CONTAINER).is(':visible')) {
            MapBox.resetMap(function() {
                $(TCL.config.form.MAP_CONTAINER).hide();
            });
        }
        // remove journey info
        $('.journey-info-container').empty();
        // remove response vehicles
        $('.vehicles-container').empty();
	},

	/**
	 * Method to reset advanced form
	 */
	resetAdvancedForm : function() {

		$(TCL.config.form.ADVANCED_CONTAINER).find('input[type=text], textarea').val('');

	},

	/**
	 * build form elements
	 */
	buildForm : function() {

		// show / hide return container depending on journey type
		if (TCL.journey.type === TCL.config.form.RETURN) {
			document.getElementById('returnJourneyCheckbox').checked = true;
			$('.return-container').show();
		} else {
			document.getElementById('returnJourneyCheckbox').checked = false;
			$('.return-container').hide();
		}

		// fill pickup value
		$(TCL.config.form.MAIN_ELEMENT).find('#pickup').val(TCL.journey.pickup);

		// fill destination value
		$(TCL.config.form.MAIN_ELEMENT).find('#destination').val(TCL.journey.destination);

		// fill vias values, if any
		if (TCL.journey.vias.length) {

			for (var i = 0; i < TCL.journey.vias.length; i++) {

				// add the via element
				TCL.addVia();

				// fill it with location value
				$('.via-block:last').find('input[type=text]').val(TCL.journey.vias[i]);

			}

		} else {

			// or else, remove any existing via elements from booking form
			$('.via-container').empty();

		}

		// fill outbound date
		if (TCL.journey.outbound) {

			// split outbound string to two parts
			var outboundDate = TCL.journey.outbound.split(', ');

			// fill outbound value
			$(TCL.config.form.MAIN_ELEMENT).find('#outbound_date').val(outboundDate[0]);
			$(TCL.config.form.MAIN_ELEMENT).find('#outbound_time').val(outboundDate[1]);

		} else {

			// reset outbound value
			$(TCL.config.form.MAIN_ELEMENT).find('#outbound_date').val('');
			$(TCL.config.form.MAIN_ELEMENT).find('#outbound_time').val('');

		}

		if (TCL.journey.return) {

			// split outbound string to two parts
			var returnDate = TCL.journey.return.split(', ');

			// fill outbound value
			$(TCL.config.form.MAIN_ELEMENT).find('#return_date').val(returnDate[0]);
			$(TCL.config.form.MAIN_ELEMENT).find('#return_time').val(returnDate[1]);

		} else {

			// reset return value
			$(TCL.config.form.MAIN_ELEMENT).find('#return_date').val('');
			$(TCL.config.form.MAIN_ELEMENT).find('#return_time').val('');

		}

		// fill people value and disable subtract action if people value is greater than 1
		$(TCL.config.form.MAIN_ELEMENT).find('#people').val(TCL.journey.people);
		if (TCL.journey.people > 1) {
			TCL.updateSubtractButton(false);
		} else {
			TCL.updateSubtractButton(true);
		}

	},

	/**
	 * generate stars for testimonials
	 */
	generateStars : function() {

		$('.star-bar').each(function(){

			// get star container and its rating score
			var $this = $(this),
				$fillElement = $(this).find('.fill'),
				starValue = $this.attr('data-number');

			// append whole stars
			for (var i = 0; i <= starValue / 20 - 1; i++) {
				$fillElement.append(_.template($("#star_rating").html())({
					type: 'full'
				}));
			}

			// append half stars
			if (starValue % 20 > 10 && starValue) {
				$fillElement.append(_.template($("#star_rating").html())({
					type: 'half'
				}));
			}

		});

	},

	/**
	 * Determine whether app banner is shown
	 */
	showAppBanner : function() {

		// get operating system
		var os = TCL.getMobileOperatingSystem();

		// detect if template is a sales one, user is accessing from mobile device, os is either ios or android and cookie is not set
		if (_SALES_SITE && _MOBILE && $.inArray(os, ['iOS', 'Android']) !== -1 && !TCL.cookie.get('app_banner')) {

			// set cookie
			TCL.cookie.set('app_banner', 1, 30);

			var $banner = $('.app-store-banner');

			$banner.html(_.template($("#app_store_banner").html())({
				store : os
			})).slideDown('fast');


			$banner.find('.close-banner').click(function() {
				$banner.slideUp('fast', function() {
					$(this).empty();
				});
			});

		}

	},

	/**
	 * Determine whether app links are shown
	 */
	showAppLinks : function() {

		if (_SALES_SITE) {

			$('.download-icons').html(_.template($("#app_store_links").html()));

		}

	},

	/**
	 * Determine the mobile operating system.
	 * This function returns one of 'iOS', 'Android', 'Windows Phone', or 'unknown'.
	 *
	 * @returns {String}
	 */
	getMobileOperatingSystem : function() {
		var userAgent = navigator.userAgent || navigator.vendor || window.opera;

		// Windows Phone must come first because its UA also contains "Android"
		if (/windows phone/i.test(userAgent)) {
			return "Windows Phone";
		}

		if (/android/i.test(userAgent)) {
			return "Android";
		}

		// iOS detection from: http://stackoverflow.com/a/9039885/177710
		if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
			return "iOS";
		}

		return "unknown";
	},

	/**
	 * remove url parameters
	 */
	removeURLParams : function() {
		if (window.history && window.history.replaceState && window.location && window.location.search) {
			var new_params = [];
			var get_params = window.location.search.substr(1).split("&");
			var remove_params = arguments;
			var removed_params = [];
			for (var i = 0; i < get_params.length; i++) {
				var param = get_params[i].split("=");
				if (param.length) {
					if ($.inArray(param[0], remove_params) === -1) {
						new_params.push(param.length === 2 ? param[0] + "=" + param[1] : param[0]);
					} else {
						removed_params.push(param[0]);
					}
				}
			}
			var search_str = new_params.length ? "?" + new_params.join("&") : "";
			window.history.replaceState({pageTitle: document.title}, document.title, window.location.href.replace(window.location.search, search_str));
			return removed_params.length ? removed_params : false;
		}
		return false;
	},

	/**
	 * format date time information
	 * @param {string} date, the date string
	 * @param {string} time, the time string
	 * @return {string | boolean}
	 */
	formatDateTime : function(date, time) {

		// if date specified, format
		if (date) {

			var dateTimeString = '',
				dateSplit = date.split('/'),
				timeSplit = time.split(':');

			dateTimeString += dateSplit[0] + ' ' + TCL.config.generic.MONTHS[parseInt(dateSplit[1]) - 1] + ' ' + dateSplit[2] + ', ' + timeSplit[0] + ':' + timeSplit[1];

			return dateTimeString;

		}

		// if not date specified, return false
		return false;
	}

};

TCL.cookie = {
	max_str_length: 1000,
	set: function(name, value, expiredays) {

		if (value.length > this.max_str_length) {
			var split = value.match(new RegExp("(.{1," + this.max_str_length + "})", "g"));
			for (var i = 0; i < split.length; i++) {
				this.set(name + "_" + i, split[i], expiredays);
			}
			this.set(name, "parts=" + split.length, expiredays);
			return;
		}

		var exdate = new Date();
		exdate.setDate(exdate.getDate() + expiredays);
		document.cookie = name + "=" + escape(value) + ((expiredays == null) ? "" : ";path=/;expires=" + exdate.toGMTString());
	},
	get: function(name) {
		if (document.cookie.length > 0) {
			var start = document.cookie.indexOf(name + "=");
			if (start != -1) {
				start = start + name.length+1;
				var end = document.cookie.indexOf(";", start);
				if (end == -1) {
					end = document.cookie.length;
				}
				var value = unescape(document.cookie.substring(start, end));

				if (value.substr(0, 6) == "parts=") {
					var parts = parseInt(value.substr(6));
					var split = [];
					for (var i = 0; i < parts; i++) {
						split.push(this.get(name + "_" + i));
					}
					return split.join("");
				}

				return value;
			}
		}
		return "";
	},
	remove: function(name) {
		this.set(name, '', -1);
	}
};

document.addEventListener("DOMContentLoaded", function(event) { 
    TCL.init();
});