// Javascript for Adoptable cat pages

var TabContainer = Class.create({

	initialize:
		function (container, active) {
			var activeTab = null;
			this._tabs = [];
			var handler = this._tabClick.bindAsEventListener(this);
			var extractId = /\/.+#([^\/]+$)/;
			active = $(active);
			$(container).select('li').each((function (elt) {
				// Find element controlled by this tab
				elt._a = elt.select('a')[0];
				elt._span = elt._a.select('span')[0];
				var results = extractId.exec(elt._a.href)
				var ref = $(results[1]);
				if (ref == active) activeTab = elt;
				elt._content = ref;
				this._tabs.push(elt);
				elt.observe('click', handler);
			}).bind(this));	// end each
			[$(activeTab)._a, $(activeTab)._span].invoke('addClassName', 'active');
			$(activeTab)._content.show();
	},

	_tabClick:
		function (event) {
			// If already open, do nothing
			var li = Event.findElement(event, 'li');
			if (li._content.visible()) return;
			// Hide currently active tab & make this one visible
			var active = this._tabs.find(function (tab) {
					return tab._a.hasClassName('active');
				});
			if (active) {
				[active._a, active._span].invoke('removeClassName', 'active');
				active._content.hide();
			}
			[li._a, li._span].invoke('addClassName', 'active');
			li._content.show().fire('tab:opened');
			event.stop();
		}

}); // TabContainer

