var Search = {
	init: function() {
		Search.Form.init();
		Search.Validation.init();
	}
};

function $(id) {
	return document.getElementById(id);
}

Search.Form = {

	init: function() {
		EventUtil.addListener(this.getKw(), "click", this.switchOutputUnits.bind(this));
		EventUtil.addListener(this.getBtu(), "click", this.switchOutputUnits.bind(this));
	    this.switchOutputUnits();

		EventUtil.addListener(this.getForm(), "submit", this.doSearch.bindAsEventListener(this));
		EventUtil.addListener(this.getForm(), "reset", this.doReset.bindAsEventListener(this));

		this.checkSubmit();

		this.setRowVisible(this.getNoMatches(), false);
	},

	getKw: 		function() { return $("search_kw"); },
	getBtu: 	function() { return $("search_btu"); },
	getOutput: 	function() { return $("search_output"); },
	getMaxCount:function() { return $("max_boiler_count"); },
	getHeight: 	function() { return $("search_height"); },
	getWidth: 	function() { return $("search_width"); },
	getDepth: 	function() { return $("search_depth"); },
	getGas:		function() { return $("search_gas"); },
	getP:		function() { return $("search_p"); },
	getOil:		function() { return $("search_oil"); },
	getBiomass: function() { return $("search_biomass"); },
	getButton: 	function() { return $("searchButton"); },
	getForm:	function() { return $("searchForm"); },
	getTable: 	function() { return $("cData"); },
	getNoMatches:function(){ return $("rowNoMatches"); },

	switchOutputUnits: function() {
		this.getTable().className = this.getKw().checked?"kw":"btu";
		this.doReset();
	},



	doSearch: function(event) {	

		//check if valid search
		if (!this.areParamsValid()) {
			alert("Numbers only! \n Red boxes mean you need to clear the box or use a number.");
			EventUtil.cancelEvent(event);
			return;
		}

		Search.Matcher.prepare();

        //collect all the rows
		var rows = this.getTable().getElementsByTagName("TR");

		//loop though and decide if visible or not
		
		var any = false;
		
		var i = 0;
		var total = 0;
		while (i < rows.length) {			
			var r = rows[i];
			//only want rowProduct (first time only really)
			if (!Element.isClass(r, "rowProduct")) {
				i++;
				continue;
			}

			var passed = false;			
			var rM = rows[++i];
			while (rM != null && Element.isClass(rM, "rowData")) {
				//these are the model rows                   

				var matches = Search.Matcher.isMatch(rM);
				total += (matches?1:0);
				this.setRowVisible(rM, matches);
				
				passed = passed || matches;

				rM = (i == rows.length-1?null:rows[++i]);
			}

            this.setRowVisible(r, passed);
			any = any || passed;
		}

		this.setRowVisible(this.getNoMatches(), !any);

		$("totalMatches").innerHTML = total;
		$("pluralMatches").style.display = (total == 1?"none": "");
		$("matches").style.visibility = "visible";
        	

		EventUtil.cancelEvent(event);
		
	},

	doReset: function() {
		var rows = this.getTable().getElementsByTagName("TR");
		for (var i=0; i < rows.length; i++) {
			var c = getElementsByTagAndClass(rows[i], "TD", "colReq");
			if (c != null && c.length != 0) c[0].innerHTML = "&nbsp;";

			this.setRowVisible(rows[i], true);
		}		
		$("matches").style.visibility = "";
	},

	checkSubmit: function() {
		this.getButton().disabled = !this.areParamsValid();
	},

	areParamsValid: function() {
		return this.isParamValid(this.getOutput()) &&
			this.isParamValid(this.getHeight()) &&
			this.isParamValid(this.getWidth()) &&
			this.isParamValid(this.getDepth());
	},

	isParamValid: function(input) {
		return !Element.isClass(input, Search.Validation.ERROR_CLASS);
	},

	setRowVisible: function(row, visible) {
		var alreadyVisible = this.isRowVisible(row);
		if (alreadyVisible && !visible) {
			row.className += " rowHidden";
		}
		if (!alreadyVisible && visible) {
			Element.replaceClass(row, "rowHidden", "");
		}
	},

	isRowVisible: function(row) {
		return !Element.isClass(row, "rowHidden");
	}	
};

//** Matcher ***

