
// "use strict";

// Wizard: namespace
var Wizard = {};

// Element: constructor
Wizard.Element = function () {

	this.opts = {
		active: true
	};

	if (arguments.length) {
		this.opts = $.extend({}, this.opts, arguments[0]);
	}

	this.id = this.opts.id || -1;
	this.name = this.opts.name || null;
	this.active = this.opts.active || true;
	this.validator = this.opts.validator;
	
	if ($('#' + this.name).is('select')) {
		this.field_selector = '#' + this.name;
	}
	else {
		var input = $('input', $('#' + this.id));
		
		// The value of these types is an array.	
		if (input.is(':checkbox') || input.is(':radio')) {
			this.field_selector = 'input[name="' + input.attr('name') + '"]';
		}
		else {
			this.field_selector = 'input[name="' + this.name + '"]';			
		}
	}

	if (this.opts.onclick) {
		var input = $('input', $('#' + this.id));
		
		// IE doesn't handle change events correctly for checkbox/radio
		if (input.is(':checkbox') || input.is(':radio')) {
			$(this.field_selector).click(this.opts.onclick);
		}
		else {
			$(this.field_selector).change(this.opts.onclick);
		}
	}
	
	// The Navigator class is responsible for showing it.
	this.hide();
}

// Element: public methods
Wizard.Element.prototype = {
	activate: function () {
		this.active = true;
		if (this.screen.isVisible()) {
			this.show();
		}
	},
	inactivate: function () {
		this.hide();
		this.active = false;
	},
	isActive: function () {
		return this.active;
	},
	hide: function () {
		if (this.active && this.id !== null) {
			$('#' + this.id).hide();
			
		}
	},
	show: function () {
		if (this.active && this.id !== null) {
			$('#' + this.id).show();
		}
	},
	validate: function () {
		if (this.name === null || $('#' + this.id + ':hidden').length) {
			return true;
		}
		return this.validator.element('#' + this.name);
	},
	field: function () {
		return $(this.field_selector);
	},
	val: function () {
		if ($(this.field_selector).length > 1) {
			var vals = [];
			$(this.field_selector + ':checked').each(function () {
				vals.push( this.value );
			});
			if ($(this.field_selector).length > 2) {
				return vals;
			}
			else {
				return vals[0] || '';
			}
		}
		else {
			return $(this.field_selector).val();
		}
	}
};

// Collection: constructor
Wizard.Collection = function () {
	this.opts = {
		active: true
	};

	if (arguments.length) {
		this.opts = $.extend({}, this.opts, arguments[0]);
	}

	this.visible = false;
	this.active = this.opts.active || true;
	this.items = this.opts.items || [];

	if (this.opts.visible === false) {
		this.hide();
	}
}

// Collection: public methods
Wizard.Collection.prototype = {
	activate: function () {
		this.active = true;
	},
	inactivate: function () {
		this.hide();
		this.active = false;
	},
	isActive: function () {
		return this.active;
	},
	countActive: function () {
		var cnt = 0;
		$.each(this.items, function () {
			if (this.isActive()) {
				cnt += 1;
			}
		});
		return cnt;
	},
	add: function () {
		this.items.push(arguments[0]);
	},
	count: function () {
		return this.items.length;
	},
	first: function () {
		return this.items.length < 1 ? false : this.items[0];
	},
	last: function () {
		var last_idx = this.items.length - 1;

		if (last_idx < 0) {
			return false;
		}

		return this.items[last_idx];
	},
	hide: function () {
		if (!this.active) {
			return;
		}
		this.visible = false;
		$.each(this.items, function () {
			this.hide();
		});
	},
	show: function () {
		if (!this.active) {
			return;
		}
		this.visible = true;
		$.each(this.items, function () {
			if (this.opts.beforeshow) {
				this.opts.beforeshow();
			}
		});
		$.each(this.items, function () {
			this.show();
			
			if (this.opts.onshow) {
				this.opts.onshow();
			}
		});
		$.each(this.items, function () {
			if (this.opts.aftershow) {
				this.opts.aftershow();
			}
		});
	},
	isVisible: function () {
		return this.visible;
	},
	validate: function () {
		var valid = true, item_valid = true;

		coll = this;
			
		// Do not stop after one fails so that we show every error message.
		$.each(this.items, function () {
			item_valid = this.validate();
			if (false == item_valid) {
				valid = false;
			}
			if (this.opts.onvalidate) {
				this.opts.onvalidate(coll, this, item_valid);
			}
		});

		if (valid) {
			$.each(this.items, function () {
				if (this.opts.onvalidscreen) {
					valid = this.opts.onvalidscreen( coll, valid, this, this.val() );
				}
			});
		}

		return valid;
	}
};

