/*
Class: Aurora.Images
*/
Aurora.Images = new Class({

	Implements: Options,
	
	options: {
	
		mode: 'image',
		
		effect: 'quad',
		ease: 'in:out',
		
		durations: {
			open: 700,
			resize: 400,
			fade: 200,
			caption: 400
		},
		
		explode: false, // to be implemented
		
		padding: 10,
		
		captions: false,
		animateCaption: true,
		
		overlay: true, // to be implemented
		
		imageQuery: 'a',
		
		counter: 'Image {num} of {total}',
		
		mouseWheel: false,
		keyboardShortcuts: false
		
	},
	
	initialize: function( target, options )
	{
		this.setOptions( options );
		
		var $el = this.$el = target;
		
		if ( !options )
			options = {};
		
		
		// Gather images
		this.images = [];
		
		
		// Mode
		switch( this.options.mode )
		{
			case 'gallery':
			
				var images = target.getElements( this.options.imageQuery );
				
				if ( images.length <= 1 )
					this.options.mode = 'image';
				
				images.each( function( $el ) {
				
					var options = $el.getDataFromComment() || {};
				
					var image = new Aurora.Images.Image( $el, this, options );
				
					this.images.push( image );
				
				}, this );
				
			break;
			
			case 'image':
			
				var image = new Aurora.Images.Image( $el, this, options );
				
				// Enable captions if its been set for the image and we don't set it specifically
				if ( image.get( 'caption' ) && !options.captions )
					this.options.captions = true;
			
			break;
		}
		
	},
	
	setup: function( image )
	{
		// Overlay
		this.$overlay = new Element( 'div', {
			'class': 'aurora-images-overlay',
			events: {
				click: this.close.bindWithEvent( this )
			}
		}).inject( document.body, 'top' );
		
		
		// Box
		this.$box = new Element( 'div', {
			'class': 'aurora-images-box',
			styles: {
				width: 0,
				height: 0,
				/* marginLeft: 0, */
				position: 'absolute',
				opacity: 0.2
			}
		}).inject( document.body, 'top' );
		
		
		// Previous / Next
		if ( this.options.mode == 'gallery' )
		{
			this.$prevLink = new Element( 'a', {
				'class': 'aurora-images-prev',
				href: 'javascript:;'
			}).inject( this.$box );
			
			this.$prevLink.addEvent( 'click', this.changeImage.bindWithEvent( this, -1 ) );
			
			this.$nextLink = new Element( 'a', {
				'class': 'aurora-images-next',
				href: 'javascript:;'
			}).inject( this.$box );
			
			this.$nextLink.addEvent( 'click', this.changeImage.bindWithEvent( this, 1 ) );
		}
		
		
		// Stage
		this.$stage = new Element( 'div', { 'class': 'aurora-images-stage' } ).inject( this.$box );
		this.$bottom = new Element( 'div', { 'class': 'aurora-images-bottom' } ).inject( this.$box );
		
		
		// Close
		this.$closeButton = new Element( 'div', {
		
			'class': 'aurora-images-close',
			
			events: {
				click: this.close.bindWithEvent( this )
			}
			
		}).inject( this.$bottom );
		
		
		// Caption
		if ( this.options.captions )
			this.$caption = new Element( 'div', { 'class': 'aurora-images-caption' } ).inject( this.$bottom );
		
		
		// Counter
		if ( this.images.length )
			this.$counter = new Element( 'div', { 'class': 'aurora-images-counter' } ).inject( this.$bottom );
			
		
		// Active flag (prevents keyboard and mouse events from firing unnecessarily)
		this._active = false;
		
		
		// Mouse Wheel
		if ( this.options.mouseWheel )
			document.addEvent( 'mousewheel', this.mouseWheelListener.bindWithEvent( this ) );
		
		
		// Keyboard
		if ( this.options.keyboardShortcuts )
			document.addEvent( 'keydown', this.keyboardListener.bindWithEvent( this ) );
		
		
		// Set flag so we don't setup again
		this._setup = true;
		
		
		// Open image
		if ( image )
			this.open( image );
	},
	
	toElement: function()
	{
		return this.$el;
	},
	
	open: function( image )
	{
		if ( !this._setup )
		{
			this.setup( image );
			return;
		}
	
		this._active = true;
		
		var size = window.getSize();
		var scroll = window.getScroll();
		var scrollSize = window.getScrollSize();
		
		this.$overlay.setStyles({
			opacity: 0,
			display: 'block',
			width: scrollSize.x,
			height: scrollSize.y
		});
		
		// Code before fancy postioning and sizing
		/* this.image.setStyles({
			display: 'block',
			top: top
		}); */
		
		// Retrieve the original size of the image and set it
		var dimensions = image.get( 'dimensions' );
		
		this.$box.setStyles({
			display: 'block',
			width: dimensions.width,
			height: dimensions.height
		});
		
		// Position
		var $el = image.$el;
		
		if ( image.$el.getElement( 'img' ) )
			$el = image.$el.getElement( 'img' );
		
		this.$box.position({ relativeTo: $el });
		
		// Code before fancy postioning and sizing
		// marginLeft: -( originalSize.x / 2 )
		
		this.$box.setOpacity( 0 );
		
		new Fx.Tween( this.$overlay, {
			property: 'opacity'
		}).start( 0.8 );
		
		this.startLoad( image );
		
		// Resize event
		window.addEvent( 'resize', function() {
		
			this.resize();
		
		}.bind( this ));
	},
	
	resize: function()
	{
		if ( !this._active )
			return;
		
		var scrollSize = window.getScrollSize();
		
		this.$overlay.setStyles({
			width: scrollSize.x,
			height: scrollSize.y
		});
	},
	
	startLoad: function( image, preload )
	{
		if ( !image )
			return;
		
		var $image = new Asset.image( image.get( 'src' ), {
		
			onload: function() {
			
				if ( !preload && this.image == image ) this.nextEffect();
			
			}.bind( this )
		
		});
		
		if ( !preload )
		{
			this.$box.addClass( 'aurora-images-loading' );
			this.$stage.setStyle( 'display', 'block' );
			this.$stage.empty();
			
			this.$bottom.setStyle( 'opacity', 0 );
			
			if ( this.options.mode == 'gallery' )
			{
				this.$prevLink.setStyle( 'display', 'none' );
				this.$nextLink.setStyle( 'display', 'none' );
			}
			
			this.image = image;
			this.$image = $image;
			this.caption = image.get( 'caption' );
			this._index = this.images.indexOf( image );
			
			this.step = 1;
		}
	},
	
	keyboardListener: function( event )
	{
		if ( !this._active )
			return;
		
		if ( event.key != 'f5' )
			event.preventDefault();
		
		switch ( event.key )
		{
			case 'esc': case 'x': case 'q': this.close(); break;
			case 'b': case 'p': case 'left': this.changeImage( event, -1 ); break;
			case 'f': case 'n': case 'right': this.changeImage( event, 1 );
		}
	},
	
	mouseWheelListener: function( event )
	{
		if ( !this._active )
			return;
		
		if ( event.wheel > 0 )
			this.changeImage( event, -1 );
		
		if ( event.wheel < 0 )
			this.changeImage( event, 1 );
	},
	
	changeImage: function( event, step )
	{
		this._changed = true;
	
		event.preventDefault();
		
		var index = this._index + step,
			image = this.images[ index ],
			count = this.images.length;
		
		if ( !image )
			index == count ? image = this.images[0] : image = this.images[ count - 1 ];
		
		if ( !image )
			return false;
		
		for ( var f in this.effects )
			this.effects[f].cancel();
		
		this.startLoad( image );
	},
	
	nextEffect: function()
	{
		switch( this.step++ )
		{
			case 1:
			
				// $log( '1) Expand from image' );
			
				var w = this.$image.width + this.options.padding * 2,
					h = this.$image.height + this.options.padding * 2;
				
				if ( this.options.mode == 'gallery' )
				{
					this.$prevLink.setStyle( 'height', h );
					this.$nextLink.setStyle( 'height', h );
				}
				
				// var marginLeft = -( this.image.width / 2 ),
				// marginTop = ( ( window.getSize().y - this.image.height ) / 2 );
				
				var scrollTop = window.getScrollTop(),
					windowSize = window.getSize();
				
				var marginLeft = ( ( windowSize.x - this.$image.width ) / 2 ),
					marginTop = ( ( windowSize.y - this.$image.height ) / 2 ) + scrollTop;
				
				// Move the image to the top if the image height exceeds the window size
				if ( windowSize.y < this.$image.height )
					marginTop += ( this.$image.height - windowSize.y ) / 2;
				
				var effect = this.options.effect;
				
				if ( this.options.ease && effect != 'linear' )
					effect += ':' + this.options.ease;
				
				var duration = this.options.durations.resize;
				
				if ( !this._changed )
					duration = this.options.durations.open;
				
				
				new Fx.Morph( this.$box, {
					duration: duration,
					transition: this.options.effect,
					onComplete: this.nextEffect.bind( this )
				}).start({
					width: w,
					height: h,
					left: marginLeft,
					top: marginTop,
					opacity: 1
				});
				
			break;
				
			case 2:
			
				// $log( '2) Fade in' );
				
				this.$box.removeClass( 'aurora-images-loading' );
				this.$stage.setStyle( 'opacity', 0 );
				
				this.$image.setStyle( 'margin', this.options.padding );
				this.$image.inject( this.$stage );
				
				new Fx.Tween( this.$stage, {
					property: 'opacity',
					duration: this.options.durations.fade,
					onComplete: this.nextEffect.bind( this )
				}).start( 1 );
			
			break;
			
			case 3:
				
				// $log( '3) Show next/previous, captions' );
				
				if ( this.options.mode == 'gallery' )
				{
					this.$prevLink.setStyle( 'display', 'block' );
					this.$nextLink.setStyle( 'display', 'block' );
				}
				
				if ( this.options.captions && this.options.animateCaption )
				{
					if ( this.options.mode == 'gallery' && this.options.counter )
					{
						var total = this.images.length;
						var num = this._index + 1;
						var counterText = this.options.counter;
						
						counterText = counterText.replace( /\{num\}/, num );
						counterText = counterText.replace( /\{total\}/, total );
						
						this.$counter.set( 'text', counterText);
					}
					
					if ( this.$caption )
						this.$caption.set( 'text', this.caption );
					
					var height = this.$bottom.getStyle( 'height' ).toInt();
					
					this.$bottom.setStyles({
						opacity: 1,
						top: -( height )
					});
					
					new Fx.Tween( this.$bottom, {
						property: 'top',
						duration: this.options.durations.caption,
						onComplete: this.nextEffect.bind( this )
					}).start( 0 );
				}
				
			break;
			
			case 4:
			
				// $log( '4) Start preloading images' );
				
				this.startLoad( this.images[ this._index -1 ], true );
				this.startLoad( this.images[ this._index +1 ], true );
			
			break;
		}
	},
	
	close: function()
	{
		this.$box.setStyle( 'display', 'none' );
		
		this.$overlay.get( 'tween' )
			.start( 'opacity', 0 );
		
		this._active = false;
		
		this._changed = false;
	}
});

