Enjin_AppForm_Conditions = function(cfg) {
	Enjin_AppForm_Conditions.__instances[cfg.preset_id] = this;
	this.init(cfg);
}

Enjin_AppForm_Conditions.__preset_id = null;
Enjin_AppForm_Conditions.__instances = {};
Enjin_AppForm_Conditions.getInstance = function(preset_id) {	
	return Enjin_AppForm_Conditions.__instances[preset_id];
}

Enjin_AppForm_Conditions.prototype = {

	init: function(cfg) 
	{		
		$.extend(this, cfg);

		// all questions/sections are contained in here
		this.form = $('.m_appform-' + this.preset_id);
		
		// find each element that is able to affect the state of modules, and assign a click
		// event to come back here and update state array and implement conditions
		var self = this;
		this.form.find('select').change( function() { self.updateState( $(this).closest('.form-question').attr('hash'), $(this).val() ); } );
		this.form.find('input[type=radio]').click( function() { self.updateState( $(this).closest('.form-question').attr('hash'), $(this).val() ); } );
		this.form.find('input[type=checkbox]').click( function() { 	
																	var val = [];
																	$(this).closest('.form-question').find('input:checked').each( function() { val.push($(this).val()); } );
																	self.updateState( $(this).closest('.form-question').attr('hash'), val ); 
																});
																
		// for now, the form submit will remove the captcha depending on if this is the last															
		// page, otherwise the answer will be posted confusing the server validation
		var self = this;
		this.form.closest('form').submit( function() { if ( self.hasNextPage() ) { $(this).find('#'+self.preset_id+'-captcha').remove(); } return true; } ); 
	}
	
	,updateState: function( hash, value )
	{
		this.state[hash] = value;
		this.applyConditions();
	}
	
	/**
	 * Checks to see if any modules on a specific page are visible
	 * @param $page Page number starting from 1 ... n
	 */
	,isPageVisible: function( page )
	{
		// check each module on this page, return true as soon as we find a visible one
		for ( var i = 0; i < this.pages[page].length; i++ )
		{
			var hash = this.pages[page][i];
			if ( this.modules[hash].visible ) { return true; }
		}
		
		return false;
	}
	
	,hasNextPage: function()
	{
		for ( var page in this.pages )
		{
			if ( page > this.page && this.isPageVisible(page) ) { return true; }
		}
		return false;
	}
	
	,isModuleVisible: function( module )
	{
		// if this is a question, and the section it is on is hidden, then the question
		// default to being hidden
		if ( module.type != 'section' && parseInt(module.section_id,10) > 0 && !this.modules[module.section_id].visible )
		{
			return false;
		}
		
		// if there are no conditions its always visible
		if ( typeof module.conditions === 'object' && $.isArray(module.conditions.items) && module.conditions.items.length )
		{			
			var op = module.conditions.on;
			var action = module.conditions.action;
			var matches = (op == "all" ? true : false);

			// check each conditions, if it is an "all" operation, we need to evaluate every condition
			// if it is an any operation then we can can stop as soon as we find a match
			var items = module.conditions.items;
			for ( var i = 0; i < items.length; i++ )
			{		
				var cond = false;
				var item = items[i].hash;
				var method = items[i].method;
				
				// if it is not filled in yet the assume we keep the default action
				// if the item being referenced is hidden, we ignore it in the compution of the conditions,
				if ( typeof this.state[item] !== 'undefined' && this.state[item] != '' && this.modules[item].visible )
				{
					if ( $.isArray(this.state[item]) )
					{
						// if the method is "is", then the condition is true if one of the values in the state is the same
						// as the value of the choice
						// otherwise, method is "is not" and the condition is only true if none of the values in the state
						// is the the same as the choice
						if ( method == "is" )
						{
							for ( var c = 0; (c < this.state[item].length) && !cond; c++ ) { if ( this.state[item][c] == items[i].choice ) {cond=true;} } 
						}
						else
						{
							cond = true;		// assume the values are all different
							// if one of the values in the state matches the choice, the "is not" condition cannot be satisfied and the condition is false
							for ( var c = 0; (c < this.state[item].length) && cond; c++ ) { if ( this.state[item][c] == items[i].choice ) {cond=false;} } 
						}					
					}
					else
					{
						cond = ( method == "is" ? (this.state[item] == items[i]['choice']) : (this.state[item] != items[i].choice) );
					}	
					matches = ( op == "all" ? matches && cond : matches || cond );
				}	
				else
				{
					// default action is to assume it does not match, because logically
					// items that are action "show" must be hidden initially, and vice versa
					// however, we can only do this if the condition is set to all [AND], in an any [OR]
					// match, this field bot being availabel has no effect on the outcome [as 'anything' OR false is 'anything']
					if ( op == "all" ) { matches = false; }
				}
				
				// if we have an "any" [OR] match, we can stop evaluating conditions as soon as the 
				// match becomes true, as true OR 'anything'  is true
				if ( op == "any" && matches ) { break; }
			}
			
			// show the element if:
			//	1. action is show   and    conditions match
			//				OR
			//	2. action is hide	and	   condtions dont match
			//				AND
			//	3. if item is a questions its section must be visible
			//
			// hide the element if
			//	1. action is hide   and    conditions match
			//				OR
			//	2. action is show   and	   conditions dont match			
			if ( (action == "show" && matches) || (action == "hide" && !matches) )
			{				
				// also show any quetion items with a section matching the id in the event that this
				// was a section being updated
				if ( module.type == 'section' )
				{
					for ( var m in this.modules ) { if ( this.modules[m].section_id == module.section_id ) { this.modules[m].visible = true; }; }
				}
				
				return true;
			}
			else
			{
				// also hide any quetion items with a section matching the id in the event that this
				// was a section being updated
				if ( module.type == 'section' )
				{
					for ( var m in this.modules ) { if ( this.modules[m].section_id == module.section_id ) { this.modules[m].visible = false; }; }
				}				

				return false;
			}
		}
		else
		{
			return true;
		}
	}	
	
	,applyConditions: function()
	{
		// check each element that has conditions now and update its state
		for ( var hash in this.modules )
		{	
			this.modules[hash].visible = this.isModuleVisible(this.modules[hash]);
			
			var el = this.form.find('#form-' + this.preset_id + '-' + hash);
			if ( this.modules[hash].visible )
			{
				el.show();
				
				// also show any quetion items with a section mathcing the id in the event that this
				// was a section being updated
				this.form.find('.form-question[section='+hash+']').show();
			}
			else
			{
				el.hide();
				this.form.find('.form-question[section='+hash+']').hide();
			}
		}

		// work out if there are now any pages to go Next to, if not the show apply button
		// otherwise show the Next button
		// we don't need to change Prev, we can always go backwards
		if ( this.hasNextPage() )
		{
			$('#' + this.preset_id + '-next').show();
			$('#' + this.preset_id + '-submit').hide();
			
			// if there is a next page, then we do not show the captcha yet, this only
			// appears on the last page
			$('#' + this.preset_id + '-captcha').hide();
		}
		else
		{
			$('#' + this.preset_id + '-next').hide();
			$('#' + this.preset_id + '-submit').show();
			
			// last page - show the captche widget
			$('#' + this.preset_id + '-captcha').show();
		}
			
		return;
	}
}
	