Search.Matcher = {
		//input search bounds
		lowerO : 0,
	 	upperO : 0,
		
		// max number of boilers
		maxN: 1,

		//kilowatts of BTUs
		kw: true,
	 
	 	//dimensions
	 	h : 0,
	 	w : 0,
	 	d : 0,
	 
	 	//fuel
	 	fG : false,		
		fP : false,		
		fO : false,
		fB : false,

		currentRow: null,

		prepare: function() {
			//copy stuff from form into properties
			var o = this._isNumberOrNull(Search.Form.getOutput().value);
			this.lowerO = (o == null)?null:0.9*o;
			this.upperO = (o == null)?null:1.1*o;
			
			if (Browser.isIE()) {
				//just getting the .value doesn't seem to work.
				//so get the text of the selected Option.
				//what a browser!
				var sIndex = Search.Form.getMaxCount().selectedIndex;		
				this.maxN = Search.Form.getMaxCount().options(sIndex).text;
			} else {
				this.maxN = Search.Form.getMaxCount().value;
			}

			this.kw = Search.Form.getKw().checked;

            this.h = this._isNumberOrNull(Search.Form.getHeight().value);
			this.w = this._isNumberOrNull(Search.Form.getWidth().value);
			this.d = this._isNumberOrNull(Search.Form.getDepth().value);

			this.fG = Search.Form.getGas().checked;
			this.fP = Search.Form.getP().checked;
			this.fO = Search.Form.getOil().checked;
			this.fB = Search.Form.getBiomass().checked;

			//this.f = _isNumberOrNull(Search.Form.get().value);
			//this.m = _isNumberOrNull(Search.Form.get().value);
		},

		_isNumberOrNull: function(value) {
			var number = parseFloat(value);
			return (isNaN(number))?null:number;
		},

		//if got a - in it number range
		_isRange: function(value) {
			return (value.indexOf("-") != -1);
		},

/*		_isInRange: function(range, m) {
			var pos = range.indexOf("-");
			var lowerR = parseFloat(range.substring(0, pos));
			var upperR = parseFloat(range.substr(pos+1));

			Debug.print("checking range for " + lowerR + " to " + upperR);

            return (
				(lowerR <= m.lowerO && upperR >= m.lowerO) ||
				(lowerR <= m.upperO && upperR >= m.upperO)
			);
		},
*/

		
		_getCountForRange: function(range, m) {
			var pos = range.indexOf("-");
			var lowerR = parseFloat(range.substring(0, pos));
			var upperR = parseFloat(range.substr(pos+1));

			//aah, whatever, just use the top value. Gets v. tricky otherwise.
			return this._getCountForValueRange(lowerR, upperR, m);

		},

		_getCountForValue: function(v, m) {
			//boiler output as float
			var boilerO = parseFloat(v);
			
			//why would it be invalid?

			//is upperOutput lower than value? ie. boiler doesn't go that low
			if (m.upperO < boilerO) return 0;

			//just the one?
			if ((m.lowerO <= boilerO) && (m.upperO >= boilerO)) return 1;

			//If its a Modular, bail here, as they are already multiple boilers
		   	if (m._isCurrentRowModular()) return 0;

            //OK, work out ratios
			var uC = Math.floor(m.upperO / boilerO);
			var lC = Math.ceil(m.lowerO / boilerO);

			//need more boilers than selected
			if (lC > this.maxN) return 0;

			//how many then? 
			
			//a whole number lies in the range
			if (uC == lC) return lC;

			//no whole number of boilers in range
			if (lC > uC) return 0;

			//ah, now, could be multiple values in range(dunno how!)
			//if top value too big, use lower one
			return (uC > this.maxN)?lC:uC;
		},

		_doesBoilerRate: function (lv, uv, m) {
			var boilerU = parseFloat(uv);
			var boilerL = parseFloat(lv);

			//why would it be invalid?
            			
			//boiler doesn't go that low
			if (m.upperO < boilerL) return 1;
			
			//boiler doesn't go that high 
			if (m.lowerO > boilerU) return -1;			
			
			//some overlap, should at least look at it
			return 0;
		},

		_getCountForValueRange: function (lv, uv, m) {			
			
			var br = this._doesBoilerRate(lv, uv, m);

			//found one!
			if (br == 0) return 1;

			//too strong
			if (br == 1) return 0;

			//too weak and no multiples allowed (==1 or already modular)
			if (br == -1 && (this.maxN <= 1 || m._isCurrentRowModular())) return 0;			
			
			
			//right lets guess the boiler count
			var boilerU = parseFloat(uv);
			var boilerL = parseFloat(lv);

		   	//OK, work out ratios - 
		   	var uC = Math.floor(m.upperO / boilerL); //highest count you should need
		   	var lC = Math.ceil(m.lowerO / boilerU);	 //lowest count you should need			

			//need more boilers than selected			
			if (lC > this.maxN) return 0;

			//how many then?			
			
			//one whole number lies in the range
			if (uC == lC) return lC;

			//no whole number of boilers in range, eg. narrow output variation
			if (lC > uC) return 0;

			//ah, now, could be multiple boiler numbers in range (v. modulating, big output variation)
			
			//well lets work our way up, testing each number for fit

			for (var i=lC; lC <= uC; i++) {
				var r = this._doesBoilerRate(lv*i, uv*i, m);
				//match?
				
				if (r == 0) return i; //woo!				
				
				//too strong?
				if (r == 1) return 0;//something funny there

			}
			
			//well clearly no matches, bit weird.
			return 0;
			
			
			//if top value too big, use lower one
			//return (uC > this.maxN)?lC:uC;			

		},

		_setRequiredCellValue: function(v) {			
			var cell = getElementsByTagAndClass(this.currentRow, "TD", "colReq")[0];
			cell.innerHTML = v;
		},

		_isCurrentRowModular: function() {
			var cell = getElementsByTagAndClass(this.currentRow, "TD", "dataModel")[0];	
			return cell.innerHTML.indexOf("x") != -1;
		},

		isMatch: function(row) {            
            this.currentRow = row;
            for (var i=0; i < row.childNodes.length; i++) {
				var c = row.childNodes[i];
				
				if (c.nodeName && c.nodeName == "TD") {
					//look for an excuse to throw it out
					
					var clazz = c.className;
					var v = c.firstChild.nodeValue;
					Debug.print("Class: " + clazz  + " | Value: " + v);
					if (!this.functors.get(clazz)(v, this)) {
						return null;
					}
				}
            }
			
			this.currentRow = null;
			return true;
		},

		functors: {
			colKw: function(value, m) {
				if (m.lowerO==null || !m.kw) return true;

				//count of boilers needed to satisfy output
				var c = 0;
				if (m._isRange(value)) {
					c = m._getCountForRange(value, m);
					
					if (c > 1) {
						var pos = value.indexOf("-");
						var lowerR = parseFloat(value.substring(0, pos));
						var upperR = parseFloat(value.substr(pos+1));

						m._setRequiredCellValue("x" + c + "= " + c*lowerR+"-"+c*upperR);
					}
					return (c != 0);

				} else {					
					var c = m._getCountForValue(value, m);

					if (c > 1) {
						m._setRequiredCellValue("x" + c + "= " + c*value);
					}

					return (c != 0);
					
					//var n = parseFloat(value);	
					//return (m.lowerO <= n && m.upperO >= n);
				}
			},
		    colBtu: function(value, m) {
				if (m.lowerO==null || m.kw) return true;

				var c = 0;
				if (m._isRange(value)) {
					c = m._getCountForRange(value, m);

					if (c > 1) {
						var pos = value.indexOf("-");
						var lowerR = parseFloat(value.substring(0, pos));
						var upperR = parseFloat(value.substr(pos+1));

						m._setRequiredCellValue("x" + c + "= " + c*lowerR+"-"+c*upperR);
					}
					return (c != 0);
				} else {								
					c = m._getCountForValue(value, m);

					if (c > 1) {
						m._setRequiredCellValue("x" + c + "= " + c*value);
					}					
					return (c != 0);
					//var n = parseFloat(value);														
					//return (m.lowerO <= n && m.upperO >= n);
				}
			},
		    colHeight: function(value, m) {
				return m.h == null || value <= m.h;
			},
		    colWidth : function(value, m) {
				return m.w == null || value <= m.w;
			},
            colDepth: function(value, m) {
				return m.d == null || value <= m.d;
			},

/*
			colFuel: function(value, m) {
				return (
					(!m.fG || value.indexOf("gas") != -1) &&
					(!m.fP || value.indexOf("p") != -1) &&
					(!m.fO || value.indexOf("oil") != -1)
				);
			},
*/

			colFuel: function(value, m) {
				return ((!m.fG && !m.fP && !m.fO && !m.fB) ||				
					(
						(m.fG && value.indexOf("gas") != -1) ||
						(m.fP && value.indexOf("p") != -1) ||
						(m.fO && value.indexOf("oil") != -1) ||
						(m.fB && value.indexOf("bio") != -1)
					)
				);			
			},

			get: function(name) {
				var f = this[name];
				return (f == null)?this.def:f;
			},

			def: function() { return true; }
		}
};



Search.Validation = {

	ERROR_CLASS: "error",
	
	init: function() {
		var numberFields = document.getElementsByTagAndClass("INPUT", "inputNumber");		
		for (var i=0; i < numberFields.length; i++) {
			var f = numberFields[i];
			EventUtil.addListener(f, "keypress", this.checkNumber.bindWith(this, [f]));				
		}
	},

	checkNumber : function(elemRef) {
		setTimeout(this._checkNumber.bindWith(this, [elemRef]), 50);
	},

	_checkNumber: function(elemRef) {			
		var passed = true;
		var value = elemRef.value;
		if (value != '') {
			try {
				var num = parseFloat(value);
				if (isNaN(num)) passed = false;
			} catch(e) {
				passed = false;
			}		
		}

		var isAlreadyError = Element.isClass(elemRef, this.ERROR_CLASS);
		if (passed == true && isAlreadyError) {
			Element.replaceClass(elemRef, this.ERROR_CLASS, "");
		} else if (passed != true && !isAlreadyError) {
			elemRef.className += " " + this.ERROR_CLASS;
		}
		Search.Form.checkSubmit();
	}
};


