if (typeof P4 == 'undefined')
	var P4 = {};
// define P4 so we can later include P4.Util

var Configurator =
{
	selectedBeforePage: [],
	selectedAfterPage: [],
	onPage: [],
	rules: {},
	messages: {},

	init: function()
	{
		var basePrice = $('basePrice').innerHTML;
		Element.remove('noJavascript');
		Element.show('withJavascript');
		this.basePrice = P4.Util.parseCurrency(basePrice);
		var shippingElement = $('shipping');
		this.shipping = shippingElement ? P4.Util.parseCurrency(shippingElement.innerHTML) : 0;
		Configurator.fractionSeparator = P4.Util.getFractionSeparator(basePrice);
		Configurator.currencySymbol = P4.Util.getCurrencySymbol(basePrice);
		this.productThumb = new Image();
		this.productThumb.src = this.productPicture;
		this.page = $F('page');
		this.onPage.each(function(option)
		{
			var input = $(option.input);
			if (input)
			{
				input.observe('change', this.drawSelectedOptions.bindAsEventListener(this));
				input.observe('click', this.drawSelectedOptions.bindAsEventListener(this));
				input.observe('keyup', this.drawSelectedOptions.bindAsEventListener(this));
			}
		}.bind(this));
		this.drawSelectedOptions();
	},

	drawSelectedOptions: function()
	{
		this.applyRules();
		var dom = [];
		var configuredPrice = this.basePrice + this.shipping;
		var previousStepName = null;
		var altImage = null;
		var include = function(option)
		{
			option.toDom(previousStepName).each(function(element)
			{
				dom.push(element);
			});
			configuredPrice += option.getPrice();
			previousStepName = option.stepName;
			if (option.altImage)
				altImage = option.altImage;
		};
		this.selectedBeforePage.each(include);
		this.onPage.each(function(option)
		{
			if (option.isSelected())
				include(option);
		});
		this.selectedAfterPage.each(include);
		$$('img.imageSwitchable').each(function(productImg)
		{
			productImg.src = altImage ? altImage.src : this.productThumb.src;
			if (productImg.parentNode.nodeName.toLowerCase() == 'a' && productImg.parentNode.rel == 'lightbox')
				productImg.parentNode.href = altImage ? altImage.src : this.productPicture;
		}.bind(this));
		$('configuredPrice').innerHTML = P4.Util.formatNumber(configuredPrice, 2, this.fractionSeparator, this.currencySymbol);
		var optionsTable = $('selectedOptions');
		while (optionsTable.hasChildNodes())
			optionsTable.removeChild(optionsTable.firstChild);
		dom.each(function(element)
		{
			optionsTable.appendChild(element);
		});
		// horrible hack that both firefox and IE need
		//noinspection SillyAssignmentJS
		optionsTable.parentNode.innerHTML = optionsTable.parentNode.innerHTML;
	},

	gotoPage: function(page)
	{
		var form = $('configuratorForm');
		var gotoInput = document.createElement('input');
		gotoInput.type = 'hidden';
		gotoInput.name = 'gotoSubmit';
		gotoInput.value = page;
		form.appendChild(gotoInput);
		form.submit();
	},

	applyRules: function()
	{
		var getIfSelected = function(chain)
		{
			var option = this.selectedBeforePage.find(Configurator.Option.withChain(chain));
			if (option)
				return option;
			option = this.selectedAfterPage.find(Configurator.Option.withChain(chain));
			if (option)
				return option;
			option = this.onPage.find(Configurator.Option.withChain(chain));
			return option && option.isSelected() ? option : null;
		}.bind(this);
		var unlocked = [];
		var unlockedThisIteration = 1;
		while (unlockedThisIteration > 0)
		{
			var locked = [];
			unlockedThisIteration = 0;
			$H(this.rules).each(function(entry)
			{
				var option = getIfSelected(entry.key);
				if (option)
				{
					entry.value.requires.each(function(requiredOptionChain)
					{
						var requiredOption = this.onPage.find(Configurator.Option.withChain(requiredOptionChain));
						if (requiredOption)
						{
							requiredOption.require(this.messages.requiredBy.replace(/\{0\}/, option.stepName).replace(/\{1\}/, option.name));
							locked.push(requiredOption);
						}
					}.bind(this));
					entry.value.prohibits.each(function(prohibitedOptionChain)
					{
						var prohibitedOption = this.onPage.find(Configurator.Option.withChain(prohibitedOptionChain));
						if (prohibitedOption)
						{
							prohibitedOption.prohibit(this.messages.prohibitedBy.replace(/\{0\}/, option.stepName).replace(/\{1\}/, option.name));
							locked.push(prohibitedOption);
						}
					}.bind(this));
				}
			}.bind(this));

			// unlock other options
			this.onPage.each(function(option)
			{
				if (!locked.member(option))
				{
					if (option.enable())
					{
						unlockedThisIteration++;
						unlocked.push(option);
					}
				}
			});
		}
		this.selectOneInRadioGroup(unlocked);
	},

	selectOneInRadioGroup: function(options)
	{
		options.findAll(function(option)
		{
			return $(option.input).type == 'radio';
		}).each(function(option)
		{
			var optionsInStep = option.allInStep().findAll(function(option)
			{
				return option.isEnabled();
			});
			var selected = optionsInStep.find(function(otherOption)
			{
				return $(otherOption.input).checked;
			});
			if(!selected && optionsInStep.length > 0)
			{
				$(optionsInStep[0].input).checked = true;
			}
		});
	}
};

