var viewport_bound_elements 				= new Array();
var fixed_elements 									= new Array();
var fixed_elements_headroom 				= 90;
var fixed_elements_footroom 				= 130;
var keys_pressed 										= new Array(); // array that keeps track of keys pressed

/**
 * Scrolls to the top of the page
 *
 */
function scroll_top()
{
	if(isset('fixed_elements_headroom_buffer'))
	{
		$('body').scrollTop(fixed_elements_headroom_buffer);
	}
	else
	{
		$('body').scrollTop(0);
	}
}

/** 
 * Reposition fixed elements as needed upon scroll
 *
 */
function reposition_fixed_elements()
{
	if(!fixed_elements.length) { return; } // canary
	
	var headroom_buffer = 0;
	
	if(isset('fixed_elements_headroom_buffer'))
	{
		headroom_buffer = fixed_elements_headroom_buffer;
	}

	var headroom = fixed_elements_headroom;
	var footroom = fixed_elements_footroom;
	
	var scrollBottom = scroll_bottom();

	for(var i=0; i < fixed_elements.length; i++)
	{
		if(!$('#'+fixed_elements[i]).length) { continue; }
		
		var element 				= $('#'+fixed_elements[i]);
		var oversized 			= ($(window).height() < (element.outerHeight() + headroom));

		// Element needs fix consideration
		if(headroom_buffer < $(window).scrollTop())
		{
			// Element shorter than viewport
			if(!oversized)
			{ 
				// Element hitting footer
				if(($(window).height() - element.height() - headroom) < (footroom - scrollBottom))
				{
					element.css({ position: 'absolute', bottom: 0, top: 'auto' });
				}
				else
				{
					element.css({ position: 'fixed', top: headroom+'px', bottom: 'auto' });
				}
			}
			// Element taller than viewport
			else
			{
				// Element hitting footer
				if(scrollBottom < footroom)
				{
					element.css({ position: 'absolute', bottom: 0, top: 'auto' });
				}
				// Scroll is far enough down to fix
				else if($(window).scrollTop() - headroom_buffer > (element.height() - ($(window).height()-headroom)))
				{
					element.css({ position: 'fixed', top: (headroom - (element.height() - ($(window).height() - headroom)))+'px', bottom: 'auto' });
				}
				// Scroll is not yet far enough to down to fix
				else
				{ 
					element.css({ position: 'absolute', top: 0, bottom: 'auto' });
				}
			}
		}
		else
		{
			element.css({ position: 'absolute', top: 0, bottom: 'auto' });
		}
	}
}

/** 
 * Resize fixed elements as needed upon viewport resize
 *
 */
function resize_fixed_elements()
{
	if(!viewport_bound_elements.length) { return; } // canary
	
	for(var i=0; i < viewport_bound_elements.length; i++)
	{
		var pieces = viewport_bound_elements[i].split('-');
		var id = pieces[0];
		
		if(pieces[1])
		{
			var bottom_margin = pieces[1];
		}
		else
		{
			var bottom_margin = 300;
		}
		
		if($('#'+id).length)
		{
			$('#'+id).css('maxHeight', ($(window).height() - bottom_margin) + 'px');
		}
	}
}

/**
 * Ensure the height of the middle column in the three column layout
 *
 */
function three_column_layout_heights_ensure()
{ 
	// Ensure height of center column based on sidebars
	if($('#col1').length && $('#col2').length && $('#col3_interior').length)
	{ 
		if($('#col1').height() > $('#col3_interior').height())
		{
			var tallest_height = $('#col1').height();
		}
		else
		{
			var tallest_height = $('#col3_interior').height();
		}

		$('#col2_interior').css({ minHeight: tallest_height + 'px' });
	}
}

var infinite_scroll_interval;
var loading_more_content = false;
var content_params;
var content_container;
var content_url;

/**
 * Initiate infinite scroll checking
 *
 */
function initiate_infinite_scroll()
{
	$('#footer').hide();
	clearInterval(infinite_scroll_interval);
	infinite_scroll_interval = setInterval('check_infinite_scroll()', 300); 
}

/**
 * Halt infinite scroll checking
 *
 */
function halt_infinite_scroll()
{
	$('#footer').show();
	clearInterval(infinite_scroll_interval);
}

/**
 * Checks for readiness to load more content on the page, a la infinite scroll
 *
 */