var Filter = Class.create({
						  
	initialize:
		function (init) {
			this._setDefaults();
			this._filter = init || { };
			this._activeRequest = null;	// No active request at start
		},
			
	getFilter:
		function () {
			return this._filter;
		},
		
	getDefaults:
		function () {
			return this._defaults;
		},
		
	// Return true if there is a filter in place
	isFiltered:
		function () {
			var defaults = this._defaults;
			var result = $H(this._filter).find(function (pair) {
					// Current filter key/value must match defaults
					// or there is a filter in place.  But, we ignore
					// start & count (they ain't really filters in that sense)
					if (pair.value!=defaults[pair.key] && pair.key!='start' && pair.key!='count')
						return true;
				});
			return !!result;
		},

	// Execute filter
	exec:
		function(elts, debug) {
			// Abort any current operation
			if (this._activeRequest) this._activeRequest.transport.abort();
			document.fire('filter:start');

			// Inspect each elt & add clause to filter object, as necessary
			if (elts) {	// null/no arg means use defaults
				if (!Object.isArray(elts))
					// Turn scalar into array so code is consistent
					elts = new Array(elts);
			} else
				elts = setToolbox(null);	// Set each element to its default

			elts.inject(this._filter, this._filterClause, this);
			debug = debug || false;	// Default is false
			// BAD HACK ALERT
			var saveStart = null;	// Save start value, if required
			if (this._filter['keep_start']) {
				saveStart = this._filter['keep_start'];
				delete this._filter.keep_start;
			}
			var tracking = this.serialize();

			var options = { method: 'get', parameters: Object.clone(this._filter) };
			delete this._filter.before;		// These don't belong in 'permanent' filter
			delete this._filter.after;
			if (saveStart)
				this.filter.start = saveStart;

			if (debug) options.parameters.debug = 1;
			//alert($H(options.parameters).inspect());
			options.onSuccess = function (response) {
				//alert('Query: ' + response.responseJSON);
				this._activeRequest = null;
				document.fire('filter:complete', $A(response.responseJSON));
				pageTracker._trackPageview('adoptable.php?' + tracking);
				//alert('adoptable.php?' + tracking);
			};
			options.onFailure = function (response) {
				this._activeRequest = null;
				alert('Ajax error: ' + response.statusText);
			};
			this._activeRequest = new Ajax.Request('/filters/filter-cats4.php', options);
		},

	// Create string of current filter values, suitable for use as a query string
	serialize:
		function () {
			//alert(Object.toQueryString(this._filter));
			return $H(this._filter).toQueryString();
		},
	
	// Return plain text/HTML explanation of current filter.
	toText:
		function (html) {
			var text = '';
			var thisFilter = this;
			var nl = (html ? '<br />' : '\n');
			
			$H(this._defaults).merge(this._filter).each(function (elt) {
					if (thisFilter._textDesc[elt.key])
						try {
							text += nl
								+ thisFilter._parmToText.bind(thisFilter)(elt.key, elt.value, html);
						} catch (e) {
							var mesg = e.description;
							if (!mesg) mesg = e.message;
							alert(mesg);
						}
				});
			return text;
		},
		
	// PRIVATE stuff starts here
	
	_parmToText:
		function (id, value, html) {
			var mesg;

			if (this._textDesc[id].multiple)
				mesg = this._multipleToText(id, value);
			else
				mesg = this._textDesc[id][value];
			if (html && value!=this._defaults[id])	// NON default value
				return '<b>' + id.toUpperCase() + ': ' + mesg + '</b>';
			else
				return id.toUpperCase() + ': ' + mesg;
		},
		
	// Can have multiple values
	_multipleToText:
		function (id, value, html) {
			var conjunction = ' OR ';
			var desc = this._textDesc[id];

			if (this._filter[desc.multiple] == 'all')
				conjunction = ' AND ';
			
			value = value.split(',');
			return value.inject('',
				function(acc, elt) {
					if (acc.length > 0) acc += conjunction;
					return acc + desc[elt];
				});
		},
	
	_setDefaults:
			function () {
				this._defaults = DEFAULT_VALUES;
				this._textDesc = DESCRIPTIONS;
			},
			
	_filterClause:
		function(acc, elt) {
			var val;
			//acc = $H(acc);
			switch (elt.id) {
				
				// Ignore these
				case 'filter-email':
				case 'set-registered-filter':
				case 'get-registered-filter':
				case 'delete-registered-filter':
				case 'keep_start':
					return acc;

				// SELECT (no MULTIPLE) elements
				case 'sort':
				case 'gender':
				case 'status':
					val = $F(elt);
					if (this._ignoreClause(elt.id, val)) return acc;
					acc[elt.id] = val;
					acc.start = 0;	// Start at 1st element after filtering
					return acc;

				// SELECT MULTIPLE elements
				case 'age':
				case 'breed':
				case 'color':
				case 'marking':
				case 'personality':
					val = $F(elt);
					if (!Object.isArray(val)) val = new Array(val);
					acc.start = 0;

					// If any element is default value, just remove this attribute & return
					if (val.include(this._defaults[elt.id])) {
						delete acc[elt.id];
						delete this._filter[elt.id];
						return acc;
					}
					val = val.join(',');
					acc[elt.id] = val;

					//  Get adjunct field & include it if it's not the default
					var field = $('toolbox')[elt.className];
					if (field.length == undefined) field = new Array(field);
					var opt = $A(field).find(function (elt) {
							return elt.checked;
						});
					acc[elt.className] = opt.value;
					return acc;

				// Pseudo elements
				case 'start':
				case 'count':
				case 'before':
				case 'after':
					val = elt.value;
					acc[elt.id] = val;
					return acc;
				
				// Radio elements or error
				default:
					// Could be radio group
					if (elt.type != 'radio') {
						alert('Unexpected elt (' + elt.id + '): ' + $H(elt).inspect());
						return acc;
					}
					// Don't do anything unless it's  chcecked
					if (!elt.checked) return acc;

					// 'value' attribute has value to use
					val = elt.value;
					if (this._ignoreClause(elt.name, val)) return acc;
					acc[elt.name] = val;
					acc.start = 0;
					return acc;
			}

		},

		_ignoreClause:
			function (id, val) {
				if (this._filter[id] && val==this._filter[id])
					return true;
				if (!this._filter[id] && val==this._defaults[id])
					return true;
				return false;
			}
});


var Current = {
	page:		1,
	nrecs:		undefined,
	npages:		undefined,
	filter:		undefined,
	CPP:		10		// Cats per page
};