Configurator.Option = Class.create();
Configurator.Option.prototype =
{
	initialize: function(stepName, name, value, defaultValue, price, type, page, input, chain, altImagePath)
	{
		this.stepName = stepName;
		this.name = name;
		this.value = value;
		this.defaultValue = defaultValue;
		this.price = P4.Util.parseCurrency(price);
		this.type = type;
		this.page = page;
		this.input = input;
		this.chain = chain;
		if (altImagePath)
		{
			this.altImage = new Image();
			this.altImage.src = altImagePath;
		}
	},

	toString: function()
	{
		return this.name;
	},

	isSelected: function()
	{
		switch (this.type)
				{
			case 'IMMUTABLE':
				return true;
			case 'QUANTITY':
				this.value = $(this.input).value;
				if (this.value == null || this.value == 0 || this.value == '' || isNaN(new Number(this.value)))
				{
					this.value = '';
					return false;
				}
				else
					return true;
			case 'BOOLEAN':
				var input = $(this.input);
				if (input.type.toLowerCase() == 'select-one')
					return input.value == this.chain;
				else
					return input.checked;
			case 'VALUE':
				this.value = $(this.input).value;
				return this.value && this.value.trim().length > 0;
		}
	},

	isEnabled: function()
	{
		return !$(this.input).hasAttribute('disabled');
	},

	enable: function(reason)
	{
		var input = $(this.input);
		if (input)
		{
			if (input.hasAttribute('disabled'))
			{
				input.removeAttribute('disabled');
				var tooltipped = input.parentNode.nodeName.toLowerCase() == 'label' ? input.parentNode : input;
				Tooltip.remove(tooltipped);
				Element.removeClassName(tooltipped.parentNode, 'required-by-rule');
				Element.removeClassName(tooltipped.parentNode, 'prohibited-by-rule');
				if (this.stateBeforeDisabled)
				{
					input.value = this.stateBeforeDisabled.value;
					input.checked = this.stateBeforeDisabled.checked;
					this.stateBeforeDisabled = null;
				}
				return true;
			}
		}
		return false;
	},

	require: function(reason)
	{
		if (this.type == 'BOOLEAN')
		{
			var input = $(this.input);
			if (input && !this.stateBeforeDisabled)
			{
				this.stateBeforeDisabled = { value: input.value, checked: input.checked };
				if (input.type.toLowerCase() == 'select-one')
					input.value = this.chain;
				else
					input.checked = true;
				input.disabled = 'disabled';
				var tooltipped = input.parentNode.nodeName.toLowerCase() == 'label' ? input.parentNode : input;
				Tooltip.set(tooltipped, reason);
				Element.addClassName(tooltipped.parentNode, 'required-by-rule');
			}
		}
	},

	prohibit: function(reason)
	{
		var input = $(this.input);
		if (input && !this.stateBeforeDisabled)
		{
			this.stateBeforeDisabled = { value: input.value, checked: input.checked };
			switch (this.type)
					{
				case 'IMMUTABLE':
					return;
				case 'BOOLEAN':
					if (input.type.toLowerCase() == 'select-one')
						return; // can't disable because we don't know which other option to select
					else
						input.checked = false;
					break;
				case 'VALUE':
					this.value = '';
					input.value = this.value;
					break;
				case 'QUANTITY':
					this.value = '0';
					input.value = this.value;
					break;
			}
			input.disabled = 'disabled';
			var tooltipped = input.parentNode.nodeName.toLowerCase() == 'label' ? input.parentNode : input;
			Tooltip.set(tooltipped, reason);
			Element.addClassName(tooltipped.parentNode, 'prohibited-by-rule');
		}
	},

	getValue: function()
	{
		var input = $(this.input);
		var value = input ? input.value : this.value;
		if (!value && this.type == 'QUANTITY')
			value = '0';
		return value;
	},

	getPrice: function()
	{
		if (this.type == 'QUANTITY')
		{
			var value = this.getValue();
			if (!value || isNaN(value))
				value = 0;
			return (value - this.defaultValue) * this.price;
		}
		else
			return this.price;
	},

	allInStep: function()
	{
		return Configurator.onPage.findAll(function(option)
		{
			return option.stepName == this.stepName;
		}.bind(this));
	},

	toDom: function(previousStepName)
	{
		var elements = [];
		if (this.stepName != previousStepName)
		{
			var stepRow = document.createElement('tr');
			var th = document.createElement('th');
			stepRow.appendChild(th);
			th.setAttribute('colspan', '2');
			var stepNameContainer = th;
			if (this.page != Configurator.page)
			{
				var a = document.createElement('a');
				th.appendChild(a);
				a.setAttribute('href', 'javascript:Configurator.gotoPage(' + this.page + ');');
				stepNameContainer = a;
			}
			stepNameContainer.setAttribute('class', 'step-name');
			stepNameContainer.appendChild(document.createTextNode(this.stepName));
			elements.push(stepRow);
		}
		var optionRow = document.createElement('tr');
		var nameTd = document.createElement('td');
		optionRow.appendChild(nameTd);
		Element.addClassName(nameTd, 'name');
		nameTd.appendChild(document.createTextNode(this.name + (this.value ? ':' : '')));
		if (this.value)
		{
			var valueSpan = document.createElement('span');
			nameTd.appendChild(valueSpan);
			Element.addClassName(valueSpan, 'value');
			valueSpan.appendChild(document.createTextNode(this.value));
		}
		var priceTd = document.createElement('td');
		optionRow.appendChild(priceTd);
		Element.addClassName(priceTd, 'price');
		var price = this.getPrice();
		if (price == 0) Element.addClassName(priceTd, 'free');
		else if (price > 0) Element.addClassName(priceTd, 'positive');
		else Element.addClassName(priceTd, 'negative');
		priceTd.appendChild(document.createTextNode(P4.Util.formatNumber(price, 2, Configurator.fractionSeparator, Configurator.currencySymbol, true)));
		elements.push(optionRow);
		return elements;
	}
};
Configurator.Option.withChain = function(chain)
{
	return function(option)
	{
		return option.chain == chain;
	};
};

