/**
 * 
 * @param {String} postcodeId - postcode input for auto select
 * @param {String} lookupButtonId - lookup button id
 * @param {String} addressSummaryId - address summary container
 * @param {String} autoAddressId - container for auto part
 * @param {String} manualAddressId - container for manual entry part
 * @param {String} adjustAddressId - button for adjusting an automatically determined address
 */
function PostcodeLookup(postcodeId, lookupButtonId, addressSummaryId, autoAddressId, manualAddressId, adjustAddressId) {
	try {
		this._ajax = new Ajax('ajax');
	} catch (err) {
		throw new Error("Unable to create Ajax object");
	}

	// get elements from the document...
	var self = this;
	this._postcode = document.getElementById(postcodeId);
	this._lookupButton = document.getElementById(lookupButtonId);
	this._autoAddresses = this._parseNodeIds(autoAddressId);
	this._manualAddresses = this._parseNodeIds(manualAddressId);
	this._addressSummaryRow = document.getElementById(addressSummaryId);
	this._adjustAddress = document.getElementById('adjustAddress');
	var summaryChildNodes = getChildElements(this._addressSummaryRow);

	// set attributes of elements...
	this._ajax.onAjaxError = function(e) {self._onAjaxError(e);};
	this._lookupButton.setAttribute('type', 'button');
	this._addressSummaryRow.elements = { label: summaryChildNodes[0], summary: summaryChildNodes[2].firstChild, adjust: summaryChildNodes[1], input: summaryChildNodes[3] };
	this._addressSummaryRow.clear = function() {
		this.elements.summary.firstChild.replaceData('No address selected.');
	}
	this._autoAddresses.hidePanel = hide;
	this._autoAddresses.showPanel = show;
	this._manualAddresses.hidePanel = hide;
	this._manualAddresses.showPanel = show;
	this._manualAddresses.populate = function(address) {
		var inputs = [];
		if (this.length) { // have we got an array of DOM nodes...
			for (var x = 0; x < this.length; x++) {
				var tmpInputs = this[x].getElementsByTagName('input');
				for (var y = 0; y < tmpInputs.length; y++) {
					inputs.push(tmpInputs[y]);
				}
			}
		} else {
			inputs = this.getElementsByTagName('input');
		}
		for (var x = 0; x < inputs.length; x++) {
			switch (inputs[x].name) {
				case 'addressLine1':
					inputs[x].value = address.line1;
					break;
				case 'addressLine2':
					inputs[x].value = address.line2;
					break;
				case 'addressLine3':
					inputs[x].value = address.line3;
					break;
				case 'postTown':
				case 'county':
				case 'postCode':
					inputs[x].value = address[inputs[x].name];
					break;					
			}
		}
	}
	this._adjustAddress.hide = hide;
	this._adjustAddress.show = show;

	// create required elements...
	this._createElements();

	// create callbacks...
	eventSubscribe( this._lookupButton, 'click', createCallback(this, 'beginPostcodeLookup') );
	eventSubscribe( this._adjustAddress, 'click', createCallback(this, 'populateManualAddress') );
}

