// namespace for ProductFilter
Dennis = window.Dennis || {};



var Filter = Dennis.Filters || {};

Filter.Class = function()
{
	return {
		getFilterBandById : function(bandId) {
			
			var band, bands;
			try {
				bands = this.getBands();
				
			for (band = 0; band < bands.length; band++) {
				if (bands.hasOwnProperty(band)) {
					if (bands[band].getId() == bandId) {
						//alert(bandId);
						return bands[band];
					}
				}
			}
			} catch (e) {
				alert(e.message + '42');
			}
			return false;
		},
		
		getType		: function()
		{
			return 'Filter';
		}

	};
};

Filter.factory = function (id, label, bands, filter)
{	
	var _filter	= filter || new Filter.Class;
	var _id 	= id;
	var _bands 	= bands || new Array();
	var _label  = label;
	
	_filter 	= Dennis.addListenEvent(_filter);
	
	
	_filter.on('bandChange', 'tellListeners');
		
	var getId = function()
	{
		return _id;
	};

	
	
	var tellListeners = function()
	{
		var listeners = this.getListeners();
		//alert('i be firing');
		for (listener in listeners) {
			listeners[listener].filterChange(this);
			//listeners[listener].fire('filterChange');
		}
	};
	
	
	var setBandActive = function(bandId)
	{
		if (band = this.getBandById(bandId)) {
			return band.setActive(true);
		}
		return false;
	};
	
	var setBandInactive = function(bandId) 
	{
		if (band = this.getBandById(bandId)) {
			return band.setActive(false);
		}
		return false;
	};	
	
	var getActiveBands = function()
	{
		var i, bands, activeBands = new Array();
		if (bands = this.getBands()) {
			for (i in bands) {
				if (bands.hasOwnProperty(i)) {
					if (bands[i].getActive()) {
						activeBands.push(bands[i]);
					}
				}
			}
		}
		return activeBands;
	};
	
	var getBands = function()
	{
		try {
			return _bands;
		} catch (e) {
			alert('96: ' + e.message);
		}
	};
	
	var setBands = function(bands, reset)
	{
		if (reset !== false) {
			_bands = new Array();
		}
		if ( (!bands) || ((bands.constructor !== Array) && (band.constructor !== Object)) ) {
        	return false;
    	} else {
			for (band in bands) {
				if (bands.hasOwnProperty(band)) {
					this.addBand(bands[band]);
				}
			}
			return true;
		}
	};
	
	
	var hasActiveBands = function()
	{
		return (this.getActiveBands().length > 0);
	};
	
	var addBand = function(band)
	{
		band.addListener(this);
		_bands.push(band);
	};
	
	var getLabel = function()
	{
		return _label;
	};
	
	//_filter.getBandById 		= getBandById;
	_filter.setBandActive		= setBandActive;
	_filter.getActiveBands		= getActiveBands;
	_filter.getBands			= getBands;
	_filter.setBands			= setBands;
	_filter.addBand				= addBand;
	_filter.tellListeners		= tellListeners;
	_filter.hasActiveBands		= hasActiveBands;
	
	_filter.getLabel			= getLabel;
	_filter.getId				= getId;
	return _filter;
};



var FilterSet  = Dennis.FilterSet || {};