function check_infinite_scroll()
{ 
	if(loading_more_content) { return; }
	
	var scrollBottom = scroll_bottom();

	var onSuccess = function(data)
	{
		if(loading_more_content)
		{
			$('#'+content_container).append(data.html);
		
			$('#content_loading').hide();
			
			loading_more_content = false;
			
			reposition_fixed_elements();
		}
	}
	
	if(scrollBottom <= infinite_scroll_point)
	{  
		loading_more_content = true;
		
		var params = content_params;
	
		params['page'] = params['page'] + 1;

		$.get(content_url, params, onSuccess);
		
		$('#content_loading').show();
	}
}

/**
 * Selects a vertical tab
 *
 * @param integer tab_id Tab ID
 */
function select_vertical_tab(tab_id)
{
  if(tab_id != selected_vertical_tab_id)
  {
    var tab_left  = $('#tab_left_'+tab_id);
    var tab_right = $('#tab_right_'+tab_id);
  
    $('#tab_left_'+selected_vertical_tab_id).removeClass('vertical_tabs_left_selected');
    $('#tab_right_'+selected_vertical_tab_id).css({ display: 'none' });
  
    tab_left.addClass('vertical_tabs_left_selected');
    tab_right.css({ display: 'block' });
  
    selected_vertical_tab_id = tab_id;
  }
}

/**
 * Register state change callback for when value in address bar changes, typically from back or forward button usage
 *
 * Note: The callback provided should *not* be called when change_state() is called, since it's assumed that any necessary state processing is handled by the caller.
 *
 * This callback is reserved for "detecting" URL or hash changes and processing state changes in reaction
 *
 * @param function Callback function, which will be passed the new state
 */
function register_state_change_callback(callback)
{ 
	// Detect pushState()-based URL changes
	window.onpopstate = function(event) { callback(get_current_state()); };

	// Use the onhashchange event, if available in the browser
	if('onhashchange' in window) 
	{
		window.onhashchange = function () 
		{
			callback(get_current_state());
		}
  }
  // Otherwise, use old school timer method to detect hash change
	else 
	{
		stored_hash = window.location.hash;
		
		window.setInterval(function () 
		{
			if(window.location.hash != stored_hash) 
			{
				callback(get_current_state());
				stored_hash = window.location.hash;
			}
		}, 50);
  }
}

/**
 * Returns the current state, represented by the URL or hash
 *
 * @return string 
 */
function get_current_state()
{
	var url 					= window.location.href;
	var dev_address 	= 'localhost:8081';

	// Special consideration for dev environment testing
	if(url.indexOf(dev_address) != -1)
	{
		var url_pieces = url.split(dev_address);
	}
	// Regular URL parsing
	else
	{
		var url_pieces = url.split($('base').attr('href'));
	}

	// Grab relative URL
	var state = url_pieces[1];

	// Clear relative URL of hash
	if(window.location.hash)
	{
		state = state.replace(window.location.hash, '');
	}

	// If the hash represents a state, use that instead
	var hash = get_hash_value();
	if(hash[0] == '/')
	{
		state = hash;
	}

	return state;
}

/**
 * Returns a given state modified by a given scope and place_id
 *
 * @param string state State
 * @param string scope Scope
 * @param integer place_id Place ID
 */
function get_scoped_state(state, scope, place_id)
{
	var scoped_state = state;
	
	// Return current state if no scope specified
	if(!scope) { return scoped_state; }
	
	// Return current state if local scope but no place_id specified
	if(scope == 'local' && !place_id) { return scoped_state; }
	
	var state_pieces = scoped_state.split('/');

	// Handle homepage state
	if(state_pieces[1] == 'home')
	{
		// Category plans
		if(state_pieces[2] == 'category')
		{
			if(scope == 'local')
			{
				scoped_state = '/home/category/' + state_pieces[3] + '/' + place_id;
			}
			else
			{
				scoped_state = '/home/category/' + state_pieces[3] + '/everywhere';
			}
		}
		// Other plans
		else
		{
			if(scope == 'local')
			{
				scoped_state = '/home/' + state_pieces[2] + '/' + place_id;
			}
			else
			{
				scoped_state = '/home/' + state_pieces[2] + '/everywhere';
			}
		}
	}
	// Handle category page state
	else if(state_pieces[1] == 'category')
	{
		if(scope == 'local')
		{
			scoped_state = '/category/' + state_pieces[2] + '/' + place_id;
		}
		else
		{
			scoped_state = '/category/' + state_pieces[2] + '/everywhere';
		}
	}

	return scoped_state;
}

/**
 * Returns the given state unscoped (ie with scope and place_id removed)
 *
 * @param string state State
 */