PostcodeLookup.prototype = {
	/**
	 * creates elements needed for the lookup functionality...
	 */
	_createElements: function() {
		//this._createOverlay();
		this._createAddressList();
	},

	/**
	 * creates the overlay: not used now as I couldn't make it work in IE6...
	 * @deprecated
	 */
	_createOverlay: function() {
		var overlay = document.createElement('div');
		overlay.id = 'overlay';
		var pageDims = getPageSize();
		overlay.style.height = pageDims.pageHeight + 'px';

		overlay.show = function() {
			document.body.appendChild(this);
		}

		overlay.hide = function() {
			document.body.removeChild(this);
		}

		this._overlay = overlay;
	},

	/**
	 * creates the address list selector
	 */
	_createAddressList: function() {
		var addressList = document.createElement('ul');
		addressList.id = 'addressList';
		var self = this;
		
		// override link for if the address isn't listed...
		var override = document.createElement('li');
		override.id = 'override';
		override.style.visibility = 'visible';
		override.appendChild(document.createTextNode("My address isn't here..."));
		override.onclick = function(e) {
			addressList.hide();
			self.enableManualEntry();			
		}
		
		addressList.appendChild(override);

		addressList.clear = function() {
			/*this.innerHTML = '';
			this.appendChild(override);*/
			while (this.firstChild.nextSibling != null) {
				var node = this.firstChild.nextSibling;
				this.removeChild(node); 
			}
		}
		
		addressList.addAddress = function(address) {
			var aItem = document.createElement('li');
			aItem.id = address.id;
			aItem.appendChild(document.createTextNode(address.desc));
			var cb = function(e) { 
				var target = (e.currentTarget != undefined) ? (e.currentTarget) : (e.srcElement); 
				self.beginAddressLookup(target.getAttribute('id'));
			}
			eventSubscribe(aItem, 'click', cb);
			this.appendChild(aItem);
		}

		addressList.show = function() {
			//document.body.appendChild(this);
			document.getElementById('syndicatePackRequest').appendChild(this);
			
			// hide all select boxes because IE will make them show through any overlaying layer...
			var selects = document.getElementsByTagName('select');
			for (var xx = 0; xx < selects.length; xx++) {
				selects[xx].style.visibility = 'hidden';
			}
		}

		addressList.hide = function() {
			//document.body.removeChild(this);
			document.getElementById('syndicatePackRequest').removeChild(this);
			
			// display all the select boxes as we hid them before...
			var selects = document.getElementsByTagName('select');
			for (var xx = 0; xx < selects.length; xx++) {
				selects[xx].style.visibility = '';
			}
		}

		this._addressList = addressList;
	},
	
	/**
	 * some properties, such as _manualAddresses and _autoAddresses, can hold either single DOM node values or arrays of DOM nodes. This utility function  
	 * generates an array of DOM nodes
	 * @param {Mixed} id of dom node, or an array of values for dom nodes.
	 * @return {Array} or an array of values for dom nodes (if only a single id is passed in, this is still an array)
	 */
	_parseNodeIds: function(val) {
		var els = [];
		if (val.length) {
			els = [];
			for (var x = 0; x < val.length; x++) {
				els.push(document.getElementById(val[x]));
			}
		} else {
			els.push(document.getElementById(val));
		}
		return els;
	},
	
	/**
	 * custom handler for ajax errors: tries to post back to the server instead, although because of IE's idiosyncracies, this handling only really works in FF...
	 * @param {Object} err
	 */
	_onAjaxError: function(err) {
		// if the ajax failed for whatever reason, we'll revert to manual entry...
		alert('We apologise but there has been a problem looking up your postcode, please type your address in instead.');
		this.enableManualEntry();
		document.getElementById('addressLine1').focus();
	},

	/**
	 * wrapper for show function of _addressList object
	 */
	_showAddressList: function() {
		//this._overlay.show();
		this._addressList.show();
	},

	/**
	 * wrapper for hide function of _addressList object
	 */
	_hideAddressList: function() {
		//this._overlay.hide();
		this._addressList.hide();
	},

	/**
	 * fires off the postcode lookup back to the server
	 * @param {Object} e
	 */
	beginPostcodeLookup: function(e) {
		eventAbortAction(e);
		if (this._postcode.value != '') {
			// hide manual address fields...
			this._manualAddresses.hidePanel();
			
			// set lookup data so that if the Ajax call goes wrong, we can still do a postback to the server as a fallback...
			this._lookupData = {type: 'lookupPostcode'};

			this._ajax.execute(createCallback(this, 'endPostcodeLookup'), 'lookupPostcode', [this._postcode.value]);
		} else {
			alert('Please enter a postcode before trying a lookup');
		}
	},
	
	/**
	 * callback for when we've had a response from the server on the postcode lookup - adds the addresses to the _addressList object and displays it...
	 * @param {Object} payload
	 */
	endPostcodeLookup: function(payload) {
		this._lookupData = null;
		this._addressList.clear();

		if ((payload.list != undefined) && payload.list.length > 0) {
			// display list...
			for(var xx = 0; xx < payload.list.length; xx ++) {
				this._addressList.addAddress(payload.list[xx]);
			}
			this._showAddressList();
		} else {
			var doManualEntry = confirm('No addresses match this postcode: do you want to just type in your address?');
			if (doManualEntry) {
				this.enableManualEntry();
			} else {
				this._postcode.focus();
			}
		}
	},

	/**
	 * when an address is selected from the list, this makes a server call for the full address details...
	 * @param {Object} id
	 */
	beginAddressLookup: function(id) {
		// set lookup data so that if the Ajax call goes wrong, we can still do a postback to the server as a fallback...
		this._lookupData = {type: 'lookupAddress', addressId: id};

		this._ajax.execute(createCallback(this, 'endAddressLookup'), 'lookupAddress', [id]);
		this._hideAddressList();
	},

	/**
	 * callback for when the server returns the address details...
	 * @param {Object} payload
	 */
	endAddressLookup: function(payload) {
		this._lookupData = null;
		this._selectedAddress = new Address(payload);

		// display address...
		this._addressSummaryRow.elements.summary.innerHTML = '';
		this._addressSummaryRow.elements.summary.appendChild(document.createTextNode(this._selectedAddress.toString()));
		this._addressSummaryRow.elements.input.value = payload.id;
		this._adjustAddress.show();

		// re-run validation tests...
		this._addressSummaryRow.elements.input.validator.isValid();

/*		if (this._addressSummary == undefined) {
			this._addressSummary = document.createElement('span');
			this._addressSummary.className = 'addressSummary';
			this._postcode.parentNode.appendChild(this._addressSummary);
		} else {
			this._addressSummary.innerHTML = '';
		}
		this._addressSummary.appendChild(document.createTextNode(a.toString()));*/
	},
	
	/**
	 * populates the manual address entry fields with the selected address...
	 */
	populateManualAddress: function(e) {
		eventAbortAction(e);
		this._manualAddresses.populate(this._selectedAddress);
		this.enableManualEntry();
		document.getElementById('addressLine1').focus();
	},

	/**
	 * switches to manual entry mode for the address
	 */
	enableManualEntry: function() {
		// display the manual entry panel...
		this._manualAddresses.showPanel();
		
		// IE6 gets in a muddle and displays the manual address fields underneath question 2, so force a re-render...
		//forceReRender(document.getElementById("q2"), true);
		/*var fieldsets = document.getElementByTagName('fieldset');
		for (var x = 0; x < fieldsets.length; x++) {
			forceReRender(document.getElementById("q2"), false);			
		}*/
		
		// hide the auto entry panel...
		this._autoAddresses.hidePanel();
	}
}