// Navigator: singleton constructor
Wizard.Navigator = (function () {

	var opts = {},
		curr = -1,
		order = [],
		els = {},
		screens = [],
		form = null,
		btn_prev = '#prev_button',
		btn_next = '#next_button',
		btn_submit = '#submit_button';

	function first() {
		return screens.length < 1 ? false : screens[0];
	}

	function isFirst() {
		return curr === 0;
	}

	function last() {
		var last_idx = screens.length - 1;

		if (last_idx < 0) {
			return false;
		}

		return screens[last_idx];
	}

	function isLast() {
		return curr === screens.length - 1;
	}

	function current() {
		if (arguments.length) {
			curr = arguments[0];
		}

		if (curr === -1 || curr > screens.length - 1) {
			return false;
		}
		return screens[curr];
	}

	function next() {
		if (curr >= screens.length)
		{
			return false;
		}

		curr += 1;

		return screens[curr];
	}

	function prev() {
		if (curr === 0)
		{
			return false;
		}

		curr -= 1;

		return screens[curr];
	}

	function submit() {
		if (form !== null) {
			$(form).submit();			
		}
	}

	function buttons() {
		if (isFirst()) {
			$(btn_prev).hide();
			$(btn_next).show();
			$(btn_submit).hide();
		}
		else if (isLast()) {
			$(btn_prev).show();
			$(btn_next).hide();
			$(btn_submit).show();
		}
		else {
			$(btn_prev).show();
			$(btn_next).show();
			$(btn_submit).hide();
		}
	}

	function hide() {
		if (arguments.length) {
			current(arguments[0]);
		}

		var screen = current();

		if (!screen) {
			return;
		}

		screen.hide();
	}

	function show() {
		if (arguments.length) {
			current(arguments[0]);
		}

		var screen = current();

		if (!screen) {
			return;
		}

		screen.show();

		buttons();
	}

	return {
		init: function () {
			opts = arguments[0] || {};

			if (opts.btn_prev) {
				btn_prev = opts.btn_prev;
			}
			if (opts.btn_next) {
				btn_next = opts.btn_next;
			}
			if (opts.btn_submit) {
				btn_submit = opts.btn_submit;
			}

			// Buttons: bind events to navigation methods
			$(btn_prev).click(Wizard.Navigator.prev);
			$(btn_next).click(Wizard.Navigator.next);
			// Rather than dealing with a separate public method, we'll let the
			// class handle knowing when to submit
			$(btn_submit).click(Wizard.Navigator.next);

			form = opts.form || null;

			order = opts.order || [];

			screens = [];

			els = {};

			var el = {},
				screen = {},
				el_name = '',
				i = 0,
				j = 0;

			for (el_name in opts.fields) {

				els[el_name] = new Wizard.Element({
					name:		el_name,
					id:			opts.fields[el_name].container_id,
					active:     opts.fields[el_name].active,
					onclick:	opts.fields[el_name].onclick,
					onshow:		opts.fields[el_name].onshow,
					aftershow:	opts.fields[el_name].aftershow,
					validator:	opts.validator,
					onvalidscreen: opts.fields[el_name].onvalidscreen,
					onvalidate:	opts.fields[el_name].onvalidate
				});
			}

			// Add Elements to Collections in the right order
			// The 2D order array contains names of elements.
			for (i = 0; i < order.length; i++) {

				if (order[i].length) {

					screen = new Wizard.Collection();

					for (j = 0; j < order[i].length; j++) {

						el = els[order[i][j]];

						if (el) {
							el.screen = screen;
							screen.add(el);
						}
					}

					if (screen.count()) {
						screens.push(screen);
					}
				}
			}

			// Autostep: bind the next action to the LAST element of each screen
			if (opts.autostep) {
				for (i in screens) {
					el = screens[i].last();

					if ($('#' + el.name).is('select')) {
						$('#' + el.name).change(function () {
							Wizard.Navigator.next();
						});
					}
					else if (screens[i].items.length == 1 && el.id !='q_email') {
						// The previous selector is inadequate for radio, etc.
						// Also, if there is only one item on the page, assign click
						// instead of change to handle IE stupidity
						$('input[name="' + el.name + '"]').click(function () {
							Wizard.Navigator.next();
						});
					}
					else {
						$('input[name="' + el.name + '"]').change(function () {
							Wizard.Navigator.next();
						});
					}
				}
			}

			// set the current screen to the first and display it
			show(0);
		},
		prev: function (e) {
			e.preventDefault();

			var screen = current();

			if (!screen) {
				return;
			}

			if (isFirst()) {
				return;
			}

			hide();

			while (1) {
				screen = prev();

				if (screen) {
					if (screen.isActive() && screen.countActive() > 0) {
						break;
					}
				}

			}

			show();

			if (opts.onprev) {
				opts.onprev(coll, curr, order.length);
			}
		},
		next: function (e) {
			if (e) {
				e.preventDefault();
			}

			var screen = current();

			if (!screen) {
				return;
			}

			if (screen.validate() === false) {
				return;
			}

			if (isLast()) {
				submit();
				return;
			}

			hide();
			
			while (1) {
				screen = next();
				
				if (screen) {
					if (screen.isActive() && screen.countActive() > 0) {
						break;
					}
				}
				
				if (!screen || isLast()) {
					submit();
				}
			}
			
			show();

			if (opts.onnext) {
				opts.onnext(coll, curr, order.length);
			}

		},
		element: function (name) {
			return els[name] || null;
		}
	};

}());