function get_unscoped_state(state)
{
	if(!state) { return null; } // canary
	
	var unscoped_state = state;
	
	var state_pieces = unscoped_state.split('/');
	
	// Handle homepage state
	if(state_pieces[1] == 'home')
	{
		// Category plans
		if(state_pieces[2] == 'category')
		{
			unscoped_state = '/home/category/' + state_pieces[3];
		}
		// Other plans
		else
		{
			unscoped_state = '/home/' + state_pieces[2];
		}
	}
	// Handle category page state
	else if(state_pieces[1] == 'category')
	{
		unscoped_state = '/category/' + state_pieces[2];
	}

	return unscoped_state;
}

/**
 * Returns the scope of the given state
 *
 * @param string state State
 * @return Array [scope, place_id]
 */
function get_state_scope_map(state)
{
	var scope_map = new Array();
	
	if(!state) { return scope_map; } // canary
	
	var state_pieces = state.split('/');
	
	// Handle homepage state
	if(state_pieces[1] == 'home')
	{
		// Category plans
		if(state_pieces[2] == 'category')
		{
			if(state_pieces[4] == 'everywhere')
			{
				scope_map['scope'] = 'everywhere';
			}
			else if(state_pieces[4])
			{
				scope_map['scope'] = 'local';
				scope_map['place_id'] = state_pieces[4];
			}
		}
		// Other plans
		else
		{
			if(state_pieces[3] == 'everywhere')
			{
				scope_map['scope'] = 'everywhere';
			}
			else if(state_pieces[3])
			{
				scope_map['scope'] = 'local';
				scope_map['place_id'] = state_pieces[3];
			}
		}
	}
	// Handle category page state
	else if(state_pieces[1] == 'category')
	{
		if(state_pieces[3] == 'everywhere')
		{
			scope_map['scope'] = 'everywhere';
		}
		else if(state_pieces[3])
		{
			scope_map['scope'] 		= 'local';
			scope_map['place_id'] = state_pieces[3];
		}
	}

	return scope_map;
}

/**
 * Returns the plan parameters that are represented by the given state
 *
 * @param string state State
 * @return Array Plan parameters
 */
function get_state_plans_params(state)
{
	var params = {};
	
	if(!state) { return params; } // canary
	
	var state_pieces = state.split('/');
	
	// Handle homepage state
	if(state_pieces[1] == 'home')
	{
		switch(state_pieces[2])
		{
			case 'category':
				params['type'] = 'category';
				break;
				
			case 'all':
				params['type'] = 'categorized';
				break;
				
			case 'friends':
				params['type'] = 'user_subscriptions';
				break;
				
			case 'categories':
				params['type'] = 'category_subscriptions';
				break;
				
			case 'me':
				params['type'] = 'user';
				break;
		}
		
		// Category plans
		if(params['type'] == 'category')
		{
			params['category_slug'] = state_pieces[3];
			
			if(state_pieces[4] == 'everywhere')
			{
				params['scope'] = 'everywhere';
			}
			else if(state_pieces[4])
			{
				params['scope'] 		= 'local';
				params['place_id'] 	= state_pieces[4];
			}
		}
		// Other plans
		else
		{
			if(state_pieces[3] == 'everywhere')
			{
				params['scope'] = 'everywhere';
			}
			else if(state_pieces[3])
			{
				params['scope'] = 'local';
				params['place_id'] = state_pieces[3];
			}
		}
	}
	// Handle category page state
	else if(state_pieces[1] == 'category')
	{
		params['type'] = 'category';
		params['category_slug'] = state_pieces[2];
		
		if(state_pieces[3] == 'everywhere')
		{
			params['scope'] = 'everywhere';
		}
		else if(state_pieces[3])
		{
			params['scope'] 		= 'local';
			params['place_id'] 	= state_pieces[3];
		}
	}

	return params;
}

/**
 * Redirects the page onload given the current hash
 *
 * Note: Legacy hash handling should be removed eventually (maybe after June 2011?) to keep things simple
 *
 * @author Mark Hendrickson
 * @since 3-21-11
 */
