/**
 * Created for Fulton Market Films.
 * 
 * Created by Conrad Akier of Broken Pencils, Inc.
 * http://webreakpencils.com
 * 
 * Those who steal this code will be attacked by rabid pandas and tried in a
 * court before their peers.
 */

/*
 * Special event for image load events
 * Needed because some browsers does not trigger the event on cached images.

 * MIT License
 * Paul Irish     | @paul_irish | www.paulirish.com
 * Andree Hansson | @peolanha   | www.andreehansson.se
 * 2010.
 *
 * Usage:
 * $(images).bind('load', function (e) {
 *   // Do stuff on load
 * });
 * 
 * Note that you can bind the 'error' event on data uri images, this will trigger when
 * data uri images isn't supported.
 * 
 * Tested in:
 * FF 3+
 * IE 6-8
 * Chromium 5-6
 * Opera 9-10
 */
(function ($) {
	$.event.special.load = {
		add: function (hollaback) {
			if ( this.nodeType === 1 && this.tagName.toLowerCase() === 'img' && this.src !== '' ) {
				// Image is already complete, fire the hollaback (fixes browser issues were cached
				// images isn't triggering the load event)
				if ( this.complete || this.readyState === 4 ) {
					hollaback.handler.apply(this);
				}

				// Check if data URI images is supported, fire 'error' event if not
				else if ( this.readyState === 'uninitialized' && this.src.indexOf('data:') === 0 ) {
					$(this).trigger('error');
				}
				
				else {
					$(this).bind('load', hollaback.handler);
				}
			}
		}
	};
}(jQuery));

function preload(arrayOfImages) {
    $(arrayOfImages).each(function(){
        $('<img/>')[0].src = this;
    });
}

