// ____________________________________________________________________________
// tbGallery v1.0
// copyright (c) Thomas Bee, November 27th, 2009
// TODOs: 
// - display EXIF data 
// ____________________________________________________________________________

(function($) {
// --------------------------------------------------
// tbGallery - plugin main function
// --------------------------------------------------
$.fn.tbGallery = function(options) {
  	// default opts
  	var opts = $.extend({
		maxImageHeight  :   675,
		maxImageWidth   :  1200,
		imageMargin     :    10,
		outerMargin     :    15,
		frameAnimSpeed  :   300,
		fadeAnimSpeed   :   500, 
		gallery         :  null,
		showFilmstrip   : false
  	}, options)
  	
  	// variables
	var imageNumber  = -1
	var currentImage 
	var frameTop, frameLeft, frameHeight, frameWidth
	var g = opts.gallery
	var showGalleries = false
	var showImpressum = false
	 
	// filmstrip scrolling
	var scrollStartX
	var scrollStep   =   0
	var scrollSteps  =   5 // number of different scroll speed increments
	var scrollDelta  =  40 // pixel delta for mouse gesture detection
	var stripWidth   =   0 // initialized when filmstrip is generated
		
  	// hide image, filmstrip, galleries
	$('#image').hide()
	$('#filmstrip').hide()
	$('#galleries').hide()
	$('#impressum').hide()

	// hook callbacks 	
	$(window).resize(handleResize)
	$('#galleries_link').click(toggleGalleries)
	$('#filmstrip_link').click(toggleFilmstrip)
	$('#impressum_link').click(showTbImpressum)
	$('#impressum_backlink').click(function(){ $('#impressum').hide()})
	$('#filmstrip').mouseenter(handleMouseEnter)
	$('#filmstrip').mousemove(handleMouseMove)
	$('#filmstrip').mouseleave(handleMouseLeave)
	
	// set initial panel size
	resizePanel()

	// fade in 1st image
	initialize()

	// ____________________________________________________________________________
	// initialize()
	// ____________________________________________________________________________
	function initialize() {
		imageNumber  = -1
		// position initial frame in the center
		$('#frame').css({
			'height':'0px', 
			'width' :'0px', 
			'top'   : $('#panel').height()/2 + 'px', 
			'left'  : $('#panel').width()/2  + 'px'
		})
		// fade in first image
		showImage('next')
	}

	// ____________________________________________________________________________
	// handleResize()
	// ____________________________________________________________________________
	function handleResize() {
		resizePanel()
		resizeImage()
		resizeFrame()
	}

	// ____________________________________________________________________________
	// handleKeydown()
	// 'n' selects next image, 'b' select previous image
	// ____________________________________________________________________________
	function handleKeydown(event) {
		// stop scrolling filmstrip
		stopFilmstripScrolling()
	
  		if ((event.keyCode == 78) || (event.keyCode == 32)) {
        	showImage('next')
    	}  else if (event.keyCode == 66) {
    		showImage('previous');
    	} else if (event.keyCode == 70) { 
			toggleFilmstrip()
    	} 
	}

	// ____________________________________________________________________________
	// showImage(direction)
	// ____________________________________________________________________________
	function showImage(direction) {	
		// disable next/previous links while fading
		disableNavigation()
				
		// remove active frame in filmstrip
		$('#frame'+imageNumber).removeClass('activeframe') 

		// adjust image index 
		if (direction == 'next') {
        	imageNumber = (imageNumber + 1) % (g.length);
    	} else if (direction == 'previous') {
        	imageNumber = (imageNumber + g.length - 1) % (g.length);
    	}

		// preload neighbouring images
 	  	var nextImage     = new Image()
    	var previousImage = new Image()
		nextImage.src     = g[(imageNumber + 1) % g.length][0]; 
		previousImage.src = g[(imageNumber + g.length - 1) % g.length][0];
		
    	// hide image
  	    $('#image').hide()

		// load next image
		currentImage = new Image()
	    $(currentImage).load(imageLoaded).attr('src', g[imageNumber][0])
    };
	
	
	// --------------------------------------------------
	// imageLoaded()
	// --------------------------------------------------
	function imageLoaded() {
		// update footer
		$('#footer-right').text((imageNumber+1) + '/' + g.length)	
		$('#footer').text(g[imageNumber][1])

		// multiline caption might increase footer height > check panel height again
		resizePanel()
		
		// resize image to fit into panel
		resizeImage()	
		
		// highlight active frame in filmstrip
		$('#frame'+imageNumber).addClass('activeframe') 

		// update filmstrip position, if needed
		adjustFilmstripPosition()

		// animate frame size change, then fade in image 		
		$('#frame').stop().animate({
			'top'    : frameTop    + 'px',  
			'left'   : frameLeft   + 'px',
			'height' : frameHeight + 'px',
			'width'  : frameWidth  + 'px'
		}, opts.frameAnimSpeed, 'swing', fadeInImage)				
	}
	
	// --------------------------------------------------
	// fadeInImage()
	// --------------------------------------------------
	function fadeInImage() {
		// fade in new image
	    $('#image').attr('src', g[imageNumber][0])
		$('#image').stop().fadeIn(opts.fadeAnimSpeed, enableNavigation);
	}

	// --------------------------------------------------
	// resizePanel()
	// set new vertical panel height depending on available viewport size
	// --------------------------------------------------
	function resizePanel() {
 		// VERTICAL RESIZE: if caption is empty and has zero height, set footer height to height of footer-right
 		var footerHeight = Math.max($('div#footer').outerHeight(), $('div#footer-right').outerHeight())
 		var panelHeight  = $(window).height() - $('div#header').outerHeight() - footerHeight
		if (opts.showFilmstrip) {
			panelHeight = panelHeight - $('div#filmstrip').outerHeight() - parseInt($('div#filmstrip').css('margin-bottom'))
		}
		panelHeight = Math.min(panelHeight, opts.maxImageHeight + 2*opts.imageMargin + 2)
		$('#panel').height(panelHeight + 'px')	  
		
		// HORIZONTAL RESIZE
		var panelWidth = Math.min($(window).width()-2*opts.outerMargin, opts.maxImageWidth + 2)
		$('#wrapper').width(panelWidth  + 'px')	  
	}

	// --------------------------------------------------
	// resizeImage()
	// --------------------------------------------------
	function resizeImage() {			
 		var maxHeight = $('#panel').height() - 2 - 2*opts.imageMargin
		var maxWidth  = $('#panel').width()  - 2

		// initialize with physical image size
		var newHeight = currentImage.height
		var newWidth  = currentImage.width
		
		if (newHeight > maxHeight) {
			var ratio  = maxHeight / newHeight
			newHeight = maxHeight
			newWidth  = newWidth * ratio
		}
		if (newWidth > maxWidth) {
			var ratio  = maxWidth / newWidth
			newHeight = newHeight * ratio	
			newWidth  = maxWidth
		}
		// update image size
		$('#image').attr('height', Math.round(newHeight))
		$('#image').attr('width',  Math.round(newWidth))

		// store frame's top and left offsets, height, width in global variables
		frameTop    = Math.round((maxHeight - newHeight)/2 + opts.imageMargin)
		frameLeft   = Math.round((maxWidth  - newWidth)/2)
		frameHeight = Math.round(newHeight)
		frameWidth  = Math.round(newWidth)
  	};
  	 
	// --------------------------------------------------
	// resizeFrame()
	// --------------------------------------------------
 	function resizeFrame() {
	    // center frame vertically and horizontally
 		$('#frame').height(frameHeight)
 		$('#frame').width(frameWidth)
 		$('#frame').css('top',  frameTop  + 'px')
 		$('#frame').css('left', frameLeft + 'px')
   	}
	
	// --------------------------------------------------
	// enableNavigation()
	// --------------------------------------------------
	function enableNavigation() {
		$(document).keydown(handleKeydown)
		$('#panel').click(panelClicked)
	}
	
	// --------------------------------------------------
	// disableNavigation()
	// --------------------------------------------------
	function disableNavigation() {
		stopFilmstripScrolling()
		$(document).unbind('keydown')
		$('#panel').unbind('click')
	}
	
	// --------------------------------------------------
	// panelClicked()
	// show next or previous image depending on where 
	// the user clicked
	// --------------------------------------------------
	function panelClicked(e) {
		var centerX = $('#panel').outerWidth()
		if (e.clientX > centerX/2) { 
			showImage('next')
		} else {
			showImage('previous')
		}
	}

	// --------------------------------------------------
	// toggleFilmstrip()
	// --------------------------------------------------
	function toggleFilmstrip() {
		if (!opts.showFilmstrip) {
			opts.showFilmstrip = true
			buildFilmstrip()		
			handleResize()
			$('#filmstrip_link').text('off')
			$('#filmstrip').slideDown(300)			
		} else {
			opts.showFilmstrip = false
			// stop scrolling film strip
			stopFilmstripScrolling()		
			$('#filmstrip_link').text('on')
			$('#filmstrip').slideUp(300, handleResize)
		}
	}
	
	// --------------------------------------------------
	// buildFilmstrip()
	// --------------------------------------------------
	function buildFilmstrip() {
		// empty previous content
		$('#filmstrip').empty()
		stripWidth = 0

		// create absolutely positioned filmstrip div
		$('<div id="filmstrip_slider"></div>').appendTo('#filmstrip').css({
			'width'   : '100000px',
			'position': 'absolute', 
			'left'    : '0px'
		})
											 		
		// populate list
	    for (i = 0; i < g.length; i++) {
	        // build thumbnail URL: find index of last slash and insert '/th/'
	        slash = g[i][0].lastIndexOf('/');
	        len   = g[i][0].length;
	        url   = g[i][0].slice(0,slash) + '/th/' + g[i][0].slice(slash+1,len);

			$('<img />').attr({ src:url, id:'frame' + i})
					    .css('cursor', 'pointer')
			            .click(jumpToImage)
			            .appendTo('#filmstrip_slider')
						.addClass((i == imageNumber) ? 'activeframe' : "")
		}
	}
	
	// --------------------------------------------------
	// jumpToImage()
	// --------------------------------------------------
	function jumpToImage() {
		// remove active frame in filmstrip
		$('#frame'+imageNumber).removeClass('activeframe') 
	
		// get image ID
		var imageId = $(this).attr('id')
		imageId = imageId.slice(5, imageId.length)
	
		// substract 1 to be able to reuse showImage()
		imageNumber = parseInt(imageId) - 1
		
		// stop scrolling (if active)	
		stopFilmstripScrolling()		
		
		// show selected image		
		showImage('next')
	}
	
	// --------------------------------------------------
	// handleMouseEnter()
	// --------------------------------------------------
	function handleMouseEnter(e) {
		// store initial x position when mouse enters filmstrip
		scrollStartX = e.clientX
		scrollStep = 0
	}	

	// --------------------------------------------------
	// handleMouseLeave()
	// --------------------------------------------------
	function handleMouseLeave(e) {
		// stop scrolling when mouse leaves filmstrip
		stopFilmstripScrolling()	
	}	

	// --------------------------------------------------
	// handleMouseMove()
	// --------------------------------------------------
	function handleMouseMove(e) {		
		if (scrollStep == 0) {
			if (e.clientX > scrollStartX + scrollDelta) {
				// start scrolling to the left
				scrollStep = 1
				scrollStartX = e.clientX
				animateFilmstripPosition() 
			} else if (e.clientX < scrollStartX - scrollDelta) {
				// start scrolling to the right
				scrollStep = -1
				scrollStartX = e.clientX
				animateFilmstripPosition() 
			}
		} else if (scrollStep > 0) {
			if (e.clientX > scrollStartX + scrollDelta) {
				scrollStartX = e.clientX
				if (scrollStep < scrollSteps) {
					// already scrolling: increase scroll speed
					scrollStep += 1
					$('#filmstrip_slider').stop()
					animateFilmstripPosition()
				} 
			} else if (e.clientX < scrollStartX - 1.5*scrollDelta) {
				// stop scrolling when moving mouse into opposite direction
				scrollStartX = e.clientX
				$('#filmstrip_slider').stop()
				stopFilmstripScrolling()	
			}
		} else if (scrollStep < 0)	{
			if (e.clientX < scrollStartX - scrollDelta) {
				scrollStartX = e.clientX
				if (Math.abs(scrollStep) < scrollSteps) {	
					// already scrolling: increase scroll speed
					scrollStep -=  1
					$('#filmstrip_slider').stop()
					animateFilmstripPosition()
				} 
			} else if (e.clientX > scrollStartX + 1.5*scrollDelta) {
				// stop scrolling when moving mouse into opposite direction
				scrollStartX = e.clientX
				$('#filmstrip_slider').stop()
				stopFilmstripScrolling()	
			}
		}
	}	
	
	// --------------------------------------------------
	// animateFilmstripPosition()
	// --------------------------------------------------
	function animateFilmstripPosition() {
		// determine scroll direction 
		var direction = (scrollStep < 0) ? 1 : -1
		
		// find new position to scroll to
		var oldPos = parseInt($('#filmstrip_slider').css('left'))
		var newPos = 0
		if (direction > 0) {
			newPos = 0
		} else {
			newPos = -( $('#frame'+(g.length-1)).position().left - $('#wrapper').width() + $('#frame'+(g.length-1)).outerWidth() + 2 )
		}

		// determine scroll duration depending on scroll distance 
		var duration = Math.abs(oldPos - newPos)
		
		// hardcoded scroll speeds
		switch (Math.abs(scrollStep)) {
			case  1: duration *= 40; break;
			case  2: duration *= 10; break;
			case  3: duration *=  5; break;				
			case  4: duration *=  2; break;
			case  5: duration *=  1; break;
			default: duration *= 40; break;		
		}

		// scroll
		$('#filmstrip_slider').stop().animate({ 'left': newPos + 'px'}, duration, 'linear')
	}

	// --------------------------------------------------
	// adjustFilmstripPosition()
	// if current image thumbnail is out of viewport, 
	// scroll filmstrip to left or right
	// --------------------------------------------------
	function adjustFilmstripPosition() {
		if (!opts.showFilmstrip) 
			return
			
		var frameLeft    = $('#frame'+imageNumber).position().left
		var frameRight   = frameLeft + $('#frame'+imageNumber).outerWidth()
		var wrapperWidth = $('#wrapper').width()
		var filmstripPos = Math.abs(parseInt($('#filmstrip_slider').css('left')))
		
		if ((filmstripPos > frameLeft)  || (frameLeft  > (filmstripPos + wrapperWidth)) ||
		    (filmstripPos > frameRight) || (frameRight > (filmstripPos + wrapperWidth))) {
			var newPos = -frameLeft
			$('#filmstrip_slider').stop().animate({ 'left': newPos + 'px'}, 'slow', 'swing')
			
		}	
	}
	
	// --------------------------------------------------
	// stopFilmstripScrolling()
	// --------------------------------------------------
	function stopFilmstripScrolling() {
		scrollStep = 0
		$('#filmstrip_slider').stop()	
	}

	// --------------------------------------------------
	// toggleGalleries()
	// --------------------------------------------------
	function toggleGalleries() {
		if (!showGalleries) {
			disableNavigation()
			if (opts.showFilmstrip) toggleFilmstrip()
			$('#galleries').slideDown(300)
		} else {
			$('#impressum').hide()
			$('#galleries').slideUp(300)
			enableNavigation()
		}
		showGalleries = ! showGalleries
	}

	// --------------------------------------------------
	//  showTbImpressum()
	// --------------------------------------------------
	function showTbImpressum() {
		$('#impressum').show()
	}

	// --------------------------------------------------
	// PUBLIC: openGallery()
	// --------------------------------------------------
	$.fn.tbGallery.openGallery = function(gallery, elem, title) {	
			// set new active gallery link
			$('a', '#galleries').removeClass('current')
			$(elem).addClass('current')	

			// slide up curtain 
			toggleGalleries()

			// store active gallery
			g = gallery
			
			// re-initialize
			initialize()
			
			// set gallery title
			$('#title').text(title)
	}
}
})(jQuery);
