/**
 * @access global
 * sr_form_instances - global variable
 * Each add-to-basket form created adds a reference to itself here
 * Used to close all open forms when opening a new one
 */
var sr_form_instances = {};

/**
 * @access global
 * Scan the documents script tags for our own url and use that to deduce url base
 * @return string - the url base to use (eg /rental)
 */
function sr_get_url_base()
{
	var bp = '/rental/';
    $A(document.getElementsByTagName("script")).findAll( function(s) {
      return (s.src && s.src.match(/js\/categories\.js(\?.*)?$/))
    }).each( function(s) {
      bp = s.src.replace(/js\/categories\.js(\?.*)?$/,'');
    });
	return bp;
}

/**
 * @access global
 * Make element visible and show the loading bar ...
 * @param htmlElement div - the element
 */
function sr_show_loader(el)
{
	$(el).innerHTML = "<div align=\"center\"><img src=\"" + sr_get_url_base() + "img/load_bar.gif\" style=\"margin:25px;\"></div>";
	$(el).style.display = "block";
}

/**
 * @access global
 * Show more verbose info on an exception
 * @param exception e
 */
function sr_handle_error(e, strWhere)
{
	var msg = "SCRIPT ERROR\n-----------\n";
	if(strWhere)
	{
		msg += "In: " + strWhere + "\n";
	}
	for(var i in e) msg += i + " : " + e[i] + "\n";
	alert(msg);
}

/**
 *	Send an AJAX request to refresh the on-screen basket summary
 *	Called in link onclick (passing element - this)
 */
function sr_reload_basket(el_link)
{
	if((el_link.title == "") || confirm(el_link.title))
	{
		new Ajax.Request(el_link.href, { onSuccess: function(transport) {
			if(sr_check_ajax_response(transport))
			{
				$('right_col').update(transport.responseText);
			}
		} } );
		return false; // important so the link doesn't trigger as well
	}
}

/**
 * Some of our AJAX calls receive HTML, but all should receive as JSON
 * error status if the session's expired.
 */
function sr_check_ajax_response(transport)
{
	// if we've got a JSON object with the key 'session' set, it's expired
	try {
		var test = transport.responseJSON.session.toString();
		self.location = sr_get_url_base() + "baskets/expired";
		return false;
	}
	catch(e) {
		return true;
	}	
}

// ==========================================================================================

/**
 * package_display - javascript class
 * When instantiated, attaches actions to the product options form.
 * One of these is started for each package on the categories page
 * @param integer id - the id value from the packages table
 * @param object obj_prices - assoc. array of price info ...
 * 							  pr_ski, pr_boot, pr_helmets, disc_rate
 */
function package_display(id, obj_prices)
{
	try {
		var div = $( 'package_' + id );
		var ski_img = $( 'ski_img_' + id );
		var ski_form_div = $( 'ski_form_' + id );
		var opts_form = div.getElementsByTagName('form')[0];
		opts_form.onsubmit = function()
		{
			return sendForm();
		}
		ski_img.onclick = function()
		{
			return sendForm();
		}
		clone_description();
		var price_updater = new sr_price_updater(opts_form, obj_prices);
		// for some reason IE wasn't liking prototype's setStyle() method here
		// div.getElementsByTagName('button')[0].style.display = "none";
	}
	catch(e)
	{
		sr_handle_error(e);
	}
	/**
	 * HIJAX the options form - send its contents via AJAX and load reponse
	 * into a div below the product display. Return false to stop normal submit
	 */
	function sendForm()
	{
		close_other_forms();
		sr_show_loader(ski_form_div);
		new Ajax.Request(opts_form.action + "?t=" + Math.random(), {
			postBody: Form.serialize(opts_form),
			onSuccess: function(transport) {
				if(sr_check_ajax_response(transport))
				{
					ski_form_div.update(transport.responseText);
					new basket_add_form(ski_form_div, opts_form);
					price_updater.set_form(ski_form_div);
				}
			}
		});
		return false;
	}
	/**
	 * Find the description div and copy its contents into a new div for
	 * mouse-overs
	 */
	function clone_description()
	{
		var dd = div.getElementsByClassName('ski_desc')[0];
		dd.style.display = "none";
		var tt;
		ski_img.onmouseover = function()
		{
			tt = new Element('div', { 'class': 'ski_desc_hover' }).update("<p>" + dd.innerHTML + "</p>");
			var pos = div.cumulativeOffset();
			tt.setStyle( {	position: "absolute",
							left: (pos[0] + div.offsetWidth) + "px",
							top: pos[1] + "px"
						} );
			document.body.appendChild(tt);
			if(tt.getDimensions().height < 60)
			{
				tt.firstDescendant().setStyle({'height': '60px'});
			}
		}
		ski_img.onmouseout = function()
		{
			tt.remove();
		}
		
	}
	/**
	 * When we open a form, close any others that are open
	 */
	function close_other_forms()
	{
		for(var i in sr_form_instances)
		{
			if(sr_form_instances[i])
			{
				sr_form_instances[i].close_form();
			}
		}
	}
	
}

//===================================================================================