function hash_redirect()
{
	var hash = get_hash_value();

	// Redirect using hash as relative URL if it starts with a slash
  if(hash[0] == '/')
  {
    $('body').hide();
    window.location.href = hash;
    return;
  }

	// Otherwise, check for legacy hashes on home and category pages; redirect as necessary
	var url_pieces 	= document.location.href.split('#')[0].split('/'); 
	var hash_pieces = hash.split('/');

	// Redirect category page
	if(url_pieces[3] == 'category')
	{
		if(hash_pieces[0] == 'local' && hash_pieces[1])
		{
			$('body').hide();
			window.location.href = get_base_url() + 'category/' + url_pieces[4] + '/' + hash_pieces[1];
			return;
		}
		else if(hash_pieces[0] == 'everywhere')
		{
			$('body').hide();
			window.location.href = get_base_url() + 'category/' + url_pieces[4] + '/everywhere';
			return;
		}
	}

	// Redirect homepage
	if(url_pieces[3] == '' || url_pieces[3] == 'home')
	{
		// User subscriptions
		if(hash_pieces[0] == 'user_subscriptions')
		{
			var type = 'friends';
		}
		
		// User
		if(hash_pieces[0] == 'user')
		{
			var type = 'me';
		}
		
		// Category subscriptions
		if(hash_pieces[0] == 'category_subscriptions')
		{
			var type = 'categories';
		}
		
		// Category
		if(!type && jQuery.inArray('category', hash_pieces[0]) == 0)
		{
			var type = 'category/' + hash_pieces[0].substr(9);
		}
		
		// Categorized
		if(hash_pieces[0] == 'categorized')
		{
			var type = 'all';
		}
		
		if(type !== undefined)
		{
			$('body').hide();
		
			if(hash_pieces[1] == 'local' && hash_pieces[2])
			{
				window.location.href = '/home/' + type + '/' + hash_pieces[2];
				return;
			}
			else
			{
				window.location.href = '/home/' + type + '/everywhere';
				return;
			}
		}
	}
}

/**
 * Signs user in from the header
 *
 */
function header_sign_in()
{
	// Use custom header sign in callback, if available
	if(typeof header_sign_in_callback == 'function')
	{
		var callback = function() { header_sign_in_callback(); return true };
	}
	else
	{
		var callback = function() { window.location.reload(); return true; };
	}
		
	prompt_toggle(true, 'sign_in', { callback: callback }); 
	
	return false;
}

/**
 * Detects the visitor's location using the JavaScript geolocation API
 * 
 */
function detect_geolocation()
{
	// Prompt for location only if currently scope map is derived from IP address, no session user or cookie
	if(scope_map_source != 'ip' || !navigator.geolocation || navigator.userAgent.indexOf('Chrome') == -1) { return; }

	navigator.geolocation.getCurrentPosition(detect_geolocation_success, detect_geolocation_failure);
	
	//track_event('Geolocation', 'Initiate');
}

/**
 * Handles successful detection of visitor's location (i.e. user permitted detection)
 *
 */
function detect_geolocation_success(position)
{
	//track_event('Geolocation', 'Accept');
	
	var geocoder_success = function(results, status)
	{ 
		if(status != google.maps.GeocoderStatus.OK) { return; } // canary
		
		//track_event('Geolocation', 'Reverse geocode success');
		
		var address_components = results[0]['address_components'];
		var params = {};
		
		// Gather location information into params
		for(var i = 0; i < address_components.length; i++)
		{
			switch(address_components[i]['types'][0])
			{
				case 'locality':
					params['city'] = address_components[i]['long_name'];
					break;
				
				case 'administrative_area_level_1':
					params['state'] = address_components[i]['short_name'];
					break;
					
				case 'country':
					params['country'] = address_components[i]['long_name'];
					break;
			}
		}

		params['latitude'] 	= results[0]['geometry']['location'].lat();
		params['longitude'] = results[0]['geometry']['location'].lng();

		$.get(routes['place_get_by_location'], params, get_by_location_success);
	}
	
	var get_by_location_success = function(data)
	{
		if(!data.place_id) { return; } // canary
		
		//track_event('Geolocation', 'Place identification success');
		
		$('#plans_scope_everywhere').after('<li place_id="' + data.place_id + '" onclick="plans_scope_select(\'local\', $(this).attr(\'place_id\'));">' + data.place_name + '</li>');

		// Change plans scope to reflect detected place, if currently viewing scope-able plans
		if($('#plans_scope_menu').length)
		{
			plans_scope_select('local', data.place_id);
		}
		// Otherwise, just save place ID in cached scope map
		else
		{
			$.post(routes['plan_set_scope_map'], { scope: 'local', place_id: data.place_id });
		}
		
		// Update scope map source variable to reflect geolocation
		scope_map_source = 'geolocation';
	}

	var geocoder 	= new google.maps.Geocoder();
	var latlng 		= new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
	
	geocoder.geocode({ 'latLng': latlng }, geocoder_success);
}

/**
 * Handles unsuccessful detection of visitor's location (i.e. user declined permission for detection)
 *
 */
function detect_geolocation_failure()
{
	//track_event('Geolocation', 'Deny');
}