var debug = false;

	document.observe('dom:loaded',
		function () {

		/// 1: SET UP TABS incl embedded buttons
			$('debug').observe('click',
				function () {
					debug = !debug;
					alert('debug: ' + debug);
				});
			new TabContainer('toolbox', 'basic-tab');
			$('collapse-tab', 'expand-tab').invoke('observe', 'click', controlTab);
			$('system-defaults').observe('click',
				function (event) {
					if (confirm('Are you sure you want to reset all values to their defaults?')) {
						Current.filter.exec(setToolbox(null), debug);
						setFilteredMesg(false);
					}
					event.stop();
				});
			$('remove-default-filter').observe('click', removeDefaultFilter);
			$('apply-default-filter').observe('click', applyDefaultFilter);
			if (!GetCookie('default-filter')) defaultButtons('disable');

		/// 2: Set up event handlers for hrefs to individual cats (allows return with last filter)
			$$('a.cat-href').invoke('observe', 'click', catHref); 

		/// 3: Get first batch of cats
			clearCats();
			document.observe('filter:complete', updateCats);
			document.observe('filter:start', clearCats);
			Current.filter = new Filter();
			//alert('Before exec: ' + $H(Current.filter).inspect());
			Current.filter.exec(getEntryFilter(), debug);
			Current.page = 1;
			setFilteredMesg();
				
			$('current-page').observe('change', function (event) {
					gotoPage($F(Event.element(event)));
				});
			$$('.next-page, .prev-page').invoke('observe', 'click',
				function (event) {		
					var elt = Event.findElement(event, 'div');
					
					if (elt.hasClassName('prev-page'))
						gotoPage(Current.page-1);
					else
						gotoPage((Current.page|0)+1);	// Coerce to int
					event.stop();
				});  // invoke
				
			// These handlers are for the toolbox form
			setHandlers();
		});	// document.observe
	
	function defaultButtons(action) {
		$('apply-default-filter', 'remove-default-filter').each(function (button) {
				button.disabled = (action == 'disable');
			});
	}

	function removeDefaultFilter(event) {
		if (confirm('Are you sure you want to remove your default filter?')) {
			DeleteCookie('default-filter');
			defaultButtons('disable')
			alert('Default filter removed');
		}
		event.stop();
	}

	function applyDefaultFilter(event) {
		event.stop();

		var defaultFilter = GetCookie('default-filter');
		if (!defaultFilter) {
			alert('You have not defined a default filter.');
			return;
		}

		// Overlay default filter on systewm defaults
		var filter = ('?' + defaultFilter).parseQuery();
		var defaults = Current.filter.getDefaults();

		filter = $H(defaults).merge(filter);
		Current.filter.exec(applyFilter(filter), debug);
		setFilteredMesg();
	}

