function ProductSelector()
{
	var self = this;
	var idItr = 0;
	this.id;
	this.selectors = {};
	this.initing = true;
	var splitChars = '_._';
	var oldSelectorState = {
		state :'init'
	};

	this.printTraceLbl = null;
	this.printTrace = function(message, append)
	{
		if (!this.printTraceLbl)
			return;
		if (append)
		{
			this.printTraceLbl.innerHTML += message;
		}
		else
			this.printTraceLbl.innerHTML = '<br /><br />' + message;
	};

	function getNextId()
	{
		return self.container.id + '_' + (idItr++);
	}

	function selectItem(selectorItem, unsetCurrent)
	{
		var selector = self.selectors[selectorItem.propertySelected];
		if (unsetCurrent)
		{
			var oldItem = selector.selectedItem;
			selector.selectedItem = selectorItem;
			if (oldItem)
			{
				deselectItem(oldItem, false);
			}

		}
		selectorItem.element.className = selector.options.selectedItemClass;
	}

	function deselectItem(selectorItem, unsetCurrent)
	{
		var selector = self.selectors[selectorItem.propertySelected];
		if (!selector.selectedItem
		        || (selectorItem.element.id != selector.selectedItem.element.id))
		{
			selectorItem.element.className = selector.options.itemClass;
		}
		if (unsetCurrent && selector.selectedItem)
			selector.selectedItem = null;
	}

	function getElementFromItem(selectorItem)
	{
		if (!selectorItem.element)
			selectorItem.element =
			        document.getElementById(selectorItem.elementId);
		return selectorItem.element;
	}

	function getSelectorItemFromIdStr(selectorAndItemStr)
	{
		var splitArgs = selectorAndItemStr.split(splitChars);
		var reprocess_name = splitArgs[1].replace(/##/g, "'");
		// GEH BUG processing see other BUG GEH note
		var selectorItem = self.selectors[splitArgs[0]].items[reprocess_name];
		selectorItem.element = getElementFromItem(selectorItem);
		return selectorItem;
	}

	this.OnMouseOver = function(selectorAndItemStr)
	{
		try
		{
			if (this.hovering)
				return;
			this.hovering = true;
			var selectorItem = getSelectorItemFromIdStr(selectorAndItemStr);
			MouseOver(null, selectorItem);
		}
		catch (ex)
		{
		}
	};
	this.OnMouseOut = function(selectorAndItemStr)
	{
		try
		{
			var selectorItem = getSelectorItemFromIdStr(selectorAndItemStr);
			MouseOut(null, selectorItem);
			this.hovering = false;
		}
		catch (ex)
		{
		}
	};
	this.OnClick = function(selectorAndItemStr)
	{
		try
		{
			var selectorItem = getSelectorItemFromIdStr(selectorAndItemStr);
			Click(null, selectorItem);
		}
		catch (ex)
		{
		}
	};

	function MouseOver(event, selectorItem)
	{
		TimerQueue.addTimer('::MouseOverStart');
		TimerQueue.addTimer('::SelectItem');
		selectItem(selectorItem);
		TimerQueue.dequeueTimer();
		showValidSkus(selectorItem);
		var selector = self.selectors[selectorItem.propertySelected];
		selector.selectionText.innerHTML = selectorItem.value;

		if (self._handleMouseOverEvent)
			self._handleMouseOverEvent(selectorItem);
		self.printTrace(TimerQueue.getAllMessage());
	}

	function MouseOut(event, selectorItem)
	{
		TimerQueue.addTimer('::HandleMouseOut');
		deselectItem(selectorItem, false);

		if (oldSelectorState.state == 'saved')
		{
			for ( var i = 0; i < oldSelectorState.items.length; i++)
			{
				var savedStateItem = oldSelectorState.items[i];
				if (savedStateItem && i < oldSelectorState.items.length
				        && (oldSelectorState.state == 'saved'))
				{
					ProtoLite.Element.setOpacity(savedStateItem.item.element,
					        savedStateItem.opacity);
					savedStateItem.item.valid = (savedStateItem.opacity > .5);
					savedStateItem.item.element.style.borderStyle =
					        (savedStateItem.item.valid) ? 'solid' : 'dotted';
				}
			}

			for ( var property in oldSelectorState.itemsText)
			{
				oldSelectorState.itemsText[property].element.innerHTML =
				        oldSelectorState.itemsText[property].text;
			}

			if (oldSelectorState.selectedSkuItem)
				self.SelectedSkuItem = oldSelectorState.selectedSkuItem;
			self.CurrentState = oldSelectorState.CurrentState;

			oldSelectorState = {
				state :'reverted'
			};

			if (self._revertSavedState)
				self._revertSavedState();

			var selectedSku = {};
			var validCombo = true;
			for ( var property in self.selectors)
			{
				var selector = self.selectors[property];
				if (selector.selectedItem)
				{
					selectedSku[selector.selectedItem.propertySelected] =
					        selector.selectedItem.value;
					if (!selector.selectedItem.valid)
						validCombo = false;
				}
			}

			if (validCombo && self._handleValidCombo)
				self._handleValidCombo(self.SelectedSkuItem);
			if (!validCombo && self._handleNonValidCombos)
				self._handleNonValidCombos(selectedSku);
		}
		TimerQueue.dequeueTimer();
		self.printTrace(TimerQueue.getAllMessage(), true);
	}

	function Click(event, selectorItem)
	{
		var selector = self.selectors[selectorItem.propertySelected];
		self.blockOOSMessageChange = false;
		if (selector.selectedItem
		        && (selectorItem.element.id == selector.selectedItem.element.id))
		{
			selector.selectedItem = null;
			selector.selectionText.innerHTML = '&nbsp';
			deselectItem(selectorItem, true);
			showValidSkus(null);
			oldSelectorState = {
				state :'reverted'
			};
		}
		else
		{
			selectItem(selectorItem, true);
			selector = self.selectors[selectorItem.propertySelected];
			selector.selectionText.innerHTML = selector.selectedItem.value;
			showValidSkus(selectorItem);

			if (self._selectItemEvent)
				self._selectItemEvent(selectorItem);
		}

		if (self._handleSelection)
		{
			var selectedSkuItem =
			        (self.SelectedSkuItem) ? self.SelectedSkuItem : null;
			self._handleSelection(selectedSkuItem);
		}
	}

	this.getDefaultSelectedSku = function()
	{
		return {};
	};

	this.SelectValidSkus = function()
	{
		showValidSkus(null);
	};

	function showValidSkus(selectorItem)
	{
		TimerQueue.addTimer('::showValidSkus');
		TimerQueue.addTimer('::objectBuild');
		oldSelectorState.state = 'saving';
		oldSelectorState.selectedSkuItem = self.SelectedSkuItem;
		oldSelectorState.CurrentState = self.CurrentState;
		oldSelectorState.itemsText = {};
		oldSelectorState.items = [];
		var validSkuItem = true;

		var selectedSku = self.getDefaultSelectedSku();
		if (selectorItem && selectorItem.subSelector)
			selectedSku[selectorItem.subSelector.property] =
			        selectorItem.subSelector.value;

		if (selectorItem && selectorItem.secondaryValue)
		{
			selectedSku[selectorItem.secondaryValueName] =
			        selectorItem.secondaryValue;
		}

		if (selectorItem && selectorItem.propertySelected)
		{
			selectedSku[selectorItem.propertySelected] = selectorItem.value;
			if (!selectorItem.valid)
			{
				validSkuItem = false;
			}
		}
		else
			selectorItem = {
				propertySelected :null
			};
		for ( var property in self.selectors)
		{
			if (selectorItem.propertySelected != property)
			{
				var selector = self.selectors[property];
				if (selector.selectedItem)
				{
					selectedSku[selector.selectedItem.propertySelected] =
					        selector.selectedItem.value;

					if (selector.options.secondaryValue)
					{
						selectedSku[selector.options.secondaryValue] =
						        selector.selectedItem.secondaryValue;
					}
				}

			}
		}

		TimerQueue.dequeueTimer();
		var noMatch = false;
		TimerQueue.addTimer('::setItems');
		// this looks like brute force, but its really not as bas as it seems
		for ( var property in self.selectors)
		{
			var selector = self.selectors[property];
			oldSelectorState.itemsText[property] = {
			    element :selector.selectionText,
			    text :selector.selectionText.innerHTML
			};
			if (selectorItem.propertySelected != property)
			{
				for ( var itr in selector.items)
				{
					var item = selector.items[itr];
					item.element = getElementFromItem(item);
					oldSelectorState.items.push( {
					    opacity :ProtoLite.Element.getOpacity(item.element),
					    item :item
					});

					var toCompare = ProtoLite.Object.Clone(selectedSku);
					toCompare[item.propertySelected] = item.value;
					if (item.secondaryValueName)
					{
						toCompare[item.secondaryValueName] =
						        item.secondaryValue;
					}

					TimerQueue.addTimer('::innerMatch');
					var matchingItems = self.FindMatchingItems(toCompare, true);
					TimerQueue.dequeueTimer();
					if (matchingItems.length == 1)
					{
						if (!item.valid)
						{
							item.valid = true;
							ProtoLite.Element.setOpacity(item.element, 1);
							item.element.style.borderStyle = 'solid';
						}
					}
					else
					{
						if (item.valid)
						{
							item.valid = false;

							ProtoLite.Element.setOpacity(item.element, .3);
							item.element.style.borderStyle = 'dotted';
						}
						if (selector.selectedItem
						        && (item.element.id == selector.selectedItem.element.id))
						{
							validSkuItem = false;
						}
					}
				}
			}
		}
		TimerQueue.dequeueTimer();

		TimerQueue.addTimer('::mediator');
		self.SelectedSkuItem = null;
		if (validSkuItem)
		{
			var matchedItems = self.FindMatchingItems(selectedSku, false);
			if (matchedItems.length == 1)
			{
				self.CurrentState = 'ValidSelection';
				self.SelectedSkuItem = matchedItems[0];
				if (self._handleValidCombo)
					self._handleValidCombo(self.SelectedSkuItem);
			}
			else
			{
				self.CurrentState = 'MultipleSelection';
				if (self._handleValidCombo)
					self._handleValidCombo(selectedSku)
			}
		}
		else
		{
			self.CurrentState = 'NonValidSelection';
			if (self._handleNonValidCombos)
				self._handleNonValidCombos(selectedSku);
		}

		oldSelectorState.state = 'saved';
		TimerQueue.dequeueTimer();
		TimerQueue.dequeueTimer();
	}

	this.FindMatchingItems = function(toCompare, firstOne)
	{
		return MatchItemsInArray(this.jsonData.sku_data, toCompare, firstOne);
	};

	this.FindMatchingOOSItems =
	        function(toCompare, firstOne)
	        {
		        return MatchItemsInArray(this.jsonData.sku_data_oos, toCompare,
		                firstOne);
	        };

	function MatchItemsInArray(arr, toCompare, firstOne)
	{
		var items = [];
		for ( var i = 0; i < arr.length; i++)
		{
			var skuItem = arr[i];
			var equiv = true;
			for ( var prop in toCompare)
			{
				if (skuItem[prop] != toCompare[prop])
				{
					equiv = false;
					break;
				}
			}

			if (equiv)
			{
				items.push(skuItem);
				if (firstOne)
					return items;
			}
		}

		return items;
	}

	this.turnSelectorOff =
	        function(selectorToDisable)
	        {
		        for ( var property in self.selectors)
		        {
			        if (property == selectorToDisable)
				        self.selectors[property].selectorContainer.style.display =
				                'none';
		        }
	        };

	this.hidePrompts = function()
	{
		for ( var property in self.selectors)
		{
			var selector = self.selectors[property];
			selector.rtSelect.style.display = 'none';
		}
	};

	this.promptSelections = function()
	{
		var returnArray = [];
		for ( var property in self.selectors)
		{
			var selector = self.selectors[property];
			if (!selector.selectedItem && selector.display == 'block')
			{
				selector.rtSelect.style.display = 'block';
				returnArray.push(selector.displayText);
			}
		}

		return returnArray;
	};

	this.functionAddSelector =
	        function(property, displayText, options)
	        {
		        TimerQueue.addTimer('::startRender-' + property);
		        var selectorElement = {};
		        var defaultSelector = '_XXdefault';

		        selectorElement.options = ProtoLite.Object.Extend( {
		            itemClass :'selectItem',
		            selectedItemClass :'selectedItem',
		            innerHTMLProp :property,
		            equalWidth :true,
		            subSelectors : [
			            {}
		            ],
		            removeSingle :true,
		            hideSelector :false,
		            titleFormater : function(optionText)
		            {
			            return 'Select a ' + optionText + ': ';
		            }
		        }, options || {});

		        if (selectorElement.options.hideSelector)
		        {
			        var hiddenToggleId = this.id + "_hiddenToggle_" + property;
			        selectorElement.selectorUnhider =
			                document.createElement('div');
			        selectorElement.selectorUnhider.innerHTML =
			                "<div onmouseover='GetElement(\""
			                        + hiddenToggleId
			                        + "\").style.display = \"inline\"' onmouseout='GetElement(\""
			                        + hiddenToggleId
			                        + "\").style.display = \"none\"' "
			                        + "onclick=\"InstanceContainer.FireEvent('"
			                        + this.id + "', 'UnhideSelector', '"
			                        + property + "')\">[ + ] " + "<span id='"
			                        + hiddenToggleId
			                        + "' style='display:none'>Show "
			                        + displayText + "</span></div><br />";
			        this.container.appendChild(selectorElement.selectorUnhider);
		        }

		        selectorElement.selectorContainer =
		                document.createElement('div');
		        selectorElement.selectorContainer.id = getNextId();
		        selectorElement.selectorContainer.className =
		                'selectorContainer';
		        this.container.appendChild(selectorElement.selectorContainer);
		        if (selectorElement.options.hideSelector)
			        selectorElement.selectorContainer.style.display = 'none';

		        selectorElement.rtSelect = document.createElement('div');
		        selectorElement.rtSelect.className = 'selectRtArrow';
		        selectorElement.rtSelect.style.display = 'none';
		        selectorElement.rtSelect.innerHTML = '&nbsp;&nbsp;select';
		        selectorElement.selectorContainer
		                .appendChild(selectorElement.rtSelect);

		        selectorElement.selectorTitleDiv =
		                document.createElement('div');
		        selectorElement.selectorTitleDiv.className = 'selectorTitle';
		        selectorElement.displayText = displayText;
		        selectorElement.selectorContainer
		                .appendChild(selectorElement.selectorTitleDiv);
		        selectorElement.selectorTitleDivText =
		                document.createElement('span');
		        selectorElement.selectorTitleDiv
		                .appendChild(selectorElement.selectorTitleDivText);
		        selectorElement.selectorTitleDivText.innerHTML =
		                selectorElement.options.titleFormater(displayText);

		        selectorElement.selectionText = document.createElement('span');
		        selectorElement.selectionText.className = 'selectorText';
		        selectorElement.selectionText.innerHTML = ' &nbsp ';
		        selectorElement.selectorTitleDiv
		                .appendChild(selectorElement.selectionText);

		        selectorElement.elementsDivs = {};
		        var itr = 0;

		        for ( var i = 0; i < selectorElement.options.subSelectors.length; i++)
		        {
			        var subSelector = selectorElement.options.subSelectors[i];
			        var elementsDivId = defaultSelector;
			        var elem = document.createElement('div');

			        var subTitlesObj = {};
			        if (subSelector.property)
			        {
				        var subTitle = document.createElement('div');
				        subTitle.className = 'subSelectorTitle';
				        if (!subSelector.displayText)
				        {
					        subTitle.style.display = 'none';
				        }
				        else
				        {
					        subTitle.innerHTML = subSelector.displayText;
				        }
				        selectorElement.selectorContainer.appendChild(subTitle);
				        elementsDivId = subSelector.property + '_' + itr;
				        subTitlesObj.subTitle = subTitle;
			        }
			        selectorElement.elementsDivs[elementsDivId] = {
			            itemContainer :elem,
			            HTMLStr :'',
			            numElements :0,
			            subTitlesObj :subTitlesObj
			        };

			        elem.className = 'selectorElements';
			        selectorElement.selectorContainer.appendChild(elem);

			        if (subSelector.property)
			        {
				        if (itr < (selectorElement.options.subSelectors.length - 1))
				        {
					        var brElem = document.createElement('br');
					        brElem.style.clear = 'both';
					        selectorElement.selectorContainer
					                .appendChild(brElem);
					        subTitlesObj.br = brElem;
				        }
				        itr++;
			        }
		        }

		        selectorElement.items = {};
		        var largestWidth = 0;
		        var elementsDivId = defaultSelector;
		        var noItems = true;

		        for ( var skutItemItr = 0; skutItemItr < this.jsonData.sku_data.length; skutItemItr++)
		        {
			        var skuItem = this.jsonData.sku_data[skutItemItr];
			        for ( var i = 0; i < selectorElement.options.subSelectors.length; i++)
			        {
				        var itemId = property + '_' + skuItem[property];
				        var subSelector =
				                selectorElement.options.subSelectors[i];
				        if (subSelector.property)
				        {
					        if (skuItem[subSelector.property] == subSelector.value)
					        {
						        elementsDivId = subSelector.property + "_" + i;
						        itemId +=
						                '_' + subSelector.property + "_"
						                        + subSelector.value;
					        }
					        else
						        continue;
				        }
				        else
					        subSelector = null;

				        if (!selectorElement.items[itemId] && skuItem[property])
				        {
					        var selectItemId = getNextId();
					        // push new item
					        selectorElement.items[itemId] = {
					            propertySelected :property,
					            value :skuItem[property],
					            exampleSku :skuItem,
					            valid :true,
					            elementId :selectItemId,
					            subSelector :subSelector
					        };

					        if (selectorElement.options.secondaryValue)
					        {
						        selectorElement.items[itemId].secondaryValue =
						                skuItem[selectorElement.options.secondaryValue];
						        selectorElement.items[itemId].secondaryValueName =
						                selectorElement.options.secondaryValue;
					        }

					        // GEH BUG Rework of naming mechanism
					        var unProcessed_selectorAndItemStr =
					                property + splitChars + itemId;
					        var selectorAndItemStr =
					                unProcessed_selectorAndItemStr.replace(
					                        /'/g, "##");

					        var selectItemHTML =
					                "<div id='"
					                        + selectItemId
					                        + "' "
					                        + "onmouseover=\"InstanceContainer.FireEvent('"
					                        + this.id
					                        + "', 'OnMouseOver', '"
					                        + selectorAndItemStr
					                        + "')\""
					                        + "onmouseout=\"InstanceContainer.FireEvent('"
					                        + this.id
					                        + "', 'OnMouseOut', '"
					                        + selectorAndItemStr
					                        + "')\""
					                        + "onclick=\"InstanceContainer.FireEvent('"
					                        + this.id
					                        + "', 'OnClick', '"
					                        + selectorAndItemStr
					                        + "')\""
					                        + "class='"
					                        + selectorElement.options.itemClass
					                        + "'>"
					                        + skuItem[selectorElement.options.innerHTMLProp]
					                        + "</div><span> </span>";
					        selectorElement.elementsDivs[elementsDivId].HTMLStr +=
					                selectItemHTML;
					        selectorElement.elementsDivs[elementsDivId].numElements++;
					        noItems = false;
				        }
			        }
		        }

		        selectorElement.selectorContainer.style.display = 'block';
		        if (noItems
		                || (!selectorElement.options.removeSingle && selectorElement.elementsDivs[elementsDivId].numElements == 1))
		        {
			        selectorElement.selectorContainer.style.display = 'none';
		        }
		        selectorElement.display =
		                selectorElement.selectorContainer.style.display;

		        var brElem = document.createElement('br');
		        brElem.style.clear = 'both';
		        for ( var selectorDiv in selectorElement.elementsDivs)
		        {
			        if (selectorElement.elementsDivs[selectorDiv].numElements == 0)
			        {
				        selectorElement.elementsDivs[selectorDiv].itemContainer.style.display =
				                'none';
				        if (selectorElement.elementsDivs[selectorDiv].subTitlesObj.br)
					        selectorElement.elementsDivs[selectorDiv].subTitlesObj.br.style.display =
					                'none';
				        if (selectorElement.elementsDivs[selectorDiv].subTitlesObj.subTitle)
					        selectorElement.elementsDivs[selectorDiv].subTitlesObj.subTitle.style.display =
					                'none';
			        }
			        else
			        {
				        selectorElement.elementsDivs[selectorDiv].itemContainer.innerHTML =
				                selectorElement.elementsDivs[selectorDiv].HTMLStr;
				        if (selectorElement.options.onAppendElementToItems)
					        selectorElement.options
					                .onAppendElementToItems(selectorElement.elementsDivs[selectorDiv].itemContainer);
				        selectorElement.elementsDivs[selectorDiv].itemContainer
				                .appendChild(brElem);
			        }
		        }
		        if (selectorElement.options.equalWidth)
		        {
			        for ( var itemId in selectorElement.items)
			        {
				        if (getElementFromItem(selectorElement.items[itemId]).clientWidth > largestWidth)
					        largestWidth =
					                selectorElement.items[itemId].element.clientWidth;
			        }
			        for ( var itemId in selectorElement.items)
			        {
				        selectorElement.items[itemId].element.style.width =
				                largestWidth + 'px';
			        }
		        }
		        this.selectors[property] = selectorElement;

		        if (selectorElement.options.hideSelector)
		        {
			        selectorElement.selectorContainer.style.display = 'none';
		        }
		        TimerQueue.dequeueTimer();

		        return selectorElement;
	        };

	this.UnhideSelector =
	        function(selectorProperty)
	        {
		        this.selectors[selectorProperty].selectorContainer.style.display =
		                'block';
		        this.selectors[selectorProperty].selectorUnhider.style.display =
		                'none';
	        };

	this.SetData = function(element, jsonData)
	{
		this.container = GetElement(element);
		this.jsonData = jsonData;
	};
}
