var FormElementAddress = Class.create({
	initialize: function (id, format, numFreeLines, tabIndex, showFields, enableAddressCompletion, forceCountryCode)
	{
		this.id = id;
		this.format = format;
		this.numFreeLines = numFreeLines;
		this.tabIndex = tabIndex;
		this.showFields = showFields;
		this.enableAddressCompletion = enableAddressCompletion;
		this.useAddressCompletion = false;
		this.forceCountryCode = forceCountryCode;
		this.readOnlyFields = [];

		this.mainElem = $(id);
		if (!this.mainElem) return false;
		this.curVals = {};
		this.triggerTimeout = null;

		if (!this.forceCountryCode) {
			this.PBAddress = new PBJSONRPCClient(PbLib.getNewURI("l/relation/rpc/address"));

			this.countryElem = $(id).select(".address-country").first();
			if (this.countryElem) {
				this.countryElem.observe("change", this.changeCountry.bindAsEventListener(this));
				this.countryElem.observe("pb:change", this.changeCountry.bindAsEventListener(this, true));
			}
		}

		if (this.enableAddressCompletion) {
			this.AddressCompletion = new PBJSONRPCClient(PbLib.getNewURI("l/postcode/rpc"));
			if (this.forceCountryCode) {
				this.readOnlyFields = this.AddressCompletion.call("getReadOnlyFields", this.forceCountryCode);
			} else {
				this.readOnlyFields = this.AddressCompletion.call("getReadOnlyFields", this.countryElem.value);
			}
			this.useAddressCompletion = this.readOnlyFields !== false;

			if (this.useAddressCompletion) {
				this.addressCompletionObserve();
				this.doAddressCompletion();
			}
		}
	},

	addressCompletionObserve: function ()
	{
		this.mainElem.select('input,select').each((function(elem) {
			elem.observe('change', this.triggerAddressCompletion.bindAsEventListener(this));
			elem.observe('keyup', this.triggerAddressCompletion.bindAsEventListener(this));
		}).bind(this));
	},

	triggerAddressCompletion: function (event)
	{
		if (this.triggerTimeout) {
			window.clearTimeout(this.triggerTimeout);
		}
		this.triggerTimeout = window.setTimeout(this.doAddressCompletion.bind(this), 400);
	},

	doAddressCompletion: function (event)
	{
		var data = {};
		this.mainElem.select('input,select').each((function(elem) {
			data[elem.id.substring(this.id.length+1)] = elem.value;
		}).bind(this));

		if (!this.forceCountryCode) {
			data = this.AddressCompletion.call("lookup", this.countryElem.value, data);
		} else {
			data = this.AddressCompletion.call("lookup", this.forceCountryCode, data);
		}

		if (data) {
			this.mainElem.select('input,select').each((function(elem) {
				if (data[elem.id.substring(this.id.length+1)]) {
					if (elem.value != data[elem.id.substring(this.id.length+1)]) {
						elem.value = data[elem.id.substring(this.id.length+1)];
					}
				}

				if (this.readOnlyFields.indexOf(elem.id.substring(this.id.length+1)) > -1) {
					elem.disabled = true;
				}
			}).bind(this));
		} else {
			this.mainElem.select('input,select').each(function(elem) {
				elem.disabled = false;
			});
		}
	},
	cachedFormats: {},
	getFormat: function (countryCode)
	{
		if (this.cachedFormats[countryCode]) {
			return this.cachedFormats[countryCode];
		}

		var format = false;
		if (this.enableAddressCompletion) {
			format = this.AddressCompletion.call("getFormat", countryCode);
			this.readOnlyFields = this.AddressCompletion.call("getReadOnlyFields", countryCode);
			this.useAddressCompletion = true;
		}
		if (format == false) {
			this.useAddressCompletion = false;
			format = this.PBAddress.call("getFormat", countryCode, true);
		}

		return this.cachedFormats[countryCode] = format;
	},
	cachedStates: {},
	getStates: function (countryCode)
	{
		if (this.cachedStates[countryCode]) {
			return this.cachedStates[countryCode];
		}

		return (this.cachedStates[countryCode] = this.PBAddress.call("getStates", countryCode));
	},
	changeCountry: function (event, noFocus)
	{
		var noFocus = !!(noFocus);
		var countryCode = this.countryElem.value;
		this.countryElem.stopObserving("change", this.changeCountry);

		// Collect current values
		this.mainElem.select('.address-field').each((function (addressField)
		{
			this.curVals[addressField.id] = $F(addressField);
		}).bind(this));

		// Create temporary container
		var container = new Element("div");

		// Get the format from the webservice
		var format = this.getFormat(countryCode);
		this.format = format + "";

		var match, key, myId, label, lineKey;
		var elems = {};
		var parts = $A();
		var row = $A();
		parts.unshift(row);
		while (match = format.match(/^([\s\S]*)#\{([^\}]+)\}([\s\S]*?\n*)$/im)) {
			format = match[1];
			key = match[2].toLowerCase();

			if (match[3].match(/(\n)/m)) {
				delete row;
				row = $A();
				parts.unshift(row);
			}

			if (key == "country_name") {
				key = "country";
			}

			if (elems[key] || !this.showFields[key]) {
				continue;
			}

			if (key == "freetext") {
				if (this.numFreeLines > 0) {
					elems[key] = true;
					var freeElem;
					for (var i = 0; i < this.numFreeLines; i++) {
						if (i > 0) {
							delete row;
							row = $A();
							parts.unshift(row);
						}

						freeElem = new Element("input", {"type": "text"});
						freeElem.name = "formdata[" + this.id + "][" + key + "][]";
						freeElem.addClassName("address-field address-" + key);

						myId = this.id + "-" + key + "-" + i;
						if (this.curVals[myId]) {
							freeElem.value = this.curVals[myId];
						}
						freeElem.id = myId;
						freeElem.writeAttribute("tabindex", this.tabIndex);

						row.unshift({
							'key': key,
							'id': myId,
							'elem': freeElem,
							'label': PbLib.g('Free text')
						});
						delete freeElem;
					}
				}
			} else {
				label = false;
				switch (key) {
					case "name":
						label = label || PbLib.g('Addressee');
					case "street":
						label = label || PbLib.g('Street');
					case "number":
						label = label || PbLib.g('Number');
					case "number_add":
						label = label || PbLib.g('Addition');
					case "postcode":
						label = label || PbLib.g('Postcode');
					case "town":
						label = label || PbLib.g('Town');
						elems[key] = new Element("input", {"type": "text"});
						break;

					case "state":
						label = label || PbLib.g('State');
						elems[key] = new Element("select");
						elems[key].appendChild(new Element("option"));
						$H(this.getStates(countryCode)).each(function (pair) {
							elems[key].appendChild(new Element("option", {"value": pair.key})).update(pair.value);
						});
						break;

					case "country":
						label = label || PbLib.g('Country');
						elems[key] = new Element("select");
						elems[key].appendChild(new Element("option"));
						$H(this.countryList).each(function (pair) {
							elems[key].appendChild(new Element("option", {"value": pair.key})).update(pair.value);
						});
						break;
				}
				elems[key].name = "formdata[" + this.id + "][" + key + "]";
				elems[key].addClassName("address-field address-" + key);

				myId = this.id + "-" + key;
				if (this.curVals[myId]) {
					elems[key].value = this.curVals[myId];
				}
				elems[key].id = myId;
				elems[key].writeAttribute("tabindex", this.tabIndex);

				row.unshift({
					'key': key,
					'id': myId,
					'elem': elems[key],
					'label': label
				});
			}
		}
		delete row;

		while (this.mainElem.firstChild) {
			this.mainElem.removeChild(this.mainElem.firstChild);
		}

		var lastKey = null;
		parts.each(function (row)
		{
			var P = this.mainElem;
			var n = row.size();
			if (n == 0) {
				return;
			} else if (n > 1) {
				P = P.appendChild(new Element('div', {'class': 'address-row'}));
			}
			row.each(function (part)
			{
				var label = P.appendChild(new Element('label', {'for': part.id}));
				if (part.key == lastKey) {
					label.addClassName('sr');
				}
				if (n != 1) {
					label.appendChild(new Element('span')).update(part.label);
					label.appendChild(document.createTextNode('\n')); // <- The newline is needed to prevent an IE 6 bug
				} else {
					label.update(part.label);
				}
				if (n == 1) {
					P.appendChild(part.elem);
				} else {
					label.appendChild(part.elem);
				}
				lastKey = part.key;
			}.bind(this));
		}.bind(this));

		this.countryElem = $(this.id + "-country");
		this.countryElem.observe("change", this.changeCountry.bindAsEventListener(this));
		this.countryElem.observe("pb:change", this.changeCountry.bindAsEventListener(this, true));

		if (this.useAddressCompletion) {
			this.addressCompletionObserve();
			this.doAddressCompletion();
		}

		if (!noFocus) {
			this.countryElem.focus();
		}

	}
});