jQuery(function(){
	var $menu = $('#menu'),
		$grid = $('#grid'),
		
		$videos = $('.video'),
		$videoLinks = $('.video a'),
		$homeLink = $('#menu h1 a'),
		$menuLinks = $('#menu-options a, #footer-contact'),
		gridHeight = $grid.height(),
		gridWidth = $grid.width(),
		boxHeight = $videos.eq(0).height(),
		boxOuterHeight = $videos.eq(0).outerHeight(true),
		boxWidth = $videos.eq(0).width(),
		boxOuterWidth = $videos.eq(0).outerWidth(true),
		menuOrigWidth = $menu.width(),
		boxVertMargin = boxOuterHeight - boxHeight,
		boxHorizMargin = boxOuterWidth - boxWidth,
		animateSpeed = 400,
		numRows = Math.floor($grid.height() / boxOuterHeight),
		numCols = Math.floor(gridWidth / boxOuterWidth),
		numVideos = $videos.length,
		direction = {negative:'-=', positive:'+='},
		isMenuSlimmed = false,
		currentGridHeight = gridHeight,
		menuHeight = $menu.height(),
		menuHeights = {},
		$menuOptions = $('#menu-options'),
		menuPosition = $menuOptions.position(),
		showingLink = $homeLink.get(0),
		menuLinkData = {},
		videoData = {},
		showVideo,
		isAnimating = false;
	
	var changes = {}
	
	var positioning = new Array(numVideos)
	var videoInfo = {}
	
	var boxes = new Array(numVideos) // worst-case scenario is every video is on it's own row (ie, large contact page)
	var boxRows = boxes.length
	var lastFullRow = numRows - 1
	var isLastRowPartial = false
	var lastBoxRowCol
	
	for (var i = 0; i < boxRows; i++) {
		boxes[i] = new Array(numCols)
	}
	
	var preventDefaultHandler = function(event) {
		event.preventDefault()
	}
	
	$menuLinks.bind('click', preventDefaultHandler)
	$videoLinks.bind('click', preventDefaultHandler)
	$videos.css('z-index', 0)
	
	var menuResizeCallback = function() {
//		$grid.css('height', 'auto')
	}
	
	var slimMenu = function() {
		$menuOptions.hide()
		$menu.animate({height: 2*boxHeight + boxVertMargin, width: boxWidth}, {speed: animateSpeed, complete: function() {
			$menu.addClass('slimmed')
			$menuOptions.removeAttr('style')
			$menuOptions.fadeIn('fast')
		}})
		
		isMenuSlimmed = true
	}
	
	var fatMenu = function() {
		$menuOptions.hide()
		$menuOptions.removeAttr('style')
		$menu.animate({height: boxHeight, width: 2*boxWidth + boxHorizMargin}, {speed: animateSpeed, complete: function() {
			$menu.removeClass('slimmed')
			$menuOptions.fadeIn('fast')
		}})
		isMenuSlimmed = false
	}
	
	var adjustViewport = function(target) {
		var topOffset = $(target).offset().top - boxVertMargin
		if (topOffset < $(window).scrollTop()) {
			$('html,body').animate({scrollTop: topOffset}, 300);
		}
	}
	
	var expandLinkContent = function(link) {
		var name = $(link).text().toLowerCase()
		
		$menuOptions.css({
			bottom: 'auto',
			top: menuPosition.top
		})
		
		moveOutBoxes(menuLinkData[name].move, menuLinkData[name].freeSpaces, menuLinkData[name].numToMove)
		resizeGridForSpaces(menuLinkData[name].freeSpaces, menuLinkData[name].numToMove, true)
		adjustViewport($menu)
		$menu.animate({height: menuHeight+(menuLinkData[name].numRowsToMove*boxOuterHeight)}, {speed: animateSpeed, complete: function() {
			$(menuLinkData[name].contentBox).fadeIn('fast', function() {
				$(link).addClass('active-menu')
				$menu.prepend('<button class="close" title="minimize">minimize</button>')
				$menu.children('button.close').one('click', function(event) {
					closeLinkContent(link, name)
				})
				enableLinks()
			})
		}})
	}
	
	var closeLinkContent = function(link, callback) {
		var name = $(link).text().toLowerCase()
		
		$menu.children('button.close').fadeOut('fast', function() {$(this).remove();})
		$(menuLinkData[name].contentBox).fadeOut('fast', function() {
			$menu.animate({height: menuHeight}, {speed: animateSpeed, complete: function() {
				$(link).removeClass('active-menu')
				$menuOptions.removeAttr('style')
				moveBackBoxes(menuLinkData[name].move, menuLinkData[name].numToMove)
				showingLink = $homeLink.get(0)
				if (typeof(callback) == 'function') {
					callback()
				}
			}})
		})
	}
	
	var showMenuLink = function(link, name) {
		showingLink = link
		
		if ($menu.find('#content-'+name+':first').length == 0) {
			$('<div id="content-container-'+name+'" class="container"></div>').appendTo($menu)
			var href = $(link).attr('href')
			$('<div id="content-'+name+'" class="secondary"></div>')
				.appendTo($('#content-container-'+name))
				.load(href, function(response, status, xhr) {
					if (status == 'error') {
						showingLink = null
						$('#content-container-'+name).remove()
						return
					}
					
					$(response).find('script').each(function() {
						eval($(this).html())
					})
					
					menuLinkData[name] = {}
					menuLinkData[name].numToMove = 0
					menuLinkData[name].contentBox = $('#content-container-'+name)
					var contentHeight = $(menuLinkData[name].contentBox).outerHeight(true)
					
					menuLinkData[name].numRowsToMove = Math.ceil(contentHeight / boxOuterHeight)
					if ((menuLinkData[name].numRowsToMove * boxOuterHeight) % contentHeight <= Math.ceil(boxVertMargin / 2)) menuLinkData[name].numRowsToMove++
					
					var numBoxesToMove = menuLinkData[name].numRowsToMove * 2
					var move = new Array(numBoxesToMove)
					
					for (var i = 0; i < numBoxesToMove; i++) move[i] = null;
					selectBoxesToMove(move, 1, 0, true, menuLinkData[name].numRowsToMove, true)
					
					while (menuLinkData[name].numToMove < numBoxesToMove) {
						if (move[menuLinkData[name].numToMove] == null) break;
						menuLinkData[name].numToMove++
					}
					menuLinkData[name].move = move
					menuLinkData[name].freeSpaces = getFreeSpaces(menuLinkData[name].numToMove, true,	null)
					
					$('#content-container-'+name).hide()
					expandLinkContent(link)
			});
		} else {
			expandLinkContent(link)
		}
	}
	
	var bindVideoHoverIn = function() {
		var $summaryBox = $(this).find('.summary:first')
		$summaryBox.stop(true).animate({top: boxHeight - $summaryBox.outerHeight(true)}, {speed:'fast'})
		if ($videoLinks.index(showingLink) > -1) {
			$(this).closest('.video').stop(true).animate({opacity: 1.0}, {speed:'fast'})
		}
	}
	
	var bindVideoHoverOut = function() {
		var $summaryBox = $(this).find('.summary:first')
		$summaryBox.stop(true).animate({top: boxHeight}, {speed:'fast'})
		if (showingLink != this && $videoLinks.index(showingLink) > -1) {
			$(this).closest('.video').stop(true).animate({opacity: 0.25}, {speed:'fast'})
		}
	}
	
	$videoLinks.bind('mouseenter focusin', bindVideoHoverIn)
	$videoLinks.bind('mouseleave focusout', bindVideoHoverOut)
	
	var positionVideos = function() {
		preload(['img/spinner.gif'])
		
		// make all videos absolutely positioned
		var pos, row, col
		
		$videos.each(function(index) {
			pos = $(this).position()
			row = (pos.top == 0) ? 0 : Math.ceil(pos.top / boxOuterHeight);
			col = (pos.left == 0) ? 0 : Math.ceil(pos.left / boxOuterWidth);
			
			videoInfo[this.id] = {
				'top': pos.top,
				'left': pos.left,
				'row': row,
				'col': col,
				'index': index,
				'offset': $(this).offset()
			}
			
			// mark this box's position as filled by putting the video's id in it
			boxes[row][col] = this.id
			
			positioning[index] = {
				'position': 'absolute',
				'top': pos.top,
				'left': pos.left,
				'float': 'none'
			}
			
			$(this).find('.summary:first').css({bottom: 'auto', top: boxHeight})
		})
		
		$grid.height(gridHeight)
		$videos.each(function(vIndex) {
			$(this).css(positioning[vIndex])
		})
		
		for (var i = 0; i < numCols; i++) {
			if (!boxes[lastFullRow][i]) {
				isLastRowPartial = true
				lastFullRow--
				break;
			}
		}
		
		var lastVideoId = $videos.last().attr('id')
		
		lastBoxRowCol = {
			row: videoInfo[lastVideoId].row,
			col: videoInfo[lastVideoId].col
		}
		
		$grid.css('overflow', 'visible') // so the close button above each video isn't clipped
	}
	
	var shuffle = function(v) {
		for (var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x) {}
		return v;
	};
	
	var getFreeSpaces = function(numSpaces, shuffled, startInCol, startRow) {
		shuffled = typeof(shuffled) != 'undefined' ? shuffled : true;
		startInCol = typeof(startInCol) != 'undefined' ? startInCol : null;
		startRow = typeof(startRow) != 'undefined' ? startRow : lastBoxRowCol.row;
		
		var spaces = new Array(numSpaces)
		var row = lastBoxRowCol.row,
			col = (startInCol != null) ? startInCol : lastBoxRowCol.col + 1;
		
		if (startInCol != null) {
			if (typeof(boxes[row][startInCol]) != 'undefined' && boxes[row][startInCol] != null) { // there is a box there, move to next row
				row++
				col = 0
			}
		}
		
//		alert('startInCol = ' + startInCol + '; startRow = ' + startRow + '; row = ' + row + '; lastBoxRow = ' + lastBoxRowCol.row)
		
		for (var i = 0; i < numSpaces; i++) {
			if (col == numCols) { // item is in last col position, so move to next row
				col = 0;
				row++;
			}
			
			spaces[i] = {row: row, col: col, video: null}
			
			if (startInCol != null) {
				if (row >= startRow) {
					col++
				} else {
					row++
				}
			} else {
				col++
			}
		}
		
		if (shuffled) {
			shuffle(spaces)
		}
		
		return spaces
	}
	
	var selectBoxesToMove = function(move, row, col, moveDown, rows, all) {
		rows = typeof(rows) != 'undefined' ? rows : 3;
		all = typeof(all) != 'undefined' ? all : false;
		
		var altColumn = (col == 0) ? 0 : 2; // answer to: we always use the middle column, but which other column do we use?
		var currentRow = row,
			lastRow = (moveDown) ? (row + rows) : (row - rows);
		var i = 0, moveLength = move.length;
		
		while (i < moveLength) {
			if (move[i] == null) break;
			i++
		}
		
		if (!all) {
			/*
			 * Sometimes the box next to the selected video is empty, which is why
			 * we check to see if it's undefined or null. If it's not, we select it
			 * and increment i, which stores the number of boxes that have been
			 * selected so far.
			 */
			if (col == 0 || col == 2) {
				if (typeof(boxes[currentRow][1]) != 'undefined' && boxes[currentRow][1] != null) {
					move[i] = boxes[currentRow][1]
					i++
				}
			} else if (typeof(boxes[currentRow][2]) != 'undefined' && boxes[currentRow][2] != null) {
				move[i] = boxes[currentRow][2]; // bias towards right column when center is selected
				i++
			}
			
			if (moveDown) {
				currentRow++
			} else {
				currentRow--
			}
		}
		
		if (moveDown) {
			var numEmptyEncountered = 0
			for (; currentRow <= lastRow; currentRow++) {
				
				if (typeof(boxes[currentRow][1]) == 'undefined' || boxes[currentRow][1] == null) {
					numEmptyEncountered++
					
					if (numEmptyEncountered > 1) break;
				} else {
					move[i] = boxes[currentRow][1]
					i++
				}
				
				if (typeof(boxes[currentRow][altColumn]) == 'undefined' || boxes[currentRow][altColumn] == null) {
					numEmptyEncountered++
					if (numEmptyEncountered > 1) break;
				} else {
					move[i] = boxes[currentRow][altColumn]
					i++
				}
			}
		} else {
			for (; currentRow > lastRow; currentRow--) {
				move[i++] = boxes[currentRow][1] // middle col is always included
				move[i++] = (col == 0) ? boxes[currentRow][0] : boxes[currentRow][2]
			}
		}
	}
	
	var resizeGridForSpaces = function(freeSpaces, numToMove, moveDown, callback) {
		var newNumRows = numRows - 1
		
		for (var i = 0; i < numToMove; i++) {
			if (freeSpaces[i].row > newNumRows) 
				newNumRows = freeSpaces[i].row
		}
		
		if (newNumRows > numRows - 1) {
			currentGridHeight = (newNumRows+1)*boxOuterHeight
			$grid.animate({height: currentGridHeight}, {speed: 'fast', complete: callback})
		}
	}
	
	var moveOutBoxes = function(move, freeSpaces, numToMove, afterAnimationsQueued) {
		for (var i = 0; i < numToMove; i++) {
			freeSpaces[i].video = move[i]
			$('#'+move[i]).css('z-index', 25)
			$('#'+move[i]).stop(true, true)
			$('#'+move[i]).animate({
				top: freeSpaces[i].row * boxOuterHeight,
				left: freeSpaces[i].col * boxOuterWidth
			}, {speed: 'fast', complete: function() {$(this).css('z-index', 0)}})
		}
		
		if (typeof(afterAnimationsQueued) == 'function') {
			afterAnimationsQueued()
		}
	}
	
	var insertVideo = function(link, video) {
		// TODO make sure videoData is defined for this link
		$(video).append('<div class="content"></div>')
		var $videoContent = $(video).find('.content:first')
		$videoContent.html(videoData[link.id]['response'])
		videoData[link.id]['content'] = $videoContent
		$videoContent.one('load', function() {
			window.setTimeout(function() { showVideo(link, video); }, 175)
		}).each(function() {
			if (this.complete || this.complete === undefined || (jQuery.browser.msie && parseInt(jQuery.browser.version) == 6)) {
				$(this).trigger('load')
			}
		})
	}
	
	var closeVideo = function(videoLink, callback) {
		showingLink = $homeLink.get(0)
		var video = $(videoLink).closest('.video').get(0)
		$(video).children('button.close').remove()
		$(video).css('overflow', 'hidden')
		$(video).find('.content:first').remove()
		
		var styleChanges = {}
		if (!videoData[videoLink.id].moveDown) {
			styleChanges.top = videoInfo[$(video).attr('id')].top
		}
		styleChanges.height = boxHeight
		styleChanges.width = boxWidth
		$(video).stop(true, true)
		$(video).animate(styleChanges, {speed: animateSpeed, complete: function() {
			$(video).css('bottom', 'auto')
			
			if (videoInfo[$(video).attr('id')].col == 2) {
				$(video).css('left', videoInfo[$(video).attr('id')].left)
			}
			$(video).removeClass('expanded')
			$(video).find('.summary:first').css('top', boxHeight)
			
			if (typeof(callback) == 'function') {
				callback()
			}
		}})
		
		if (videoData[videoLink.id].slimMenu == true) fatMenu()
		
		moveBackBoxes(videoData[videoLink.id].move, videoData[videoLink.id].numToMove, function() {
			$(video).siblings('.video').animate({opacity: 1.0}, 'fast')
		})
	}
	
	var calculateVideoMovements = function(link, video) {
		var contentHeight = $(videoData[link.id]['content']).outerHeight(true)
		var numRowsToMove = Math.ceil(contentHeight / boxOuterHeight)
		if ((numRowsToMove * boxOuterHeight) % contentHeight <= Math.ceil(boxVertMargin / 2)) numRowsToMove++
		var numBoxesToMove = numRowsToMove * 2 + 1
		var move = new Array(numBoxesToMove),
			row = videoInfo[video.id].row,
			col = videoInfo[video.id].col,
			moveDown = true;
		
		videoData[link.id].slimMenu = false
	
		for (var i = 0, l = move.length; i < l; i++) move[i] = null;
		
		if (row <= numRowsToMove || row <= numRows - numRowsToMove) {
			if (row == 0) {
				isMenuSlimmed = true
				//move first box under menu
				move[0] = boxes[1][0]
				videoData[link.id].slimMenu = true
			}
		} else {
			moveDown = false
		}
		
		selectBoxesToMove(move, row, col, moveDown, numRowsToMove-1)
		
		var numToMove = 0
		while (numToMove < numBoxesToMove) {
			if (move[numToMove] == null) break;
			
			numToMove++
		}
		
		var freeSpaces
		if (moveDown && row >= numRows - numRowsToMove) {
			freeSpaces = getFreeSpaces(numToMove, true,
					(col == 0) ? 2 : 0, // startInCol
					row+numRowsToMove-1) // skipRows
		} else {
			if (row == numRows - 1) { // last row
				freeSpaces = getFreeSpaces(numToMove, true,
						(col == 0) ? 2 : 0)
			} else {
				freeSpaces = getFreeSpaces(numToMove, true,	null)
			}
		}
		
		videoData[link.id].move = move
		videoData[link.id].freeSpaces = freeSpaces
		videoData[link.id].numToMove = numToMove
		videoData[link.id].numRowsToMove = numRowsToMove
		videoData[link.id].moveDown = moveDown
		videoData[link.id].height = boxHeight+((videoData[link.id].numRowsToMove-1)*boxOuterHeight)
	}
	
	showVideo = function(link, video) {
		if (!videoData[link.id] || !videoData[link.id].move) {
			calculateVideoMovements(link, video)
		}
		
		$(video).addClass('expanded')
		$(video).css('z-index', 50)
		
		currentGridHeight = gridHeight
		
		var animateVideo = function() {
			if (videoData[link.id].slimMenu == true) {
				slimMenu()
			}
			
			moveOutBoxes(videoData[link.id].move, videoData[link.id].freeSpaces, videoData[link.id].numToMove, function() {
				var cssChanges = {}
				
				if (!videoData[link.id].moveDown) {
					$(video).css('bottom', currentGridHeight - ((videoInfo[$(video).attr('id')].row + 1) * boxOuterHeight))
				}
				
				if (videoInfo[video.id].col == 2) {
					cssChanges.left = 'auto'
					cssChanges.right = 0
				}
				if (!videoData[link.id].moveDown) {
					cssChanges.top = 'auto'
					cssChanges.bottom = currentGridHeight - ((videoInfo[video.id].row + 1) * boxOuterHeight)
				}
				
				$(video).css(cssChanges)
				$(video).stop(true, true)
				$(video).find('.spinner').remove()
				$(video).animate({
					height: videoData[link.id].height,
					width: boxWidth+(boxOuterWidth)
				}, {speed:animateSpeed, complete: function() {
					adjustViewport(video)
					$(video).siblings('.video').animate({opacity: 0.25}, 'fast')
					$(video).find('.content:first').css('top', 0)
					$(video).css('overflow', 'visible')
					enableLinks()
					$(video).prepend('<button class="close" title="Close Video">Close</button>')
					$(video).children('button.close').one('click', function(event) {
						closeVideo(link)
					})
				}})
			})
		}
		
		resizeGridForSpaces(videoData[link.id].freeSpaces, videoData[link.id].numToMove, videoData[link.id].moveDown, animateVideo)
	}
	
	var getVideo = function(link, video) {
		showingLink = link
		if (!videoData[link.id]) {
			$.get($(link).attr('href'), function(response, status, xhr) {
				// TODO if (status == 'error') { }]
				videoData[link.id] = {'response':response}
				insertVideo(link, video)
			})
		} else {
			insertVideo(link, video)
		}
	}
	
	var disableLinks = function() {
		isAnimating = true
		$menuLinks.unbind('click', handleMenuClick)
		$videoLinks.unbind('click', handleVideoClick)
	}
	
	var enableLinks = function() {
		isAnimating = false
		$menuLinks.bind('click', handleMenuClick)
		$videoLinks.bind('click', handleVideoClick)
	}
	
	var handleVideoClick = function(event) {
		if (isAnimating) {
			// we need to wait for animations to complete
			return false
		}
		
		disableLinks()
		var link = this,
			oldShowingLink = showingLink
		
		var video = $(link).closest('.video').get(0)
		$(video).prepend('<div class="spinner"></div>')
		
		if (oldShowingLink === $homeLink.get(0)) {
			getVideo(link, video)
		} else {
			if ($menuLinks.index(oldShowingLink) > -1) {
				closeLinkContent(oldShowingLink, function() {
					getVideo(link, video)
				})
			} else {
				closeVideo(oldShowingLink, function() {
					getVideo(link, video)
				})
			}
		}
	}
	
	var handleMenuClick = function(event) {
		if (isAnimating) {
			// we need to wait for animations to complete
			return false
		}
		
		var link = this,
			oldShowingLink = showingLink
		
		if (showingLink === link) {
			return false
		}
		disableLinks()
		var name = $(link).text().toLowerCase()
		
		if (oldShowingLink === $homeLink.get(0)) {
			showMenuLink(link, name)
		} else {
			if ($menuLinks.index(oldShowingLink) > -1) {
				closeLinkContent(oldShowingLink, function() {
					showMenuLink(link, name)
				})
			} else {
				closeVideo(oldShowingLink, function() {
					showMenuLink(link, name)
				})
			}
		}
	}
	
	var moveBackBoxes = function(move, numToMove, callback) {
		for (var i = 0; i < numToMove; i++) {
			$('#'+move[i]).css('z-index', 50)
			$('#'+move[i]).animate({
				top: videoInfo[move[i]].row * boxOuterHeight,
				left: videoInfo[move[i]].col * boxOuterWidth
			}, {speed: 'fast', complete: function() {
				$(this).css('z-index', 0)
			}})
		}
		
		$grid.animate({height: gridHeight}, {speed: animateSpeed, complete: function() {
			if (typeof(callback) == 'function') {
				callback()
			}
		}})
	}
	
	positionVideos()
	enableLinks()
});

//function slideNav() {
//	var links = $('#reindeer a, .cartcount, .position-control');
//	var targets = $('li.card');
//	var targetMap = {};
//	
//	$(links).each(function(index) {
//		targetMap[$(this).attr('href').replace(/^#/, '')] = index;
//	});
//	
//	$.address.autoUpdate(false);
//	
//	var addressChanger = function(e) {
//		e.preventDefault();
//		$.address.value($(this).attr('href').replace(/^#/, ''));
//		$.address.update();
//	};
//	
//	// tie links to address plugin
//	$(links).click(addressChanger);
////	$('.subnav li a').click(addressChanger);
//	
//	$.address.change(function(event) {
//	    if (event.value == '/') {
//	    	$('html,body').animate({scrollTop: 0},'slow');
//	    } else if (event.value.length > 1) {
//	    	var value = event.value.substr(1);
//	    	
//	    	if (value in targetMap) {
//	    		$('html,body').animate({scrollTop: $('#'+value).offset().top - 10},'slow');
//	    	} /*else {
//	    		$('html,body').animate({scrollTop: 0},'slow');
//	    	}*/
//	    }
//	});
//}