/**
 * basket_add_form - javascript class
 * Once the add-to-basket form is loaded, an instance of this  will assign actions
 * to it.
 * @param htmlElement div - the form's container div
 * @param htmlElement opts_form - a refence to first (options) form
 */
function basket_add_form(div, opts_form)
{
	try {
		div.style.display = "block";
		var add_form = div.getElementsByTagName('form')[0];
		add_form.onsubmit = function() {
			return send_form();
		}
		getButton('cancel').onclick = function()
		{
			return close_form();
		}
		sr_form_instances[div.id] = this;
	}
	catch(e)
	{
		sr_handle_error(e);
	}
	
	/**
	 * Identify the save / cancel buttons by checking their className
	 * @param string cn - the name of the class to look for
	 * @returns {mixed} htmlElement on success / null on failure
	 */
	function getButton(cn)
	{
		return $(add_form).getElementsByClassName(cn)[0];
	}

	/**
	 * HIJAX this form too
	 * We're expecting JSON back ...
	 * { status: 'OK' } on success
	 * or { status: 'ERR', fields: { 'name1': 1, ... } } on failure
	 * Remove our form and reload the basket on sucess
	 * Highlight invalid fields on failure
	 */
	function send_form()
	{
		new Ajax.Request(add_form.action, {
			postBody: Form.serialize(add_form),
			onComplete: function(transport) {
				if(sr_check_ajax_response(transport))
				{
					var json = transport.responseJSON;
					if(json == null)
					{
						alert("Error - in response from server:\n" + e.message + "\n"
							  + transport.responseText);
					}
					else if(json.status == "OK")
					{
						close_form();
						new Ajax.Updater('right_col', sr_get_url_base() + 'baskets/preview');
					}
					else if(json.fields)
					{
						if(json.message)
						{
							alert(json.message);
						}
						error_fields(json.fields);
					}
				}
			} } );
		return false;
	}	
	/**
	 *	If send_form got a failure response, use it to highlight invalid fields
	 */
	function error_fields(obj_fields)
	{
		for(var i=0; i<add_form.elements.length; i++)
		{
			var el = add_form.elements[i];
			if( el.name && el.name.match(/data\[.+\]\[(.+)\]/) )
			{
				el.style.background = obj_fields[RegExp.$1] ? "#ff6666" : "";
			}
		}
	}
	/**
	 * Remove our add-to-basket form and hide it's container div
	 * Make this method publicly availabe so other objects can close form
	 */
	this.close_form = close_form;
	function close_form()
	{
		add_form.parentNode.style.display = "none";
		add_form.parentNode.innerHTML = "";
		delete sr_form_instances[div.id];
		return false;
	}
	
}

// =====================================================================================

/**
 * Price updating class - attach to options form so that options selects are passed to
 * add-to-basket form dynamically, and so that prices update dynamically, too.
 * @param htmlFormElement el_form - the form to attach to 
 */
function sr_price_updater(el_form, obj_prices)
{
	var opts = new Hash();
	var spans = new Hash(); // tags that store prices
	var add_form;
	// Grab some references to the price tags to update
	spans.set('price_rrp', $(el_form).getElementsByClassName('ski_price_rrp')[0]);
	spans.set('price', $(el_form).getElementsByClassName('ski_price')[0]);
	// Grab references to the checkboxes and add their actions
	// And run an update now (in case the browser's left ticks after refresh)
	scan_form(el_form);
	pr_update();
	/**
	 *	Scan a form's elements for checkboxes the influence our price calculation
	 *	@param DomElement - the form to scan
	 */
	function scan_form(el_form)
	{
		Form.getElements(el_form).each( function(item) {
			if(item.type == "checkbox")
			{
				if(item.name.match(/data\[.+\]\[(.+)\]/))
				{
					opts.set('pr_' + RegExp.$1, item);
					item.onclick = function() { pr_update(); };
				}
			}
		} );
	}
	/**
	 * Our updater method, called whenever one of the checkboxes is changed
	 * Slightly botched to include the insurance field ...
	 * @param DomElement el_ins - the insurance dropdown (if called by that field)
	*/
	function pr_update(el_ins)
	{
		var price = parseFloat(obj_prices['pr_skis']);
		opts.keys().each( function(item) {
			var el = opts.get(item);
			price += el.checked ? parseFloat(obj_prices[item]) : 0;
			if($(add_form))
			{
				try { // we need this to happen, so make a fuss if it breaks!
					$(add_form).elements[el.name].value = el.checked ? 1 : 0;
				}
				catch(e)
				{
					sr_handle_error(e);
				}
			}
		} );
		var disc_price = price * ( 1 - (parseFloat(obj_prices['disc_rate']) / 100))
		spans.get('price_rrp').update(price.toFixed(2) + ' &euro;');
		spans.get('price').update(disc_price.toFixed(2) + ' &euro;');
	}
	/**
	 * Our public method for attaching the add-to-basket form for options updates
	 */
	this.set_form = function(el_container)
	{
		add_form = el_container.getElementsByTagName('form')[0];
		scan_form(add_form);
		pr_update();
	}

}


