/**
 * ROSS Search Javascript UI using JQuery.
 * Uses Solr JSON output to render results.
 */
(function($){

	
	/**
	 * ROSS Search Request.
	 */
	$.fn.ross_query = function(q, start, options, callback) {
		var opt = $.extend({}, $.fn.ross_query.defaults);
		opt = $.extend(opt,options);
		opt.params.q = q;
		opt.params.start = start;
		opt.params.rows = opt.rows;
		if (opt.facet) {
			opt.params['facet.field'] = opt.facet;
		}
		if (q != '') {
			debug("Getting search results from url: " + opt.url);
			$.getJSON(opt.url + "?json.wrf=?", opt.params, callback);
		}
		else {
			debug("Search Query is empty. Not calling callback.")
		}
	};
	
	
	/**
	 * Ross Search and Search Results.
	 */
	$.fn.ross_search = function(options) {
		var opt = $.extend({}, $.fn.ross_search.defaults);
		opt = $.extend(opt,options);
		_DEBUG = opt.debug;
		
		var $this = this;
		$this.each(function() {
			$each = $(this);
			debug($each);
			// Hide our search results until we get the results.
			var rq = [opt.jq_search_results, opt.jq_facet_results, opt.jq_search_count + ' .count'].join(',');
			
			var setRealInput = function(v) {
				$each.find("input[@name='q']").get(0).value = v;
			};

			var getRealInput = function() {
				return $each.find("input[@name='q']").get(0).value;
			};
			
			var setInput = function (v) {
				$each.find(opt.jq_search_query).get(0).value = v;
			};
			
			var getInput = function () {
				return $each.find(opt.jq_search_query).get(0).value;
			};
			
			$each.find(rq).hide();
			$each.find(rq).empty();
			//Get the query string.
			//var q = $each.find(opt.jq_search_query).get(0).value;
			var q = getUrlParam('q');
			debug("Query from url is: ", q);
			setRealInput(q);
			
			var start = getUrlParam('start');
			debug("Start is:", start);
			start = start == "" ? 0 : parseInt(start);
			
			var fq = new FacetQuery(q, opt.facet);
			debug("Query is : " + q);
			setInput(fq.getSimpleQuery());
			
			//Search Now.
			$each.ross_query(q, start, opt, function(rsp) {
				//Handle the result rsp.
				debug("Search request sucessful. rsp:", rsp);
				$(opt.jq_search_results).ross_search_results(rsp,opt);
				$(opt.jq_facet_results).ross_facet_results(rsp,opt);
				$(opt.jq_facet_breadcrumbs).ross_facet_breadcrumbs(rsp,opt);
				$(opt.jq_search_paging).ross_search_paging(rsp,opt);
				$(opt.jq_search_count).ross_search_count(rsp,opt);
			});
			
			
			//On Submit take the simple query and turn into a real faceted query.
			$each.submit(function(){
				debug("Handling submit.")
				var simple_q = getInput();
				debug("Simple Query inputted.", simple_q);
				var newFq = new FacetQuery(simple_q, opt.facet);
				//Copy our selected facets to the new query.
				newFq.facets = fq.facets;
				var newQ = newFq.getFacetQuery();
				debug("New Query is: ", newQ);
				//Set our real form input to the new faceted query.
				setRealInput(newQ);
				return true;
			});
		});		
	};
	 
	
	
	var FACET_SEP = ':';
	var TERM_SEP = ' AND ';
	
	function FacetQuery(q, facetField) {
		this.facets = [];
		this.nonFacets = [];
		this.facetField = facetField;
		this.originalQ = q;
		if (q && facetField)
			return this.init(q, facetField);
		else
			return this;
	} 
	
	
	$.extend(FacetQuery.prototype, {

		init : function (q, facetField) {
			this.facetField = facetField;
			this.originalQ = q;
			this.parse(q);
			
			return this;
		},
		
		
		parse : function(q) {
			var fq = this;
			var terms = $.map(q.split(TERM_SEP), function(item) { return $.trim(item); });
			
			$.each(terms, function(i, item) { 
				if ( item.indexOf(fq.facetField + FACET_SEP) == 0 ) {
					var f = item.replace(fq.facetField + FACET_SEP, "");
					if (f != "") {
						fq.facets.push(f);
					}
				}
				else if (item != "") {
					fq.nonFacets.push(item);
				}
			});
		},
		
		getFacetQuery : function() {
			var fq = this;
			debug("getFacetQuery", fq);
			var facets = $.map(fq.facets, function(item) { return fq.facetField + FACET_SEP + item; });
			var query = fq.nonFacets.concat(facets).join(TERM_SEP);
			return query; 
		},
		
		getSimpleQuery : function() {
			var fq = this;
			return fq.nonFacets.join(TERM_SEP);
		},
		
		getLink : function(params) {
			var p = { q : this.getFacetQuery() };
			if (params)
				p = $.extend(p, params);
			return "?" + $.param(p);
		},
		
		clone : function() {
			return new FacetQuery(this.getFacetQuery(), this.facetField);	
		}
		
	});
	
	$.fn.ross_search.FacetQuery = FacetQuery;	
	
	function getUrlParam( param ) {
		param = param.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
		var regexS = "[\\?&]"+param+"=([^&#]*)";
		var regex = new RegExp( regexS );
		var results = regex.exec( window.location.href );
		if( results == null )
			return "";
		else
			return unescape(results[1].replace(/\+/g, "%20"));
	}
	
	$.fn.ross_search.getUrlParam = getUrlParam;	

	
	/**
	 * Handles the Search Results.
	 */
	$.fn.ross_search_results = function(rsp, options) {
		debug("Handling search results.");
		var opt = options;
		this.each(function() {
			$elem = $(this);
			debug($elem);
			//Add the header to the search results slot.
			var results = opt.header;
			$.each(rsp.response.docs, function(i, doc) {
				var f = function(wrapper) { return $.isFunction(wrapper) ? wrapper(i,doc) : wrapper; }
				//Reconstruct the snippet search result.
				results += $.map([opt.before,doc[opt.snippet_field], opt.after], f).join('');
			});
			//Add the footer to the search results slot.
			results += opt.footer;
			debug(results);
			$elem.append(results);
		});
		this.show();
		return this;
	};
	
	/**
	 * Handles the Search Results Count.
	 */
	 $.fn.ross_search_count = function(rsp, options) {
	 	debug("Handling search results count.");
	 	debug(this.html());
	 	this.append(_html('span', {'class' : 'count'}, rsp.response.numFound));
		this.show();
		return this;
	 };
	
	function Paging(start, rows, total) {
		this.start = start;
		this.rows = rows;
		this.total = total;
	}
	
	$.extend(Paging.prototype, {
		
		
		_safeNum : function(num) {
			return num < 0 || num == NaN ? 0 : num;
		},
		
		getPage : function() {
			var r = this._safeNum(this.rows);
			var s = this._safeNum(this.start);
			return r == 0 ? 0 : Math.floor(s / r);
		},
		getCount : function() {
			var t = this._safeNum(this.total);
			var r = this._safeNum(this.rows);
			return t == 0 ? 0 : Math.ceil(t / r);
		},
		getPageIndexes : function () {
			var c = this.getCount();
			var indexes = [];
			var rows = this._safeNum(this.rows);
			for (var j = 0, i = 0; i < c; i++, j += rows) {
				indexes.push(j)
			}
			return indexes;
		}
		
		
	});
	
	function _html(e, atts, text) {
		var attList = [];
		$.each(atts, function (k,v) {
			attList.push(k.toString() + '="' + v.toString() + '"'); 
		});
		return '<' + e + ' ' + attList.join(' ') + '>' + text.toString() + '</' + e + '>';
	}
	
	$.fn.ross_search_paging = function(rsp, options) {
		debug("Handling Search Results Paging.", this);
		var rows = parseInt(rsp.responseHeader.params.rows);
		var total = parseInt(rsp.response.numFound);
		var start = parseInt(rsp.response.start);
		
		var opt = options;
		var fq = new FacetQuery(q_rsp(rsp), opt.facet);
		var paging = new Paging(start, rows, total);
		
		var indexes = paging.getPageIndexes();
		var links = $.map(indexes, function(item) { return fq.getLink({'start': item}); });
		this.each(function() {
			$this = $(this);
			var ahrefs = [];
			$.each(links, function(i, item) { 
				var p = i+1;
				if (i == paging.getPage()) {
					ahrefs.push(_html('span', {title : item}, p));	
				}
				else {
					ahrefs.push(_html('a', {href : item}, p));
				}
				//ahrefs.push('<a href="'+ item +  '">' + (i+1).toString() + '</a>');
			});
			debug("ross search paging", $this);
			debug("a hrefs", ahrefs);
			var pgCount = paging.getCount();
			var pg = paging.getPage() + 1;
			var pgLabel = "Page " + pg +  " of " + pgCount;
			$this.append(_html('div', {'class' : 'ross_search_paging_count'}, pgLabel)).end();
			$this.append(_html('div',{'class' : 'ross_search_pages'},ahrefs.join(' \n')));
		});
		
		return this;
	};
	
	/**
	 * Handles the Facet Result response.
	 */
	$.fn.ross_facet_results = function(rsp, options) {
		debug("Handling facet results.");
		var opt = options;
		this.each(function() {
			$elem = $(this);
			if (opt.facet) {
				
				var facetData = rsp.facet_counts.facet_fields[opt.facet];
				var facets = facetResponseToFacets(facetData, rsp, opt);
				
				// Segment Categorization
				if (opt.facetCategories) {
					var fc = opt.facetCategories;
					var cfr = categorizeFacetResults(facetData, opt.facetCategories);
					var html = [];
					$.each(fc.categories, function(i, cat) {
						if (cfr[cat]) {
							html.push('<h2>');
							html.push(facetHtmlList(cfr[cat]));
							html.push('</h2>');
						}
					});
					$elem.append(html.join('\n'));
								
				}
				// No Categorization
				else {
				
					debug("Facets:", facets);
					var text = facetHtmlList(facets);
					$elem.append(text);

				}
				
				if (facets.length)
					$elem.show();
					
				
			}
		});
		return this;
	};
	
	function facetHtmlList(facets) {	
		var items = [];
		$.each(facets, function(i,f) {
			items.push(_html('li', {}, _html('a', {href : f.link}, f.name) ) );
		});
		return _html('ul', {'class' : 'facets'}, items.join('\n'));
	}
	
	function facetResponseToFacets(facetData, rsp, opt) {
		var facets = [];
		$.each(facetData, function(i, ignore) {
			if (i % 2 == 0) {
				var fname = facetData[i];
				var l = "?q=" + escape(q_rsp(rsp) + TERM_SEP + opt.facet + FACET_SEP + facetData[i] );
				debug("Link", l);
				debug("Rsp", rsp);
				debug("Options", opt);
				facets.push({name: fname, value: facetData[i+1], link: l});
			}
		});	
		
		return facets;
	}
	
	function categorizeFacetResults(facets, facetCategories) {

		var categorizedFacetResults = {};

		
		// loop through each facet
		// for each facet loop through all the categories
		// check to see if the facet belongs to the categories
		// if it does put in the bucket.
		$.each(facets, function(i, facet) {
			$.each(facetCategories.categories, function(i, cat) {
				if($.inArray(facet.name, facetCategories.segments[cat]) > -1) {
					if( ! categorizedFacetResults[cat] ) {
						categorizedFacetResults[cat] = [];
					}
					categorizedFacetResults[cat].push(facet);
				}
					
			});
		});
		
		categorizedFacetResults.all = [];
		categorizedFacetResults.all.push(facets);
		return categorizedFacetResults;
	
	}
	
	/**
	 * Get the query from the Solr response.
	 */
	function q_rsp(rsp) {
		return rsp.responseHeader.params.q;
	}
	
	function css(c) {
		return c.replace("#","").replace(".","");
	}
	
	$.fn.ross_facet_breadcrumbs = function(rsp, options) {
		debug("Handling facet breadcrumbs.");
		var opt = options;
		var fq = new FacetQuery(q_rsp(rsp), opt.facet);
		debug(this);
		var facetElements = $.map(fq.facets, function(f) {
			var flink = "";
			var flabel = f;
			var remove = "(X)"
			return '<span><a title="' + f +'" href="' + flink +'" >' 
				+ flabel + 
				'</a> ' +
				'<a href="" title="' + f + '" class="' + css(opt.jq_facet_remove) +'">' 
				+ remove + 
				'</a></span>';
		});
		this.empty()
			.append(facetElements.join(opt.breadcrumb_separator));
			//When they click on remove we need to remove that facet.
		
		this.find(opt.jq_facet_remove)
			.each(function() {
				$this = $(this);
				var facet = $this.attr("title");
				var newFq = fq.clone();
				var index = $.inArray(facet, newFq.facets);
				if (index > -1) {
					newFq.facets.splice(index,1);
				}
				
				$this.attr('href', newFq.getLink());
				
			});
	}
	
	/**
	 * Default Settings for the search request.
	 */
	$.fn.ross_query.defaults = {
		url : '/solr/select/',
		rows : 10,
		params : {
			wt : 'json',
			start : 0,
			facet : "true"
		}
	};
	
	/**
	 * Default Setting for search ui.
	 */
	$.fn.ross_search.defaults = {
		debug : false,
		// JQuery Identifiers to find elements that need decorating.
		jq_search_results : '.ross_search_results',
		jq_search_count : '.ross_search_count',
		jq_search_paging : '.ross_search_paging',
		jq_search_form : '.ross_search_form',
		jq_search_query: '.ross_search_query',
		jq_facet_results : '.ross_facet_results',
		jq_facet_remove : '.ross_facet_remove',
		jq_facet_breadcrumbs : '.ross_facet_breadcrumbs',
		
		//Search Options.
		snippet_field : 'ross_snippet',
		facet : 'ross_segments',
		header : '<div>',
		before : '',
		after : '',
		footer : '</div>',
		facetCategories : null,
		
		breadcrumb_separator : ' --> ',
		rows : 10
	};
	
	var _DEBUG =  $.fn.ross_search.defaults.debug;
	
	function debug() {
		if (typeof  console != 'undefined' && _DEBUG) {
			$.each(arguments, function(i, item) {
				console.log(item);
			});
		}
	}
	
})(jQuery);