FilterSet.Class = function(xmlRequestUri) {

	var _xmlRequestUri = xmlRequestUri || null;
	// the xml object for going off and getting teh stuff
	var _category;

	var _limit = 10, _offset = 0, _search = 'name', _sort = 'DESC';
	//alert(myOffset);
	return {
		
		/**
		 * Return the currently set request URI
		 */
		getRequestUri	: function()
		{
			return _xmlRequestUri;
		
		},
		/**
		 * Set the remote URI to request Reviews from
		 */
		setRequestUri	: function(uri)
		{
			_xmlRequestUri = uri;
		},
		
		/**
		 * Simple method so you know what object this is.  All Dennis objects should use this.
		 */
		getType	: function()
		{
			return 'FilterSet';
		},

		/**
		 * Set how many you wish to offset by (for pagination)
		 */
		setOffset		: function(offset)
		{
			_offset = offset;
		},

		/**
		 * Set how many you wish to offset by (for pagination)
		 */
		getOffset		: function()
		{
			return _offset;
		},
		
		/**
		 * How many reviews do you want back?  the final answer is _limit + selectedReviews
		 */
		setLimit		: function(limit)
		{
			_limit = limit;
		},
		
		getLimit		: function()
		{
			return _limit;
		},
		
		setSearch		: function(search)
		{
			_search = search;
		},
		
		getSearch		: function()
		{
			return _search;
		},
		
		setSort			: function(sort)
		{
			_sort	= sort;
		},
		
		getSort			: function()
		{
			return _sort;
		},
		
		
		/**
		 * Set the categoryId we are in.
		 */
		setCategory		: function(category)
		{
			_category = category;
		},
		
		
		/**
		 * Returns the arguments to send to the remote service
		 * Unless specifically stated, the arguments are returned as a single string.
		 */
		getRequestArgs	: function(asString)
		{
			var i, j, reviews, activeBands, limit, offset;
			var requestArgs = new Array();
			
			var filters = this.getFilters();
			
			var bands = new Array();
			
			for (i in filters) {
				if (filters[i].hasActiveBands()) {
					
					bands[i] = new Array();
					activeBands = filters[i].getActiveBands();
					for (j in activeBands) {
						//alert(activeBands[j].getId());
						bands[i].push(activeBands[j].getId());
					}
				}
			}
			
			reviews = this.getReviews();
			
			offset 	= _offset;
			limit	= _limit;
			for (i in reviews) {
				if (reviews[i].getSelected()) {
					limit+=1;
				}
			}
			requestArgs['filterset']	= this.getId();
			requestArgs['method']		= 'fetch';
			requestArgs['offset']		= offset;
			requestArgs['limit']		= limit;
			requestArgs['bands']		= bands;
			requestArgs['category']		= _category;
			requestArgs['sort']			= _sort;
			requestArgs['search']		= _search;
			
			if (asString !== false) {
				requestString = new Array();
				requestString.push('method=' + requestArgs['method']);
				requestString.push('offset=' + requestArgs['offset']);
				requestString.push('limit='  + requestArgs['limit']);
				requestString.push('category=' + requestArgs['category']);
				requestString.push('sort=' + requestArgs['sort']);
				requestString.push('filterset=' + requestArgs['filterset']);
				
				if (_search) {
					requestString.push('search=' + requestArgs['search']);
				}
				
				for (i in requestArgs['bands']) {
					
					for (j in requestArgs['bands'][i]) {
						bandString = 'bands[' + i + '][' + j + ']=' + requestArgs['bands'][i][j];
						requestString.push(bandString);
					}
					
				}
				requestArgs = requestString.join('&');				
			}

			return requestArgs;
		}
	};
};
/**
 * Factory for making Dennis.FilterSet.Class objects automatically
 */