// Determine whether there is a filter we should start with.
	// There are 3 possible starting filters.  They are listed below
	// in decreasing order of precedence:
	//
	//		1: last-filter
	//		2: query string on URL
	//		3: default-filter
	function getEntryFilter() {
		var lastFilter = GetCookie('last-filter');
		var filter;

		if (lastFilter)
			filter = '?' + lastFilter;
		else
			// See if there is a query string on URL
			if (location.href.indexOf('?') >= 0)
				filter = location.search;
			else {
				var defaultFilter = GetCookie('default-filter');
				if (!defaultFilter)
					filter = '?';
				else
					filter = '?' + defaultFilter;
			}
		//alert('Before applyFilter: ' + $H(Current.filter.getDefaults()).inspect());
		var result = applyFilter(filter);
		//alert('After applyFilter: ' + $H(Current.filter.getDefaults()).inspect());
		return result;
	}
	
	// Accepts string (query string to be parsed) or object (already parsed)
	function applyFilter(filter) {
		var filterObj;
		
		if (Object.isString(filter))
			filterObj = filter.parseQuery();
		else
			filterObj = filter;

		// We have a filter object.  Use it to set the form elements.
		if (Object.values(filterObj).size() == 0) return null;
		return setToolbox(filterObj);
	}

	function setToolbox(filter) {
		var elts = [];
		
		if (!filter) filter = Current.filter.getDefaults();
	
		$H(filter).each(function (pair) {
				switch (pair.key) {
					case 'sort':
					case 'gender':
						return elts.push(setSelect(pair));
					case 'age':
					case 'color':
					case 'breed':
					case 'marking':
					case 'personality':
						return elts.push(setSelectMultiple(pair));
					case 'selectedColors':
					case 'selectedTraits':
					case 'status':
						return elts.push(setRadio(pair));
					case 'selectedMarkings':
					case 'selectedBreeds':
					case 'selectedAges':
						return;		// These are constant
					case 'start':
					case 'keep_start':	// HACK
					case 'count':
					case 'before':
					case 'after':
						return elts.push({ id: pair.key, value: pair.value });
					case 'debug':
						return elts.push({ id: pair.key, value: 1 });
					default:
						alert('Unexpected elt in setToolbox: ' + pair.key);
				}
			});
		return elts;
	}
	
	// Select, no multiple
	function setSelect(pair) {
		var elt = $(pair.key);
		var option = $A(elt.options).find(function (elt) {
				return elt.value == pair.value;
			});

		option.selected = true;
		return elt;
	}
	
	function setSelectMultiple(pair) {
		var elt = $(pair.key);
		var values = pair.value.split(',');

		$A(elt.options).each(function (option) {
				option.selected = values.include(option.value);	
			});
		return elt;		
	}
	
	function setRadio(pair) {
		var elt = $('toolbox')[pair.key];

		if (elt.length == undefined) field = new Array(field);
		return $A(elt).find(function (option) {
				if (option.value == pair.value)
					return (option.checked = true);
				else
					return false;
			});
	}
		
	function gotoPage(page) {
		if (page<1 || page>Current.npages) {
			//alert('Page out of range: ' + page);
			return;
		}

		// Execute with new page info
		//alert('gotoPage: ' + page);
		Current.filter.exec({ id: 'start', value: (page-1)*Current.CPP }, debug);
		// Note that we don't have to much with the filter-mesg here, as this is
		// only a page change.
	}

	// User clicked on a specific cat.  Set cookie with current filter value for clean return
	function catHref(event) {
		SetCookie('last-filter', Current.filter.serialize() + '&keep_start=' + Current.start,
					new Date((new Date()).getTime()+60*5*1000));	// just 5 minutes
	}

	function controlTab(event/* might be nuill */, state/* 'open', 'close' */) {
		if (state && state=='open' && $('toolbox').visible())
			return;
		if (state && state=='close' && !$('toolbox').visible())
			return;
		$('toolbox', 'toolbox-closed').invoke('toggle');
		if ($('toolbox').visible() && !controlTab.opened) {
			controlTab.opened = true;
			pageTracker._trackEvent('adoptable.php', 'options', 'open');
		}
	}
	
	controlTab.opened = false;	// Hasn't been opened yet
	
	function clearCats() {
		// Clear reset images & clear other info
		$('cats').select('tr.row img, tr.row a, tr.row div.cat-name, tr.row div.cat-age, \
							tr.row div.cat-desc, tr.row div.sn').each(function (elt) {
								switch (elt.tagName) {
									case 'A':
										elt.href='#';
										break;
									case 'IMG':
										if (!elt.hasClassName('adopted'))
											elt.show().src = 'images/petrified/ajax-waiting.gif';
										else
											elt.remove();		// ADOPTED overlay
										break;
									case 'DIV':
										if (elt.hasClassName('sn'))
											elt.remove();
										else
											elt.update();									
								}
				});  // select
	}
	
	function href(cat) {
		return 'cat-desc.php?id=' + cat['Id'];
	}
	
	var option = new Template('<option value="#{index}" #{selected}>#{index}</option>');

	function setPageInfo(info) {
		var noun = 'cats';

		Current.nrecs = info['total-records'];
		$('npages').update((Current.npages = Math.ceil(Current.nrecs/Current.CPP)) + ' ');
		if (Current.nrecs == 1) noun = 'cat';
		$('total-records').update(Current.nrecs + ' ' + noun);
		Current.page = Math.floor(info['first-record']/Current.CPP)+1;
		
		// Adjust prev & next page ctls as necessary
		$$('.prev-page, .next-page').each(function (ctl) {
				if ((ctl.hasClassName('prev-page') && Current.page==1)
						|| (ctl.hasClassName('next-page') && Current.page==Current.npages))
					ctl.down('a').addClassName('off');
				else
					ctl.down('a').removeClassName('off');
			});

		// Set up page select control
		$('current-page').update($R(1, Current.npages).inject('',
			function (acc, n) {
				return acc + option.evaluate({index: n, selected: (n==Current.page ? 'selected' : '')});
			}));
	}
	
	function updateCats(event) {
		var result = event.memo;	// JSON returned by Ajax & passed by fire statement
							// 1st elt is info object, remainder are cat objects

		// Alert to # cats retrieved
		document.fire('exec:cats', result[0]['total-records']);

		var cells = $('cats').select('tr.row td');	// Each cell holds cat info
		var entries = cells.zip(result.slice(1));	// [[cell, (cat | undefined)], ...]
		
		// Set page info
		setPageInfo(result[0]);
		
		// Debugging mode?
		if (debug) {
			//alert(result[0]['query']);
			$('query').show().update(result[0]['query']);
		}
		
		//clearCats();	// Now down much earlier
		entries.each(function (entry) {
				if (!entry[1]) {
					// No cat for this cell
					//entry[0].select('div.cat')[0].hide();
					entry[0].select('img')[0].hide();
					return;
				}

				var cat = entry[1], cell = entry[0];
		
				// Set image(s)
				var img = cell.select('img')[0];
				img.src = 'imageresize3.php/' + cat['photo'] + '/95/100';
				img.up('a').href = href(cat);
				if (cat['state'] == 'adopted')
					cell.select('div.cat')[0].insert("<img src='../images/petrified/adopted.gif' class='adopted' />",
							{ position: 'top' });
				// Now do name & other always-present info
				cell.select('div.cat-name')[0].update('<a href="../' + href(cat)
								+ '">' + cat['Name'] + '</a>');
				cell.select('div.cat-age')[0].update(cat['age']);
				cell.select('div.cat-desc')[0].update(cat['personality']);
				if (cat['specialneeds'] == 'Y')
					cell.select('div.desc')[0].insert('<div class="sn">SPECIAL NEEDS</div>');
			});  // each
	}
	
	function changeHandler(event) {
		// Pup up a waiting pic in case we're slow
		pageTracker._trackEvent('adoptable.php', 'click', Event.element(event).id);
		Current.filter.exec(Event.element(event), debug);
		setFilteredMesg();
	}
	
	function emailAddressInput(event) {
		var elt = Event.element(event);
		
		event.stop();
		if (elt._cleared) return;
		elt.setStyle({fontStyle: 'normal'}).clear()._cleared = true;
	}
	
	function saveFilter(event) {
		event.stop();
		var mesg = Current.filter.toText();

		if (confirm('Please confirm your filter settings:\n' + mesg)) {
			SetCookie('default-filter', Current.filter.serialize(),
				new Date((new Date()).getTime()+1000*60*31000));
			defaultButtons('enable');
		}
	}
	
	var noCatsTip;	// Global told elt to display "NO CATS" popup
	
	function setHandlers() {
		// Attach an observer to each #toolbox form element
		var clicked = changeHandler;
		$('toolbox').getElements().each(function (elt) {
					if (elt.hasClassName('no-handler')) return;
					if (elt.tagName=='INPUT' && elt.type=='radio')
						elt.observe('click', clicked);
					else
						elt.observe('change', clicked);
				});  // getElements
				
		$('filter-email').observe('click', emailAddressInput)
		$('set-registered-filter', 'get-registered-filter',
		  	'delete-registered-filter').invoke('observe', 'click', filterEmail);
		$('save-search').observe('click', saveFilter);
		$('filter-closed', 'filter-open').each(displayFilter);
	}
	
	function setTips() {
		/* GLOBAL */ noCatsTip = $('cats').down('div.cat', 2);
		var options = {
			closeButton: true,
			style: 'darkgrey',
			width: '200px',
			fixed: true,
			hideAfter: 10,
			showOn: false,
			offset: { x: -55, y: -20 }
		};
		new Tip(noCatsTip, '<div id="no-cats">NO CATS MATCH THIS FILTER</div>', options);
		document.observe('exec:cats',
			function (event) {
				if (event.memo == 0)
					noCatsTip./*show().*/prototip.show();
				else if (noCatsTip.visible())
					noCatsTip.prototip.hide();
			});
	}
	
	function displayFilter(elt) {
		var options =  {
			title: 'Current Filter',
			offset: {x: -250, y: -50},
			className: 'protogrey',
			fixed: true,
			style: 'darkgrey',
			hideOn: { element: 'target', event: 'mouseout' }
		};

		new Tip(elt, '<div id="' + elt.id + '-tip"></div>', options);
		elt.observe('prototip:shown',
			function (event) {		
				var tipElt = $(elt.id + '-tip');
				if (tipElt)
					tipElt.update(Current.filter.toText(true));
			});
	}
	
	function filterEmail(event) {
		event.stop();
		var addr = $F('filter-email');
		if (!validEmailAddr(addr)) {
			alert('Enter a valid email address');
			return;
		}
		
		pageTracker._trackEvent('adoptable.php', 'filter', Event.element(event).id);
		switch (Event.element(event).id) {
			case 'set-registered-filter':
				return registerEmail(addr);
			case 'get-registered-filter':
			case 'delete-registered-filter':
				return sendFilterRequest(addr);
			default:
				jsError('Unknown button in filterEmail: ' + Event.element(event).id);
				AjaxErrorAlert();
		}
	}

	function registerEmail(addr) {
		// See if there is a filter set
		if (!Current.filter.isFiltered()) {
			if (!confirm('You have not set any filtering.  Are you sre this is what you want?'))
				return;
		}

		// Send via Ajax to save info
		var defaults = Current.filter.getDefaults();
		var active = Current.filter.getFilter()
		var filter = $H(defaults).merge(active);

		filter.unset('start');	// Current page is not part of filter
		filter.unset('count');	// Either is count
		filter.unset('sort');	// Or sort(?)

		var qs = filter.toQueryString();
		var parms = { email: addr, filter: qs };
		var options = { method: 'get', parameters:  parms };

		options.onSuccess = function (response) {
			alert('Filter registered.  You will receive confirmation email.');
		};
		options.onFailure = function (response) {
			jsError('Error during filter registration: ' + response.statusText);
			AjaxErrorAlert();
		};
		new Ajax.Request('/filters/register.php', options);
	}
	
	function sendFilterRequest(addr) {
		var options = { method: 'get', parameters: { email: addr } };

		options.onSuccess = function (response) {
			var mesg = 'Email has been sent to your address with instructions.';
			if (response.responseJSON) mesg = response.responseJSON['mesg'];
			alert(mesg);
		};
		options.onFailure = function (response) {
			jsError('Error during filter request: ' + response.statusText);
			AjaxErrorAlert();
		};
		new Ajax.Request('/filters/request.php', options);
	}

	function AjaxErrorAlert() {
		alert('An error was encountered processing this request.  '
				+ 'Email has been sent to the webmaster.  We apologize for the inconvenience.');
	}

	function setFilteredMesg(state) {
		var mesg;

		if (state || Current.filter.isFiltered())
			mesg = 'FILTER ON';
		else
			mesg = 'no filter';
		$('filter-mesg-open', 'filter-mesg-closed').invoke('update', mesg);
	}