function Address(parts) {
	this._props = ['organisationName', 'departmentName', 'line1', 'line2', 'line3', 'line4', 'line5', 'postTown', 'county', 'postCode'];
	for (var xx = 0; xx < this._props.length; xx++) {
		if ( (parts[this._props[xx]] != undefined) ) {
			this[this._props[xx]] = parts[this._props[xx]];
		} else {
			this[this._props[xx]] = '';
		}
	}
}

Address.prototype = {
	toString: function() {
		var output = '';
		for (var xx = 0; xx < this._props.length; xx++) {
			if (this[this._props[xx]] != '') {
				output += this[this._props[xx]] + ', ';
			}
		}
		output = output.substring(0, (output.length - 2));
		return output;
	}
}
 
/**
 * should never be used in isolation, this function should be attached to existing DOM nodes
 * in order to hide them
 */
function hide() {
	if (this.length) {
		for (var x = 0; x < this.length; x++) {
			if (this[x].hide == undefined) {
				this[x].hide = hide;
			}
			this[x].hide();
		}
	} else {
		if (this.className.indexOf('hide') == -1) {
			this.className += ' hide';
			if (this.childNodes.length > 0) {
				var inputs = this.getElementsByTagName('input');
				
				// disable all input elements being that we're hiding...
				for (var x = 0; x < inputs.length; x++) {
					inputs[x].disabled = true;
				}
			}
		}
	}
}

/**
 * should never be used in isolation, this function should be attached to existing DOM nodes
 * in order to show them after they've previously been hidden
 */
function show() {
	if (this.length) {
		for (var x = 0; x < this.length; x++) {
			if (this[x].show == undefined) {
				this[x].show = show;
			}
			this[x].show();
		}
	} else {
		this.className = this.className.replace(/\s*hide/, '');
		if (this.childNodes.length > 0) {
			var inputs = this.getElementsByTagName('input');
			
			// enable all input elements now we're being displayed...
			for (var x = 0; x < inputs.length; x++) {
				inputs[x].disabled = false; 
			}
			
			// focus on first input element...
			if (inputs.length > 0) {
				inputs[0].focus();
			}
		}		
	}
}