var attributeManager = function(config) {
	return {
		variations: null,
		attributesConfig: null,
		usePricesWithVat: false,
		hierarchy: null,
		hideInvalidOptions: null,
		disableInvalidOptions: null,
		displayStockMinusReserved: null,
		outOfStockShowCanBePurchased: null,
		attributes: {},
		variationAttributesTable: [],
		setConfigData: function(config) {
			this.variations = config.variations;
			this.attributesConfig = config.attributesConfig;
			this.usePricesWithVat = config.usePricesWithVat;
			this.hierarchy = config.hierarchy;
			this.hideInvalidOptions = config.hideInvalidOptions;
			this.disableInvalidOptions = config.disableInvalidOptions;
			this.displayPrices = config.displayPrices;
			this.displayStockMinusReserved = config.displayStockMinusReserved,
			this.outOfStockShowCanBePurchased = config.outOfStockShowCanBePurchased,
			this.mainImage = config.mainImage;
		},
		getHash: function() {
			var str = document.location.hash;
			return str.substring(1, str.length); // Cut the '#'
		},
		getHistory: function() {
			if(!this.getHash().length){
				return null;
			}

			var urlInfo = this.getHash().split('=');
			return (1 < urlInfo.length) ? decodeURIComponent(urlInfo[1]) : null;
		},
		setHistory: function() {
			var variationIndex = this.getActiveVariation();
			if (null == variationIndex) {
				return;
			}

			document.location.hash = 'variation=' + encodeURIComponent(this.variations[variationIndex]['id']);
		},
		getActiveVariation: function() {
			var variationIndex = null;
			for (var i = 0; i < this.variationAttributesTable.length; i++) {
				variationIndex = i;
				var foundVariation = true;
				for (var attributeId in this.attributes) {
					if (this.variationAttributesTable[i][attributeId] != this.attributes[attributeId].getSelectedOption().value) {
						foundVariation = false;
						variationIndex = null;
						break
					}
				}

				if (foundVariation) {
					break;
				}
			}

			return variationIndex;
		},
		init: function(config) {
			this.setConfigData(config);

			for (var i = 0; i < this.attributesConfig.length; i++) {
				var newAttribute = new attributeManager.Attribute(this.attributesConfig[i]);
				this.attributes[newAttribute.id] = newAttribute;
			}

			var historyVariationId = this.getHistory();
			var historyVariationIndex = null;
			for (var i = 0; i < this.variations.length; i++) {
				this.variationAttributesTable[i] = {};

				for (var j = 0; j < this.variations[i]['ProductsAttributevalue'].length; j++) {
					var attributeId = this.variations[i]['ProductsAttributevalue'][j]['attribute_id'];
					var attributeOptionValue = this.variations[i]['ProductsAttributevalue'][j]['value'];

					this.variationAttributesTable[i][attributeId] = attributeOptionValue;
				}

				if (historyVariationId && (historyVariationId == this.variations[i]['id'])) {
					historyVariationIndex = i;
				}
			}

			// Set variation from history as selected one
			if (historyVariationIndex) {
				for (var attributeId in this.attributes) {
					this.attributes[attributeId].setSelectedOption(this.variationAttributesTable[historyVariationIndex][attributeId]);
				}
			}

			if (0 < this.attributesConfig.length) {
				this.onSelectionChange(this.attributesConfig[0]['id'], true);
			}
		},
		updateProductData: function(previousVariationId) {
			var submitFormButton = $('submit_form_button');
			var hiddenFieldVariationId = $('hidden_field_variation_id');
			var productItemId = $('product_item_id');
			var productPrice = $('product_price');
			var stockDetail = $('stock_detail');
			var buyButton = $('div_submit_buy_button');
		
			var activeVariationIndex = this.getActiveVariation();

			if (null == activeVariationIndex) {
				if (submitFormButton) {
					submitFormButton.disabled = true;
				}
				productItemId.innerHTML = '-';
				productPrice.innerHTML = '-';
				if (null != stockDetail) {
					stockDetail.innerHTML = '-';
				}
				hiddenFieldVariationId.value = -1;
				buyButton.hide();
				return;
			} else {
				// change submit_form_button value to selected variation id
				hiddenFieldVariationId.value = this.variations[activeVariationIndex]['id'];
			}

			productItemId.innerHTML = this.variations[activeVariationIndex]['Webshopitem']['serial_number'];
			productPrice.innerHTML = this.variations[activeVariationIndex]['Webshopitem']['display_price'];
			if (null != stockDetail) {
				stockDetail.innerHTML = this.variations[activeVariationIndex]['Webshopitem']['variation_stock_state'];
			}
			var resultingStockAmount = this.displayStockMinusReserved ? (this.variations[activeVariationIndex]['Webshopitem']['in_stock'] - this.variations[activeVariationIndex]['Webshopitem']['stock_reserved']) : this.variations[activeVariationIndex]['Webshopitem']['in_stock']; 
			if (this.outOfStockShowCanBePurchased || ('REMOTE' == this.variations[activeVariationIndex]['Webshopitem']['stock_type']) || (0 < resultingStockAmount)) {
				buyButton.show();
			} else {
				buyButton.hide();
			}

			var newMainImage = false;
			if (this.variations[activeVariationIndex]['Images']) {
				newMainImage = this.variations[activeVariationIndex]['Images'];
			} else if (this.mainImage) {
				newMainImage = this.mainImage;
			}

			if (newMainImage) {
				$('main_product_image_link').title =  newMainImage['name'];
				$('main_product_image_link').href = newMainImage['url'];
				$('main_product_image').alt =  newMainImage['name'];
				$('main_product_image').src = newMainImage['main_src'];

				$('product_view_large_image').href = newMainImage['url'];

				previousVariationId = ((false === previousVariationId) && (0 != activeVariationIndex)) ? this.variations[0]['id'] : previousVariationId;
				$('product_image_link_' + newMainImage['id']).style.display = 'none';
				for (var i in this.variations) {
					if (this.variations[i]['id'] == previousVariationId) {
						var previousImageId = this.variations[i]['Images'] ? this.variations[i]['Images']['id'] : this.mainImage['id'];
						if (previousImageId != newMainImage['id']) {
							$('product_image_link_' + previousImageId).style.display = '';
						}
						break;
					}
				}
			}

			if (submitFormButton) {
				submitFormButton.disabled = false;
			}
		},
		onSelectionChange: function(attributeId, dontReset) {
			var init = true;
			if ('undefined' == typeof(dontReset)) {
				dontReset = null != this.getActiveVariation();
				init = false;
			}

			if (!this.hierarchy) {
				var variationList = this.getAttributeVariationList(attributeId, this.attributes[attributeId].getSelectedOption().value);
				this.selectValidVariation(variationList);
			}

			var disableWholeAttribute = false;
			var resetSelectedOption = false;
			for (var currentAttributeId in this.attributes) {
				this.repaintAttribute(currentAttributeId, dontReset);

				if (!this.hierarchy || dontReset) {
					continue;
				}

				if (disableWholeAttribute) {
					this.attributes[currentAttributeId].disable();
				} else {
					this.attributes[currentAttributeId].enable();
				}
				if (resetSelectedOption) {
					this.attributes[currentAttributeId].setSelectedOption(0);
				}

				if (0 == this.attributes[currentAttributeId].getSelectedOption().value) {
					disableWholeAttribute = true;
					resetSelectedOption = true;
				}
				if (!resetSelectedOption && (attributeId == currentAttributeId)) {
					resetSelectedOption = true;
				}
			}

			var previousVariationId = init ? false : this.getHistory();
			this.setHistory();
			this.updateProductData(previousVariationId);
		},
		selectValidVariation: function(variationList) {
			var currentSelectionIsValid = true;

			for (var i = 0; i < variationList.length; i++) {
				currentSelectionIsValid = true;
				for (var attributeId in this.attributes) {
					if (variationList[i][attributeId] != this.attributes[attributeId].getSelectedOption().value) {
						currentSelectionIsValid = false;
						break
					}
				}

				if (currentSelectionIsValid) {
					break;
				}
			}

			if (currentSelectionIsValid) {
				return;
			}

			alert("You selected combination that is not supported, system will automaticaly find valid combination that match best with one you selected.");

			// Set first variation in a list as selected one
			for (var attributeId in this.attributes) {
				this.attributes[attributeId].setSelectedOption(variationList[0][attributeId]);
			}
		},
		getAttributeVariationList: function(attributeId, attributeValue) {
			var variationList = [];
			for (var i = 0; i < this.variationAttributesTable.length; i++) {
				if (attributeValue == this.variationAttributesTable[i][attributeId]) {
					variationList.push(this.variationAttributesTable[i]);
				}
			}

			return variationList;
		},
		repaintAttribute: function(attributeId, addPriceSuffix) {
			var validOptionsValues = {};

			for (var i = 0; i < this.variationAttributesTable.length; i++) {
				var variationIsValid = true;
				for (var currentAttributeId in this.attributes) {
					// Skip attribute for wich we are collecting valid options
					if (currentAttributeId == attributeId) {
						if (this.hierarchy) {
							break;
						} else {
							continue;
						}
					}

					// If this option is already found as valid skip checking
					if ('undefined' != typeof(validOptionsValues[this.variationAttributesTable[i][attributeId]])) {
						break;
					}

					var validAttributeOptionValue = this.attributes[currentAttributeId].getSelectedOption().value;
					if (validAttributeOptionValue != this.variationAttributesTable[i][currentAttributeId]) {
						variationIsValid = false;
						break;
					}
				}

				if (variationIsValid) {
					validOptionsValues[this.variationAttributesTable[i][attributeId]] = {priceChange: this.getAttributevaluePriceChange(attributeId, this.variationAttributesTable[i][attributeId])};
				}
			}

			this.attributes[attributeId].repaint(validOptionsValues, addPriceSuffix);
		},
		getAttributevaluePriceChange: function(attributeId, optionValue) {
			// Skip price change calculation for currently selected option
			if (optionValue == this.attributes[attributeId].getSelectedOption().value) {
				return null;
			}

			var activeVariationIndex = this.getActiveVariation();
			// If no previous variation is selected (valid) dont display price change
			if (null == activeVariationIndex) {
				return null;
			}

			var optionVariation = this.findVariationForAttributeOption(attributeId, optionValue);
			if (null == optionVariation) {
				return null;
			}

			var priceIndexToUse = this.usePricesWithVat ? 'price_with_tax' : 'price';
			var currentPrice = this.variations[activeVariationIndex]['Webshopitem'][priceIndexToUse];
			var optionVariationPrice = optionVariation['Webshopitem'][priceIndexToUse];

			return optionVariationPrice - currentPrice;
		},
		findVariationForAttributeOption: function(attributeId, optionValue) {
			var variationIndex = null;
			for (var i = 0; i < this.variationAttributesTable.length; i++) {
				// Skip all variations where option value for attribut id is not equal to target option value
				if (this.variationAttributesTable[i][attributeId] != optionValue) {
					continue;
				}

				variationIndex = i;
				var foundVariation = true;
				for (var currentAttributeId in this.attributes) {
					// Skip attribute for wich we do the search
					if (currentAttributeId == attributeId) {
						continue;
					}

					if (this.variationAttributesTable[i][currentAttributeId] != this.attributes[currentAttributeId].getSelectedOption().value) {
						foundVariation = false;
						variationIndex = null;
						break
					}
				}

				if (foundVariation) {
					break;
				}
			}

			return (null != variationIndex) ? this.variations[variationIndex] : null;
		}
	}
}();
attributeManager.Attribute = function(config) {
	var initOptions = function() {
		if ('undefined' == typeof(config['Attributevalue'])) {
			return [];
		}

		var selectedOption = null;
		var HtmlAttributeCombo = $('attribute_' + config['id']);
		if (HtmlAttributeCombo) {
			if ((0 <= HtmlAttributeCombo.selectedIndex) && (HtmlAttributeCombo.options.length > HtmlAttributeCombo.selectedIndex)) {
				selectedOption = HtmlAttributeCombo.options[HtmlAttributeCombo.selectedIndex];
			} else {
				selectedOption = HtmlAttributeCombo.options[0];
			}
		}

		var options = [];
		for (var i = 0; i < config['Attributevalue'].length; i++) {
			var newOption = new attributeManager.AttributeOption(config['Attributevalue'][i]);
			if (selectedOption && (selectedOption.value == newOption.value)) {
				newOption.selected = true;
			}

			options.push(newOption);
		}

		return options;
	};

	return {
		id: config['id'],
		options: initOptions(),
		invalidClass: 'invalid',
		attributeManager: attributeManager,
		getOption: function(value, returnHtmlElement) {
			if ('undefined' == returnHtmlElement) {
				returnHtmlElement = false;
			}

			var optionToReturn = null;
			for (var i = 0; i < this.options.length; i++) {
				if (value == this.options[i].value) {
					optionToReturn = this.options[i];
					break;
				}
			}

			if (optionToReturn && returnHtmlElement) {
				optionToReturn = this.getHtmlOptionElement(optionToReturn);
			}

			return optionToReturn;
		},
		getHtmlOptionElement: function(option) {
			return $('attributeOption_' + this.id + '_' + option.value);
		},
		getSelectedOption: function() {
			var selectedOption = null;
			var HtmlAttributeCombo = $('attribute_' + config['id']);
			if (HtmlAttributeCombo) {
				selectedOption = HtmlAttributeCombo.options[HtmlAttributeCombo.selectedIndex];
			}

			return this.getOption(selectedOption.value);
		},
		setSelectedOption: function(optionValueToSelect) {
			this.unselectAll();
			var optionToSelect = this.getOption(optionValueToSelect);
			optionToSelect.selected = true;
			var htmlOptionElement = this.getHtmlOptionElement(optionToSelect);
			if (htmlOptionElement) {
				htmlOptionElement.selected = true;
			}
		},
		unselectAll: function() {
			for (var i = 0; i < this.options.length; i++) {
				this.options[i].selected = false;
				var htmlOptionElement = this.getHtmlOptionElement(this.options[i]);
				if (htmlOptionElement) {
					htmlOptionElement.selected = false;
				}
			}
		},
		disable: function() {
			var attributeHtmlElement = $('attribute_' + this.id);
			if (attributeHtmlElement) {
				attributeHtmlElement.disabled = true;
			}
		},
		enable: function() {
			var attributeHtmlElement = $('attribute_' + this.id);
			if (attributeHtmlElement) {
				attributeHtmlElement.disabled = false;
			}
		},
		removeAllHtmlOptionsTags: function() {
			var attributeHtmlElement = $('attribute_' + this.id);
			if (!attributeHtmlElement) {
				return;
			}

			while (0 < attributeHtmlElement.options.length) {
				attributeHtmlElement.remove(attributeHtmlElement.options.length - 1);
			}
		},
		appendHtmlOption: function(option, priceChange) {
			if (this.getHtmlOptionElement(option)) {
				return;
			}

			var attributeHtmlElement = $('attribute_' + this.id);
			if (!attributeHtmlElement) {
				return;
			}

			var newOptionText = option.text;
			var newOptionTitle = false;
			if (0 != option.value) {
				if ((null == priceChange)) {
					newOptionText += ' *';
					newOptionTitle = "This option is unavaiable for current variation";
				} else if (this.attributeManager.displayPrices && (0 != priceChange)) {
					var formattedPrice = rawNumberFormat(
						priceChange,
						2,
						',',
						'.'
					)
					newOptionText += ' (' + ((0 > priceChange) ? '' : '+') + formattedPrice + ')';
				}
			}
			var newOption = new Option(newOptionText, option.value);
			newOption.id = 'attributeOption_' + this.id + '_' + option.value;
			if (newOptionTitle) {
				newOption.setAttribute('title', newOptionTitle);
			}

			attributeHtmlElement.options[attributeHtmlElement.options.length] = newOption;
//          newOption.selected = option.selected;
		},
		repaint: function(validOptions, addPriceSuffix) {
			// IE can't simply hide options inside select, so we have to remove all options from select and rebuild select again with walid options
			var selectedOption = this.getSelectedOption();
			if (attributeManager.hideInvalidOptions) {
				this.removeAllHtmlOptionsTags();
			}

			for (var i = 0; i < this.options.length; i++) {
				if ((0 == this.options[i].value) || ('undefined' != typeof(validOptions[this.options[i].value]))) {
					var priceChange = ('undefined' != typeof(validOptions[this.options[i].value])) ? validOptions[this.options[i].value].priceChange : null;

					// Reset price change for currently selected option
					priceChange = (addPriceSuffix && (selectedOption.value != this.options[i].value)) ? priceChange : 0;
					this.appendHtmlOption(this.options[i], priceChange);
					this.removeOptionClass(this.options[i], this.invalidClass);
				} else {
					this.addOptionClass(this.options[i], this.invalidClass);
				}
			}

			this.setSelectedOption(selectedOption.value);
		},
		addOptionClass: function(option, className) {
			var htmlElement = this.getHtmlOptionElement(option);
			if (!htmlElement) {
				return;
			}

			if(className && !htmlElement.hasClassName(className)) {
				htmlElement.addClassName(className);
			}

			/*if (attributeManager.hideInvalidOptions && (0 != option.value)) {
				htmlElement.hide();
				htmlElement.style.visibility = 'hidden';
			}*/

			if (attributeManager.disableInvalidOptions && (0 != option.value)) {
				htmlElement.disabled = true;
			}
		},
		removeOptionClass: function(option, className) {
			var htmlElement = this.getHtmlOptionElement(option);
			if (!htmlElement || !className || !htmlElement.className) {
				return;
			}

			if(htmlElement.hasClassName(className)) {
				htmlElement.removeClassName(className);

				/*if (attributeManager.hideInvalidOptions) {
					htmlElement.show();
					htmlElement.style.visibility = 'visible';
				}*/

				if (attributeManager.disableInvalidOptions) {
					htmlElement.disabled = false;
				}
			}
		}
	}
};
attributeManager.AttributeOption = function(config) {
	config.selected = ('undefined' == typeof(config.selected)) ? false : config.selected;
	return {
		text: config['name'],
		value: config['id'],
		selected: config.selected
	}
};

rawNumberFormat = function(number, decimals, dec_point, thousands_sep) {
	// http://kevin.vanzonneveld.net
	// +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
	// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +     bugfix by: Michael White (http://getsprink.com)
	// +     bugfix by: Benjamin Lupton
	// +     bugfix by: Allan Jensen (http://www.winternet.no)
	// +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
	// +     bugfix by: Howard Yeend
	// *     example 1: number_format(1234.5678, 2, '.', '');
	// *     returns 1: 1234.57

	var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
	var d = dec_point == undefined ? '.' : dec_point;
	var t = thousands_sep == undefined ? ',' : thousands_sep, s = n < 0 ? '-' : '';
	var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + '', j = (j = i.length) > 3 ? j % 3 : 0;

	return s + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');
}