FilterSet.factory = function(filterSet)
{
	var _filterSet = filterSet || new FilterSet.Class;
	
	_filterSet = Dennis.addListenEvent(_filterSet);

	_filterSet.on('reviewChange', 'xmlRefresh', new Array());
	 
	//_filterSet.on('filterChange', 'xmlRefresh', new Array());	
	
	
	var _id;
	
	var _filters = new Array();
	
	var _reviews = new Array();
	
	var _activeFilterId;
	
	var _total;
	
	
	var filterChange = function(filter)
	{
		_activeFilterId = filter.getId();
		this.xmlRefresh();
	};
	
	/**
	 * Return the id of the filterset
	 * 
	 * @return integer id
	 */
	var getId = function()
	{
		return _id;
	};
	
	var setId = function(id)
	{
		_id = id;
	};
	
	
	var getActiveFilterId = function()
	{
		return _activeFilterId;
	};
	
	var setActiveFilterId = function(activeFilterId)
	{
		_activeFilterId = activeFilterId;
	};
	
	/**
	 * Call the public xhr funciton (whether it be dojo or Jquery is
	 * irrelevant) giving it the filterSet as an object.
	 */
	var fetchRequest = function()
	{
		getRequest(this);
	};
	
	var getTotal = function()
	{
		return _total;
	};
	
	var _setTotal = function(total)
	{
		_total = total;
	};
	
	/**
	 * Refresh the XML feed, proxies to fetchRequest(), but is used by event handling
	 */
	var xmlRefresh = function()
	{
		//alert('I sensed a change');
		//alert(this.getActiveFilterId());
		this.fetchRequest();
	};
	
	/**
	 * Add a filter to the internal array _filters and listen to it.
	 * 
	 * @param Dennis.Filter.Class
	 */
	
	var addFilter = function(filter)
	{
		_filterListen(this, filter);
		_filters.push(filter);
	};
	
	/**
	 * Return all the registered filters
	 * 
	 * @return array
	 */
	var getFilters = function()
	{
		return _filters;
	};
	
	/**
	 * Public function to parse either JSON or XML and then populate the
	 * internal _reviews array with the results.
	 * 
	 * @param string|xmlDom Result from the AJAX fetch
	 */
	var xmlParse = function(response)
	{
		//alert("we're parsing nao!");
		var i, product, products, xml;
		xml = _fromXml(response);

		products = _parseProductsFromXml(xml);
		if (!products) {
			try {
				products = dojo.toJson(response);
				} catch (e) {
					//alert('first part (XML parse)' + e.message);
				}
		}
		try {
			this.setReviews(products);
			reviews = this.getReviews();
		} catch (e) {
				//alert('reviews (XML parse) '+e.message);
		}
		// now update the bands.
		_updateBandCounts(xml, this);

		_parseTotal(xml, this);
		this.update();
		//filterSetBuild(this);
		
	};
	
	/**
	 * Private function that takes the response text and attempts to create a 
	 * browser-specific xmlDom object.  It then parses through the XML DOM to 
	 * create a Review using Review.factory().  The entire array is then returned.
	 * @param text txt the responseText
	 * @return array of Dennis.Review.Class objects
	 */
	var _fromXml = function(txt)
	{

		var xmlDoc;
		
		//alert(txt);

		try //Internet Explorer
		{
		  xmlDoc		= new ActiveXObject("Microsoft.XMLDOM");
		  xmlDoc.async	= "false";
		  xmlDoc.loadXML(txt);
		} catch(e) {
			try {
				//Firefox, Mozilla, Opera, etc. 
				parser	= new DOMParser();
				xmlDoc	= parser.parseFromString(txt,"text/xml");
		    } catch(e) {
		    	xmlDoc = null;
		    }
		}
		return xmlDoc;
	};
	
	var _updateBandCounts = function(xmlDoc, filterSet)
	{

		var i, j, node;
		if (xmlDoc) {
			var xmlBands = xmlDoc.getElementsByTagName('band');
		
			try
			{
				var i, j, k, bustOut, bandToUpdate, updateBandCount, filterBands;
				var updateFilters = filterSet.getFilters();
				//alert(xmlBands);

				for (i=0; i<updateFilters.length; i++) {
					if (updateFilters[i].getId() != filterSet.getActiveFilterId()) {
						filterBands = updateFilters[i].getBands();
						for (j=0; j<filterBands.length; j++) {
							
							bustOut = false;
							bandToUpdate 	= filterBands[j];
	
							if (bandToUpdate) {
								updateBandCount = 0;
								if (xmlBands.length) {
									for(k=0; k<xmlBands.length;k++) {
										xmlBand = xmlBands[k].childNodes;
										for (node = 0; node < xmlBand.length; node++) {
											//alert('nodeValue:' + xmlBand[0].firstChild.nodeValue);
											if (xmlBand[0].firstChild.nodeValue == bandToUpdate.getId()) {
												updateBandCount = xmlBand[1].firstChild.nodeValue;
												//alert('new count:' + updateBandCount);
												bustOut = true;
												break;
											}
										}
										if (bustOut) {
											//alert('breaking out');
											break;
										}
									}
								}
								//alert(bandToUpdate.getId() + ' bandCount is now ' + updateBandCount);
								bandToUpdate.setCount(updateBandCount);
							}
						}
					}
				}			
			} catch (e) {
				//alert('Rob fix this in IE please: ' + e.message);
			}
		}
		return;
	};
	

	var _parseTotal = function(xmlDoc)
	{
		var productCount = xmlDoc.getElementsByTagName('count');

		_total = productCount[0].firstChild.nodeValue;

	};
	
	var _parseProductsFromXml = function(xmlDoc) {
		var i, xmlKey, xmlKeys, product, products;
		var productsArray = new Array();	
		if (xmlDoc) {
			// successfully turned it into XML.
			products = xmlDoc.getElementsByTagName('product');
			// OK we now have all the products.
			xmlKeys = new Array('id', 'name', 'partCode', 'uri', 'image', 'price', 'rating', 'manufacturerId', 'manufacturerName', 'masterId');
			// these are the only keys we're interested in.
			for(i=0; i<products.length;i++) {
				product = products[i].childNodes;
				productsArray[i] = new Array();

				for (node = 0; node < product.length; node++) {
					for (xmlKey in xmlKeys) {
						if (xmlKeys.hasOwnProperty(xmlKey)) {
							keyName = xmlKeys[xmlKey];
							if ((product[node].nodeName === keyName) && (product[node].hasChildNodes()) ) {
								// found the node value, populate the array with it.
								productsArray[i][keyName] = product[node].firstChild.nodeValue;
							}
						}
					}
				}
			}
			
		}
		products = new Array();
		
		for (i in productsArray) {
			// turn these into Review.Class objects using Review.factory()
			var manufacturer = new Manufacturer.Class(
				productsArray[i].manufacturerId,
				productsArray[i].manufacturerName
			);
			products[i] = Review.factory(
				productsArray[i].id,
				productsArray[i].partCode, 
				productsArray[i].name, 
				productsArray[i].uri, 
				productsArray[i].image, 
				manufacturer, 
				productsArray[i].price, 
				productsArray[i].rating,
				productsArray[i].masterId
			);
		}
		/*
		for (i in products) {
			//alert(products[i].getId());
		}
		*/
		return products;
	};
	
	/**
	 * Listen to a filter for when it changes status by adding itself to the Filter's
	 * internal array of listeners.
	 * 
	 * @param Dennis.FilterSet.Class
	 * @param Dennis.Filter.Class
	 */
	var _filterListen = function(filterSet, filter)
	{
		filter.addListener(filterSet);
	};
	
	/**
	 * Listen to a review for when it changes status by adding itself to the Filter's
	 * internal array of listeners.
	 * 
	 * @param Dennis.FilterSet.Class
	 * @param Dennis.Review.Class
	 */
	var _reviewListen = function(filterSet, review)
	{
		review.addListener(filterSet);
	};
	
	/**
	 * Return all the reviews currently loaded.
	 * @return array 
	 */
	var getReviews = function()
	{
		return _reviews;
	};
	
	
	/**
	 * Add a single Dennis.Review.Class object to the internal _reviews array
	 * (and listen to it)
	 * @param Dennis.Review.Class
	 */
	var addReview = function(review) 
	{
		_reviewListen(this, review);
		_reviews.push(review);
	};
	
	
	/**
	 * Clear all loaded reviews
	 */
	var resetReviews = function()
	{
		_reviews = new Array();
	};
	
	/**
 	 * Public function for setting the internal reviews array
	 * It is assumed an array of reviews is passed in.
	 * Unless the second argument is explicitly false, the new array
	 * replaces the old one, otherwise it is appended. 
	 */
	var setReviews = function(reviews, resetReviews)
	{
		if (resetReviews !== false) {
			this.resetReviews();
		}
		if ( (!reviews) || ((reviews.constructor !== Array) && (reviews.constructor !== Object)) ) {
        	return false;
    	} else {
			for (i = 0; i < reviews.length; i++) {
				this.addReview(reviews[i]);
			}
			return true;
		}
	};
	
	/**
	 * Return a single Dennis.Review.Class object that has the designated ID
	 * 
	 * @param integer the ID of the Review
	 * @return Dennis.Review.Class 
	 */
	var getReviewById = function(reviewId)
	{

		var review, reviews = this.getReviews();
		for (review=0; review<reviews.length; review++) {
			if (reviews[review].getId() == reviewId) {
				return reviews[review];
			}
		}
	};
	
	var update = function()
	{
		var i, listeners = this.getListeners();
		for (i in listeners) {
			//alert(listeners[i].getType());
			listeners[i].fire('xmlRefresh', this);
		}
	};
	
	/**
	 * Set public functions for this object
	 */
	
	_filterSet.getReviews			= getReviews;
	_filterSet.setReviews			= setReviews;
	_filterSet.addReview			= addReview;
	_filterSet.resetReviews			= resetReviews;
	_filterSet.getReviewById		= getReviewById;
	_filterSet.addFilter			= addFilter;
	_filterSet.getFilters			= getFilters;
	_filterSet.getId				= getId;
	_filterSet.setId				= setId;
	//_filterSet.getImage		= getImage;
	_filterSet.fetchRequest			= fetchRequest;
	_filterSet.xmlParse				= xmlParse;
	_filterSet.xmlRefresh			= xmlRefresh;
	_filterSet.update				= update;
	_filterSet.getTotal				= getTotal;
	_filterSet.filterChange			= filterChange;
	_filterSet.getActiveFilterId 	= getActiveFilterId;
	_filterSet.setActiveFilterId 	= setActiveFilterId ;
	
	return _filterSet;
};

