Gallery = new JS.Class('Gallery', {
	include: [Ojay.Observable, JS.State],
	
	initialize: function(root, imageContainers) {
		this._root = Ojay(root).addClass('show-captions');
		
		this._images = Ojay(imageContainers).map(function(container) {
			var caption = container.children('.caption'),
				link    = container.children('a'),
				img     = link.children('img'),
				wrapper;
			
			wrapper = Ojay(Ojay.HTML.div(function(H) {
				H.img({alt: img.node.alt, src: link.node.href});
			})).insert(caption);
			
			wrapper.setStyle({
				position: 'absolute',
				top:      0,
				left:     0,
				zIndex:   1,
				opacity:  0
			}).hide();
			
			return wrapper;
		});
		
		this._current = 0;
		this._captionsVisible = true;
		
		this.setState('CREATED');
	},
	
	states: {
		CREATED: {
			setup: function() {
				this._container = Ojay(Ojay.HTML.div()).setStyle({
					position: 'relative',
					overflow: 'hidden',
					width:    '700px',
					height:   '500px'
				});

				this._images.forEach(function(image) {
					this._container.insert(image, 'bottom');
				}, this);

				this._images[this._current].setStyle({
					opacity: 1,
					zIndex:  3
				}).show();

				this._root.insert(this._container, 'top');
				
				this.on('imagechange', function() {
					if (this._action && typeof this[this._action] === 'function') {
						this[this._action]();
						delete this._action;
					}
				}, this);
				
				this.setState('READY');
			}
		},
		
		READY: {
			setImage: function(index, controller) {
				if (index === this._current) return;

				var current = this._images[this._current],
					next    = this._images[index];
				
				// if (controller !== this) {
				// 	this.pause();
				// }
				
				this.setState('ANIMATING');
				
				current.setStyle({zIndex: 1})
				._(next).show().setStyle({zIndex: 3}).animate({
					opacity: {
						from: 0,
						to:   1
					}
				}, 1.0)
				._(current).setStyle({opacity: 0}).hide()
				._(function(self) {
					self._current = index;
					self.setState('READY');
					self.notifyObservers('imagechange', next, index);
				}, this);
			},
			
			next: function() {
				var nextImage = this._current + 1;
				
				if (nextImage >= this._images.length) nextImage = 0;
				
				this.setImage(nextImage);
			},
			
			previous: function() {
				var previousImage = this._current - 1;
				
				if (previousImage < 0) previousImage = this._images.length - 1;
				
				this.setImage(previousImage);
			},
			
			play: function() {
				this._playing = setInterval(function() {
					this.next();
				}.bind(this), 3000);

				this.next();
			},
			
			pause: function() {
				clearInterval(this._playing);
				this._playing = null;
			}
		},
		
		ANIMATING: {
			play: function() {
				this._action = 'play';
			},
			
			pause: function() {
				this._action = 'pause';
			}
		}
	},
	
	getCurrentIndex: function() {
		return this._current;
	},
	
	isPlaying: function() {
		return typeof this._playing === 'number';
	},
	
	hideCaptions: function() {
		if (this._captionsVisible) {
			this._root.replaceClass('show-captions', 'hide-captions');
			this._captionsVisible = false;
		}
	},
	
	showCaptions: function() {
		if (!this._captionsVisible) {
			this._root.replaceClass('hide-captions', 'show-captions');
			this._captionsVisible = true;
		}
	},
	
	captionsVisible: function() {
		return this._captionsVisible;
	},
	
	getImages: function() {
		return this._images;
	}
});

Gallery.Controls = {};

Gallery.Controls.Next = new JS.Class('Gallery.Controls.Next', {
	initialize: function(gallery) {
		this._gallery = gallery;
		
		this._html 		 = Ojay(Ojay.HTML.div({className: 'next-previous-controls'}));
		this._prevButton = Ojay(Ojay.HTML.div({className: 'previous'}, 'Previous'));
		this._nextButton = Ojay(Ojay.HTML.div({className: 'next'}, 'Next'));
		
		this._html.insert(this._prevButton).insert(this._nextButton, 'bottom');
		
		this._prevButton.on('click')._(this._gallery).previous();
		this._nextButton.on('click')._(this._gallery).next();
	},
	
	getHTML: function() {
		return this._html;
	}
});

Gallery.Controls.Captions = new JS.Class('Gallery.Controls.Captions', {
	initialize: function(gallery) {
		this._gallery = gallery;
		this._html    = Ojay(Ojay.HTML.div({className: 'control'}, 'Hide captions'));
		
		this._html.on('click')._(this).toggleCaptions();
	},
	
	toggleCaptions: function() {
		var method, text;
		
		if (this._gallery.captionsVisible()) {
			method = 'hideCaptions';
			text   = 'Show captions';
		} else {
			method = 'showCaptions';
			text   = 'Hide captions';
		}
		
		this._gallery[method]();
		this._html.setContent(text);
	},
	
	getHTML: function() {
		return this._html;
	}
});

Gallery.Controls.Slideshow = new JS.Class('Gallery.Controls.Slideshow', {
	initialize: function(gallery) {
		this._gallery = gallery;
		this._html    = Ojay(Ojay.HTML.div({className: 'control slideshow'}, 'Start Slideshow'));
		
		this._html.on('click')._(this).toggleSlideshow();
	},
	
	toggleSlideshow: function() {
		var method, text;
		
		if (this._gallery.isPlaying()) {
			method = 'pause';
			text   = 'Play slideshow';
		} else {
			method = 'play';
			text   = 'Pause slideshow';
		}
		
		this._gallery[method]();
		this._html.setContent(text);
	},
	
	getHTML: function() {
		return this._html;
	}
});

Gallery.Controls.Thumbnails = new JS.Class('Gallery.Controls.Thumbnails', {
	initialize: function(gallery, container, options) {
		this._gallery   = gallery;
		this._container = Ojay(container);
		this._links     = this._container.descendants('a');
		this._options   = options || {};
	},
	
	setup: function() {
		var self = this;
		this._links.forEach(function(link, i) {
			link.on('click', function(elem, evnt) {
				evnt.stopDefault();
				self._gallery.setImage(i);
			});
		});
		
		this._paginator = new Ojay.Paginator(this._container, this._options);
		this._paginator.setup();
		this._paginator.addControls('after');
	},
	
	getHTML: function() {
		return this._paginator.getHTML();
	}
});

Gallery.Notifier = new JS.Class('Gallery.Notifier', {
	initialize: function(gallery, container) {
		this._gallery   = gallery;
		this._container = Ojay(container);
		this._html      = Ojay(Ojay.HTML.p({className: 'gallery-notifier'}));
		
		this._gallery.on('imagechange', function(g, image, index) {
			this.update(index, g.getImages().length);
		}, this);
		
		this._container.insert(this._html);
		this.update(this._gallery.getCurrentIndex(), this._gallery.getImages().length);
	},
	
	getHTML: function() {
		return this._html;
	},
	
	update: function(index, count) {
		this._html.setContent('Slide ' + (index + 1) + ' of ' + count);
	}
});
