(function()
{
	// plugin definition. This must be defined before it can be extended (with statics etc)
	jQuery.fn.dprRollover = function(settings)
	{
		// merge supplied & default args
		settings = jQuery.extend({
			// TODO: (jm, 9/4/09) defaults
		}, settings);
		
		var $rolloverTargets = this;
		
		$rolloverTargets.each(function ()
		{
			try
			{
				var $rolloverTarget = jQuery(this);
				var $rolloverContainer = jQuery('<div></div>').insertBefore($rolloverTarget);
				var ui = new jQuery.fn.dprRollover.RolloverUI($rolloverContainer, $rolloverTarget);
				ui.Build();
			}
			catch (err)
			{
				// TODO: log
			}
		});
		
		return null;
	}
	
	/******************
	plugin constants
	*******************/
	
	// the default class that the plugin expects rollovers to be decorated with
	jQuery.fn.dprRollover.DefaultClass = "widget-rollover";
	jQuery.fn.dprRollover.CaptionClass = "caption";
	jQuery.fn.dprRollover.NotApplicableClass = "na";
	jQuery.fn.dprRollover.NotApplicableText = "not applicable";
	
	/******************
	RolloverUI - Class
	*******************/
	
	// structure which holds taggable item attributes
	jQuery.fn.dprRollover.RolloverUI = function($container, $sourceElement) 
	{
		/******************
		RolloverUI - constants
		*******************/
		jQuery.fn.dprRollover.RolloverUI.ContainerClass = "rollover-runtime";
		jQuery.fn.dprRollover.RolloverUI.WrapperKey = "wrapper";
		jQuery.fn.dprRollover.RolloverUI.StateKey = "state";
		jQuery.fn.dprRollover.RolloverUI.TriggerClass = "trigger";
		jQuery.fn.dprRollover.RolloverUI.ColumnHeaderClass = "col";
		jQuery.fn.dprRollover.RolloverUI.SelectedTriggerClass = "trigger-selected";
		jQuery.fn.dprRollover.RolloverUI.TriggerRowClass = "triggers";
		jQuery.fn.dprRollover.RolloverUI.PrevTriggerClass = "prev";
		jQuery.fn.dprRollover.RolloverUI.NextTriggerClass = "next";
		jQuery.fn.dprRollover.RolloverUI.CaptionClass = "caption";
		jQuery.fn.dprRollover.RolloverUI.MinStatesForNextPrevInclusive = 4;
		
		/******************
		RolloverUI - statics
		*******************/
		jQuery.fn.dprRollover.RolloverUI.DefaultData = function ()
		{
			return { caption: null, width: null, padding: 0, states: [], columns: [] };
		}
		jQuery.fn.dprRollover.RolloverUI.DefaultState = function ()
		{
			return { label: "Untitled state", data: [], $element: null, $trigger: null };
		}
		jQuery.fn.dprRollover.RolloverUI.DefaultColumn = function ()
		{
			return { label: "Unknown column", data: [] };
		}
		jQuery.fn.dprRollover.RolloverUI.DefaultDatum = function (state, column)
		{
			return { state: state, column: column, src: null, alt: null };
		}
		
		/******************
		RolloverUI - Methods
		*******************/
		
		function /* RolloverUI */ ParseData ()
		{
			// reset
			this.data = jQuery.fn.dprRollover.RolloverUI.DefaultData();
			this.selectedStateIndex = 0;
			
			// columns
			var $columnHeaders = this.$source.find('th[scope="col"]');
			for (var i=1; i<$columnHeaders.length; i++)
			{
				var newColumn = jQuery.fn.dprRollover.RolloverUI.DefaultColumn();				
				this.data.columns.push(newColumn);

				// parse column label
				var columnLabel = jQuery.trim(jQuery($columnHeaders[i]).text());
				if (columnLabel != '')
				{
					newColumn.label = columnLabel;
				}
			}
			
			// states (and data)
			var $stateRows = this.$source.find('tr');
			for (var i=1; i<$stateRows.length; i++)
			{
				var newState = jQuery.fn.dprRollover.RolloverUI.DefaultState();
				
				// parse state label
				var $stateHeaderCell = jQuery($stateRows[i]).find("th");
				if ($stateHeaderCell && $stateHeaderCell.length > 0 && jQuery.trim(jQuery($stateHeaderCell.get(0)).text()) != '')
				{
					newState.label = jQuery.trim(jQuery($stateHeaderCell.get(0)).text());
				}
				
				// parse data
				var $stateCells = jQuery($stateRows[i]).find("td");
				for (var j=0; j<this.data.columns.length; j++)
				{
					var newDatum = jQuery.fn.dprRollover.RolloverUI.DefaultDatum(newState, this.data.columns[j]);
					newDatum.state.data.push(newDatum);
					newDatum.column.data.push(newDatum);
					
					// img
					var $img = jQuery($stateCells[j]).find("img");
					if ($img && $img.length > 0)
					{
						newDatum.src = $img.attr('src');
						newDatum.alt = $img.attr('alt');
					}
				}
				
				
				// add state to collection (only if it has at least one datum)
				var hasData = false;
				for (var j=0; j<newState.data.length; j++)
				{
					if (newState.data[j].src != null)
					{
						hasData = true;
						break;
					}
				}
				if (hasData == true)
				{
					this.data.states.push(newState);
				}
			}
			
			// table properties
			var tableWidth = this.$source.attr('width');
			if (tableWidth != null)
			{
				this.data.width = tableWidth;
			}
			var tableCaption = $columnHeaders.length != 0 ? jQuery($columnHeaders[0]).text() : null;
			if (tableCaption != null && tableCaption != '')
			{
				this.data.caption = tableCaption;
			}
			var tableCellPadding = this.$source.attr('cellpadding');
			if (tableCellPadding != null && tableCellPadding != '')
			{
				this.data.padding = tableCellPadding;
			}
		};
		
		function /* RolloverUI */ Build ()
		{
			$container.html('');
			
			for (var i=0; i<this.data.states.length; i++)
			{
				var state = this.data.states[i];
				
				// create table
				var $element = jQuery('<table cellpadding="' + this.data.padding + '" cellspacing="0" border="0" width="100%" style="display:none;"></table>').appendTo(this.$container);
				
				// create caption row
				if (this.data.columns.length > 1)
				{
					var $captionRow = jQuery('<tr></tr>').appendTo($element);
					for (var j=0; j<this.data.columns.length; j++)
					{
						var $captionCell = jQuery('<th valign="top">' + this.data.columns[j].label + '<br/>' + state.label + (state.data[j].alt != null ? (' ' + state.data[j].alt) : '') + '</th>').appendTo($captionRow);
						$captionCell.addClass(jQuery.fn.dprRollover.RolloverUI.ColumnHeaderClass);
					}
				}
				
				var $elementRow = jQuery('<tr></tr>').appendTo($element);
				for (var j=0; j<state.data.length; j++)
				{
					var datum = state.data[j];
					var $elementCell = jQuery('<td valign="top" width="' + Math.round(this.data.width / this.data.columns.length) + '"></td>').appendTo($elementRow);
					if (datum.src != null)
					{
						jQuery('<img src="' + datum.src + '" alt="' + datum.column.label + ' - ' + datum.state.label + (datum.alt != null && datum.alt != '' ? (' - ' + datum.alt) : '') + '" />').appendTo($elementCell);
					}
					else
					{
						$elementCell.addClass(jQuery.fn.dprRollover.NotApplicableClass);
						$elementCell.html(datum.column.label + '<br/>' + datum.state.label + '<br/>' + jQuery.fn.dprRollover.NotApplicableText);
					}
				}
				state.$element = $element;
			}
			
			// container width
			if (this.data.width != null)
			{
				// width + borders and padding
				this.data.width = Number(this.data.width) + (this.data.columns.length * Number(this.data.padding) * 2) + (this.data.columns.length + 1);
				$container.css({width: this.data.width + 'px'});
			}
			
			// triggers
			var $triggerDiv = jQuery('<div></div>').appendTo(this.$container).addClass(jQuery.fn.dprRollover.RolloverUI.TriggerRowClass);
			var $triggerTable = jQuery('<table cellspacing="0" cellpadding="0" border="0" width="100%"></table>').appendTo($triggerDiv).addClass(jQuery.fn.dprRollover.RolloverUI.TriggerRowClass);
			var $triggerRow = jQuery('<tr></tr>').appendTo($triggerTable);
			
			// prev
			if (this.data.states.length >= jQuery.fn.dprRollover.RolloverUI.MinStatesForNextPrevInclusive)
			{			
				var $triggerCell = jQuery('<td></td>').appendTo($triggerRow).addClass(jQuery.fn.dprRollover.RolloverUI.PrevTriggerClass);
				var $prev = jQuery('<a href="#" title="click to view previous state">prev</a>').appendTo($triggerCell);
				$prev.addClass(jQuery.fn.dprRollover.RolloverUI.TriggerClass);
				$prev.data(jQuery.fn.dprRollover.RolloverUI.WrapperKey, this);
				$prev.click(function (e) 
				{
					var wrapper = jQuery(this).data(jQuery.fn.dprRollover.RolloverUI.WrapperKey);
					wrapper.Move(-1);
					e.preventDefault();
				});
			}
			else
			{
				$triggerRow.css({paddingLeft: '0px', paddingRight: '0px'});
			}
				
			// state triggers (buttons)
			var $triggerCell = jQuery('<td></td>').appendTo($triggerRow);
			for (var i=0; i<this.data.states.length; i++)
			{
				var state = this.data.states[i];
				state.$trigger = jQuery('<a href="#" title="mouse over or click to view ' + state.label + ' data">' + state.label + '</a>').appendTo($triggerCell);
				state.$trigger.addClass(jQuery.fn.dprRollover.RolloverUI.TriggerClass);
				state.$trigger.data(jQuery.fn.dprRollover.RolloverUI.WrapperKey, this);
				state.$trigger.data(jQuery.fn.dprRollover.RolloverUI.StateKey, state);
				state.$trigger.mouseover(function () 
				{
					var wrapper = jQuery(this).data(jQuery.fn.dprRollover.RolloverUI.WrapperKey);
					var stateToDisplay = jQuery(this).data(jQuery.fn.dprRollover.RolloverUI.StateKey);
					wrapper.ShowState(stateToDisplay);
				}).click(function(e)
				{
					// execute on click as well (for mobile UAs that don't do 'mouseover' events)
					var wrapper = jQuery(this).data(jQuery.fn.dprRollover.RolloverUI.WrapperKey);
					var stateToDisplay = jQuery(this).data(jQuery.fn.dprRollover.RolloverUI.StateKey);
					wrapper.ShowState(stateToDisplay);
					e.preventDefault();
				});
			}
			
			
			// next
			if (this.data.states.length >= jQuery.fn.dprRollover.RolloverUI.MinStatesForNextPrevInclusive)
			{			
				var $triggerCell = jQuery('<td></td>').appendTo($triggerRow).addClass(jQuery.fn.dprRollover.RolloverUI.NextTriggerClass);
				var $next = jQuery('<a href="#" title="click to view next state">next</a>').appendTo($triggerCell);
				$next.addClass(jQuery.fn.dprRollover.RolloverUI.TriggerClass);
				$next.data(jQuery.fn.dprRollover.RolloverUI.WrapperKey, this);
				$next.click(function (e) 
				{
					var wrapper = jQuery(this).data(jQuery.fn.dprRollover.RolloverUI.WrapperKey);
					wrapper.Move(1);
					e.preventDefault();
				});
			}
			
			this.ShowState(this.data.states[0]);
			
			// caption
			if (this.data.caption != null)
			{
				var $caption = jQuery('<div class="' + jQuery.fn.dprRollover.RolloverUI.CaptionClass + '">' + this.data.caption + '</div>').appendTo(this.$container);
			}
			
		};
		
		function /* RolloverUI */ ShowState (stateToShow)
		{
			// hide currently selected state
			if (this.selectedState && this.selectedState != stateToShow)
			{
				this.selectedState.$element.css({display: 'none'});
				this.selectedState.$trigger.removeClass(jQuery.fn.dprRollover.RolloverUI.SelectedTriggerClass);
			}
			this.selectedState = stateToShow;
			this.selectedState.$element.css({display: 'block'});
			this.selectedState.$trigger.addClass(jQuery.fn.dprRollover.RolloverUI.SelectedTriggerClass);			
		}
		
		function /* RolloverUI */ Move (delta)
		{
			var selectedIndex = this.GetSelectedStateIndex();
			var newSelectedIndex = (selectedIndex + delta + this.data.states.length) % this.data.states.length;
			this.ShowState(this.data.states[newSelectedIndex]);
		}
		
		function /* Rollover */ GetSelectedStateIndex ()
		{
			for (var i=0; i<this.data.states.length; i++)
			{
				if (this.data.states[i] == this.selectedState)
				{
					return i;
				}
			}
			return 0;
		}
		
		// publicly accessible members
		var wrapper = 
		{
			// fields
			$source: $sourceElement,
			data: jQuery.fn.dprRollover.RolloverUI.DefaultData(),
			selectedState: null,
			$container: $container,
			// methods
			ParseData: ParseData,
			Build: Build,
			ShowState: ShowState,
			Move: Move,
			GetSelectedStateIndex: GetSelectedStateIndex
		};
		// bind publicly accessible members so that dom event handlers can access the wrapper data
		$container.data(jQuery.fn.dprRollover.RolloverUI.WrapperKey, wrapper);
		
		/************************
		RolloverUI - Constructor logic
		*************************/
		
		$container.addClass(jQuery.fn.dprRollover.RolloverUI.ContainerClass);
		wrapper.ParseData();
		
		return wrapper;
	};
	
})(jQuery);