Aurora.Images.Image = new Class({
	
	Implements: [ Events, Options ],
	
	options: {
	
		src: null,
		
		caption: '',
		
		width: null,
		height: null
	
	},
	
	initialize: function( image, images, options )
	{
		this.setOptions( options );
		
		var $el = this.$el = image;
		
		if ( !this.options.src )
			this.options.src = image.get( 'href' );
		
		if ( !this.options.caption )
			this.options.caption = ( image.get( 'title' ) || ( image.getElement( 'img' ) ? image.getElement( 'img' ).get( 'alt' ) : '' ) );
		
		$el.addEvent( 'click', (function( event ) {
		
			event.stop();
		
			images.open( this );
		
		}.bind( this )));
	},
	
	toElement: function()
	{
		return this.$field;
	},
	
	set: function( what, value )
	{
		switch ( what )
		{
			//
		}
		
		return this;
	},
	
	get: function( what )
	{
		switch ( what )
		{
			case 'src':
				return this.options.src;
				
			case 'caption':
				return this.options.caption;
				
			case 'dimensions':
				
				var $el = this.$el;
				
				if ( this.$el.getElement( 'img' ) )
					$el = this.$el.getElement( 'img' );
				
				return $el.getDimensions({ styles: [ 'padding' ] });
				
			case 'position':
			
				var $el = this.$el;
				
				if ( this.$el.getElement( 'img' ) )
					$el = this.$el.getElement( 'img' );
				
				return $el.getPosition();
		}
	}
	
});
