var Golin = (function () {
	return Sammy('#main', function() {		
		var App = this,
		
			init = function () {
				//Turn regular URLs into #!/ routes
				if (location.href.indexOf('#!/') === -1) {
					location.href = location.href.replace(CONFIG.root, CONFIG.root + '/#!');
				}
					
				//Map routes to controller
				//TODO: Should just use Sammy's method--this one doesn't let you use regular expressions.
				createRoutes('get', {
					'#!/':									'home',
					'#!/search/:query/':					'search',
					'#!/tags/:tag/':						'tag',
					'#!/:slug/':							'page',
					'#!/:parent/:slug/':					'page',
					
					//Really only happens with revisions:
					'#!/:grandparent/:parent/:slug/':		'page',
					
					//Handle everythingSlider stuff
					'#!/:slug/&:sldr':						'slider',
					'#!/:parent/:slug/&:sldr':				'slider',
					
					//Fix for an unfortunate Firefox bug
					'#%21/tags/:tag/':						'tag'
				});
				
				bindEvents(events);
				setupUI();
				loadGoogleAnalytics();
			},
		
			//Mapped to by createRoutes. Naming convention is {request method}_{action}
			controllers = {
				get_home: function (context) {
					api('/golin/home', {}, function (data) {
						App.trigger('load_tiles', data);
					});
					
					setBodyID('home');
				},
				get_page: function (context) {
					var slug = this.params.slug;
					
					//Wordpress requires parent in slug as well
					if (this.params.parent) {
						slug = this.params.parent + '/' + slug;
					}
					if (this.params.grandparent) {
						slug = this.params.grandparent + '/' + slug;
					}
					
					//If tile has children, display as tiles, otherwise it's content meant for the lightbox.
					api('/golin/tile', {
						slug: slug
					}, function (data) {
						if (data.tile.children.length > 0) {
							App.trigger('load_tiles', { status: data.status, tiles: data.tile.children } );
						} else {
							App.trigger('load_content', data);
						}
						
						App.trigger('receive_page', data);
					});
					
					setBodyID(this.params.slug);
					
				},
				get_search: function (context) {
					api('/golin/search', {
						search: this.params.query
					}, function (data) {
						App.trigger('load_tiles', data);
					});
					
					setBodyID('results');
				},
				get_tag: function (context) {					
					api('/golin/tagged', {
						t: this.params.tag
					}, function (data) {
						App.trigger('load_tiles', data);
					});
					
					setBodyID('tag');
				},
				get_slider: function (context) {
					if ($('#fancybox-content .anythingSlider').length === 0) {
						this.redirect('#!/' + location.href.split('#!/')[1].split('&')[0]);
					}
				}
			},
			
			//Takes care of grabbing the templates remotely; uses callback functions in case
			//templates are still loading when they're called on.
			views = (function () {
			
				//View object hides the remote calls needed to grab templates
				var View = function (name) {
						var def = jQuery.Deferred(),
							render;
						
						$.get(CONFIG.templates + '/' + name + '.hb', function (template) {
							render = Handlebars.compile(template);
							def.resolve();
						});
						
						return {
							render: function (data, callback) {
								def.done(function () {
									callback(render(data));
								});
								return this;
							}
						};					
					},
					views = {};
					
				return {
					get: function (name) {
						if (typeof views[name] === 'undefined') {
							views[name] = new View(name);
						}						
						return views[name];
					}
				};
			}()),
			
			//Events used internally in the app, you can add more here or bind later.
			events = {
				init: function () {
					//Preload the views
					views.get('tiles');
					views.get('content');
					views.get('content_tags');
					views.get('search');
				},
				load_tiles: function (e, data) {
					var viewName;
					if (data.search) {
						viewName = 'search';
					} else {
						viewName = 'tiles';
					} 
					$('body').scrollTop(0);
					views.get(viewName).render(data, replaceContent);
				},
				load_content: function (e, data) {
					var viewName;
					if (data.tile.tags.length > 0) {
						viewName = 'content_tags';
					} else {
						viewName = 'content';
					}
					
					App.trigger('before_new_content');
					views.get(viewName).render(data.tile, function (content) {
						$.fancybox(filters.runEach('content', content), $.fancybox.golinSettings);
						filters.runEach('lightbox_after', $('#fancybox-content'));
						App.trigger('after_new_content');
					});
				},
				close_lightbox: function () {
					var storedLocation = $('#main').data('location');
					
					//Let the browser return us to previous state
					if (storedLocation) {
						history.back();
					}
					//This means the user came here directly or refreshed the page. Go to the parent
					else {
						location.href = location.href.replace(/[^\/]+\/?$/, '');
					}
				},
				error: function (e, data) {
					console && console.log(e, data);
				}		
			},
			
			filters = (function () {
				var filters = {
						content: {
							h1_override: function (content) {
								var $content = $(content),
									$h1s = $content.find('h1');				

								//User can override the page title with an h1 tag
								if ($h1s.length > 1) {
									$h1s.eq(0).html($h1s.eq(1).html());
									$h1s.eq(1).remove();
								}
								
								return runEach('lightbox_init', $content).html();
							}
						},
						lightbox_init: {
							twitter: function ($content) {						
								//Assign current page location to Twitter share iframe
								$('.sharing .twitter iframe', $content).attr('src', 'http://platform1.twitter.com/widgets/tweet_button.html?_=1307545039072&amp;count=horizontal&amp;id=twitter_tweet_button_2&amp;lang=en&amp;original_referer=http%3A%2F%2Fdgolinharris.com&amp;text=Tweet%20Button%20%7C%20dev.twitter.com&url=' + encodeURIComponent(location.href));
								return $content;
							},
							facebook: function ($content) {
								$('.sharing .facebook iframe', $content).attr('src', 'http://www.facebook.com/plugins/like.php?href&send=false&layout=button_count&width=100&show_faces=false&aaction=like&acolorscheme=light&font&height=21&href=' + encodeURIComponent(location.href));
								return $content;
							},
							email: function ($content) {
								$('.sharing .email a', $content).attr('href', 'mailto: ?subject=Golin Harris Site&body=Check this out: ' + location.href);
							}
						},
						lightbox_after: {
							slideshow: function ($content) {
								$('.slideshow', $content).anythingSlider($.anythingSlider.golinSettings);
								return $content;
							},
							plusone: function () {
								gapi.plusone.go('fancybox-content');
							}
						}
					},
					addFilter = function (type, name, fn) {
						if (typeof filters[type] === 'undefined') {
							filters[type] = {};
						}
						filters[type][name] = fn;
						return this;
					},
					runFilter = function (id, content) {
						var split = name.split('.'),
							type = split[0],
							name = split[1];
						return filters[type][name](content) || content;
					},
					runEach = function (type, content) {
						var k,
							filtered;
						for (k in filters[type]) {
							filtered = filters[type][k](content);
						}
						return filtered || content;
					};
					
				return {
					addFilter: addFilter,
					runFilter: runFilter,
					runEach: runEach
				};
			}()),
			
			Header = (function () {
			
				var $header = $('#header'),
					$expander = $('.btn_expand', $header),
					$headline_container = $('#headline'),
					$content = $('.headline_content', $headline_container),
					
					contentHeight = $content.innerHeight(),
					
					heightFunction = function (how) {
						var height = { expand: contentHeight, collapse: 0 }[how],
							eventName = how + '_header',
							classMethod = { expand: 'addClass', collapse: 'removeClass' }[how];
							
						return function (callback) {						
							$headline_container.stop().animate({
								height: height + 'px'
							}, 500, function () {
								$header[classMethod]('expanded');
							}).css({
								overflow: 'visible'
							});							
							App.trigger(eventName);
							
							return this;
						};
					},
					expand = function () {
						var fn = heightFunction('expand');
					
						fn();
						$header.removeClass('expanded');				
						$content.css({
							display: 'block',
							opacity: 0,
							height: '0px'
						}).animate({
							opacity: 1,
							height: contentHeight + 'px'
						}, 700, function () {
							$(this).css('opacity', '');
						});
						
						return false;
					},
					collapse = function () {
						var fn = heightFunction('collapse');
						
						$content.animate({
							opacity: 0,
							height: '0px'
						}, 200, function () {
							//Reset any embeds
							$content.find('iframe').each(function () {
								var $this = $(this),
									src = $this.attr('src');
									
								$this.attr('src', '').attr('src', src);
							});
						});
						
						fn();
						
						return false;
					},
					
					toggle = function () {
						return $header.hasClass('expanded')? collapse(): expand();
					};
				
				App.bind('before_new_content', function () {
					if ($('#main').data('location')) {
						collapse();
					}
				});
				
				$expander.click(toggle);
				
				if (CONFIG.headline_duration > 0) {
					window.setTimeout(collapse, CONFIG.headline_duration);
				}

				return {
					expand: expand,
					collapse: collapse,
					toggle: toggle
				};
				
			}()),
			
			History = (function () {
				var $queued,
					$history = $('#footer #history'),
					$items = $('.items', $history),
					$expander = $('.btn_expand', $history),
					$qty = $history.find('h2 .qty'),
					clickWasTile = false,
					
					baseHeight = $history.height() - parseInt($history.css('padding-top')) * 2,
				
					Item = function (thumb, href, title, classes) {
						var $item = $('<li />').addClass('item'),
							$a = $('<a />').attr({
								href: href,
								title: title
							}).addClass(classes),
							$img = $('<img />').attr({
								src: thumb || CONFIG.templates + '/images/tile-default.gif'
							});
							
						$a.append($img).appendTo($item).append('<span class="bar"/>');
						return $item;
					},
					
					queue = function (thumb, href, title, classes) {
						var $hTiles = $items.find('.item'),
							$future,
							$existing = $hTiles.find('a[href="' + href + '"]').parent();
							
						if (clickWasTile && $queued) {
							$future = $items.find('.future');
							
							//If the use selects a history item then navigates around the site, delete future items
							if ($future.length > 0) {
								$queued.before($future);
							} else {
								$queued.prependTo($items);
							}
							
							//Limit to 8 items
							$hTiles.slice(7).remove();
							$qty.text('(' + $items.children().length + ')');
							adjust();
						}
						
						//If item is already in history, shift it to front of history instead of duplicating it
						if ($existing.length > 0) {
							$queued = $existing.eq(0);
							$existing.slice(1).remove();
						} else {
							$queued = new Item(thumb, href, title, classes);
						}
						
						return this;
					},
					
					expand = function () {
						$history.stop().animate({
							height: baseHeight + $items.outerHeight() + 20 + 'px'
						}, 300).addClass('expanded').css({
							overflow: 'visible'
						});
						
						App.trigger('expand_history');
						return this;
					},
					
					collapse = function () {
						$history.stop().animate({
							height: baseHeight + 'px'
						}, 300).removeClass('expanded').css({
							overflow: 'visible'
						});
						
						App.trigger('collapse_history');
						return this;
					},
					
					toggle = function () {
						return $history.hasClass('expanded')? collapse(): expand();
					},
					
					adjust = function () {
						if ($history.hasClass('expanded')) {
							expand();
						}
						return this;
					},
					
					showTooltip = function () {
						var $li = $(this),
							title = $li.find('a').attr('title'),
							$tooltip = $li.find('.tooltip');
							
						if ($tooltip.length === 0) {
							$tooltip = $('<span class="tooltip"></span>').html(title).append('<span class="pointy" />');
							$tooltip.appendTo($li);
						}
						
						$tooltip.css({
							opacity: 0,
							display: ''
						}).stop().fadeTo(300, 1);
						
						return false;
					},
					
					hideTooltip = function () {
						$(this).find('.tooltip').stop().fadeTo(300, 0, function () {
							$(this).css('display', 'none');
						});
					};
					
				//Keep the height locked until it's triggered
				$history.css('height', baseHeight + 'px');
				$expander.add($history.find('h2')).click(function () {
					toggle();
					return false;
				});
				
				$('#footer #history .item a').live('click', function () {
					var $item = $(this).closest('.item');
					$item.prevAll().addClass('future');
					$item.removeClass('future').nextAll().removeClass('future');
					clickWasTile = false;
				});
				
				$('#main .tile a').live('click', function () {
					$('#footer #history .future').remove();
					clickWasTile = true;
				});
				
				$('#primary_nav a').live('click', function () {
					clickWasTile = true;
				});
				
				$('#history .item').live('mouseenter', showTooltip).live('mouseleave', hideTooltip);
				
				//Queue page after it's received by api
				App.bind('receive_page', function (e, data) {
					queue(data.tile.thumbnail, '#!/' + data.tile.href, data.tile.title, data.tile.classes);
				});
			
				return {
					queue: queue,
					expand: expand,
					collapse: collapse,
					toggle: toggle,
					adjust: adjust
				};
				
			}()),
			
			Contact = (function () {
				$('.tile_contact-us form').live('submit', function () {
					var $form = $(this),
						def = new jQuery.Deferred();
						
					$('input[type=text]', $form).each(function () {
						var $this = $(this).focus(),
							$hidden;
						if ($this.val() == '') {
							$hidden = $('<input type="hidden" value="" />');
							$this.after($hidden).appendTo('body').hide();
							def.done(function () {
								$hidden.before($this.show()).remove();
							});
						}
					});
					
					$.fancybox.showActivity();
					$.post($form.attr('action'), $form.serialize(), function (response) {
						var errors = response._wpcf7_validation_errors;
						if (errors) {
							$.each(errors.messages, function (k, v) {
								var $wrapper = $form.find('.' + k);
								$wrapper.find('.error').remove();
								$wrapper.append($('<p class="error" />').html(v));
							});
							$.fancybox.hideActivity();
						} else {
							$form.closest('.page_content').html('<h2>Thank you!</h2>');
							window.setTimeout(function () {							
								$.fancybox.close();
								$.fancybox.hideActivity();
							}, 1000);
						}
					}).fail(function () {
						$.fancybox.close();
						$.fancybox.hideActivity();
					});
					
					def.resolve();
					return false;
				});
				
				return {};
			}()),
			
			//Wrapper method for using API; includes caching of responses mapped to their paths.
			api = (function () {
				var _cache = {};
				return function (path, data, callback) {
					var args = [];
					$.each(data, function (k, v) {
						args.push(k + '=' + v);
					});
					if (args.length > 0) {
						path += '?' + args.join('&');
					}
					if (typeof _cache[path] !== 'undefined') {
						callback(_cache[path]);
					} else {
						$.fancybox.showActivity();
						$.getJSON(CONFIG.root + '/api' + path, function (data) {
							if (data.error) {
								App.trigger('error', data);
							} else {
								_cache[path] = data;
								callback(data);
							}
						}).always(function () {
							$.fancybox.hideActivity();
						});
					}
				}
			}()),
			
			//Makes sure content isn't already loaded into page, fades out current content, then replaces it with new stuff.
			replaceContent = function (content) {
				var $main = $('#main');
				
				if ($main.data('location') != location.href) {
					App.trigger('before_new_content');
					$main.fadeTo(400, 0, function () {
						var $this = $(this);
						$this.html(content).fadeTo(400, 1);
						App.trigger('after_new_content');
					}).data('location', location.href);
				}
				
				//Close the lightbox
				$.fancybox.close();
			},
			
			//A helper method that takes a route type (get, post, put, delete), and a map. Keys correspond to routes, values to controllers.
			createRoutes = function (type, map) {
				$.each(map, function (route, controllerName) {
					controllerName = type + '_' + controllerName;
					App[type](route, function () {
						var controller = controllers[controllerName],
							ret = controller.apply(this, arguments);
						this.controllerReturn = ret;
						App.trigger(controllerName, this);
						return ret;
					});
				});
			},
			
			//Sets body ID to style elements against
			setBodyID = function (id) {
				$('body').attr('id', id);
			},
			
			//Binds a map of events instead of one at a time
			bindEvents = function (map) {
				$.each(map, function (k, fn) {
					App.bind(k, fn);
				});
			},
			
			//Makes sure Analytics script is loaded before calling in GA plugin
			loadGoogleAnalytics = function () {
				var track = function () {
						_gaq && _gaq.push(['_trackPageview', location.href]);
					};
					
				bindEvents({
					get_home:		track,
					get_page:		track,
					get_search:		track,
					get_tag:		track
				});
			},
			
			showTagHeader = function (name) {
				var id = 'current_tag',
					$tag = $('<div><a class="close">x</a> <span class="label"></span></div>').attr('id', id),
					$label = $tag.find('.label').html(name);
				
				//Remove currently shown tag
				$('#' + id).fadeTo(200, 0, function () {
					$(this).remove();
				});
				
				//Add current tag
				$tag.css({
					opacity: 0
				}).prependTo($('#main')).fadeTo(200, 1);
			},
			
			setupUI = function () {
				$.extend($.fancybox, {
					lastPage: null,
					golinSettings: {
						width: 920,
						height: 620,
						scrolling: 'auto',
						autoDimensions: false,
						overlayColor: '#111',
						overlayOpacity: .8,
						onCleanup: function () {
							if ($.fancybox.lastPage == location.href) {
								App.trigger('close_lightbox');
							}
							$.fancybox.lastPage = null;
						},
						onStart: function () {
							$.fancybox.lastPage = location.href;
						},
						onComplete: function () {
							//Hack for ie7
							$('.ie7 #fancybox-content > div').css({
								'overflow': ''
							}).parent().css({
								'overflow-y': 'scroll'
							}); 
						}
					}
				});
				
				$.extend($.anythingSlider, {
					lastPage: null,
					golinSettings: {
						width          : 400,
						height         : 300,
						toggleControls : true,
						startStopped   : false
					}
				});
				
				$('#search').submit(function () {
					var query = $('.query', this).val();
					if (query.length > 0) {
						location.href = '#!/search/' + encodeURIComponent(query).replace(/%20/g, '+') + '/';
					}
					return false;
				});
				
				$('#current_tag .close').live('click', function () {
					location.href = '#!/';
					return false;
				});
				
				$('#primary_nav li').live('click', function () {
					location.href = $(this).find('a').attr('href');
				});
				
				$('.wpcf7-text, .wpcf7-form-control-wrap textarea').live('focus', function () {
					var $this = $(this),
						title = $this.attr('title');
					
					if (title) {
						if ($this.val() === title) {
							$this.val('');
						}
					} else {
						$this.attr('title', $this.val());
						$this.val('');
					}
					
				}).live('blur', function () {
					var $this = $(this),
						title = $this.attr('title');
					
					if ($this.val() === '') {
						$this.val(title);
					}
				});
									
				$('.anythingSlider a').live('click', function (e) {
					e.preventDefault();
				});
				
			};
			
		try {
			init();
		} catch (e) {
			App.trigger('error', e);
		}
		
	});
	
}());

$(function () {
	Golin.run('#!/').trigger('init');
});

