/**
 * Prototype background animation
 *
 * Usage:
 *		new Techtwo.BackgroundAnimation({
 *			container: 'elementid',
 *			imageList: [
 *				'img/image1.jpg',
 *				'img/image2.jpg'
 *			]
 *		});
 *
 * @created 2011-04-07
 * @author Bastiaan Heeren
 * @copyright Techtwo Webdevelopment 2011
 * @version 1.0.0
 */

var Techtwo = Techtwo || {};

// Background animation
Techtwo.BackgroundAnimation = Class.create();
Techtwo.BackgroundAnimation.prototype = {

	delay:              7000,
	duration:           3000,
	container:          null,

	loadedImages:       [],
	activeImageNode:    null,
	activeImageIndex:   -1,
	hiddenImageNode:    null,
	hiddenImageIndex:   -1,

	activeEffect:       false,
	activeTimer:        false,

	/**
	 * Constructor
	 */
	initialize: function (options) {
		if (typeof options.delay     !== 'undefined') { this.delay     = options.delay;     }
		if (typeof options.duration  !== 'undefined') { this.duration  = options.duration;  }
		if (typeof options.container !== 'undefined') { this.container = options.container; }
		if (typeof options.imageList !== 'undefined') { this.addImages(options.imageList);  }
		if (typeof options.onChange  !== 'undefined') { this.onChange  = options.onChange;  }

		Event.observe(document, 'dom:loaded', this.init.bindAsEventListener(this));
	},

	/**
	 * Create DOM elements and attach event handlers.
	 */
	init: function () {
		// resolve container dom element
		this.container = $(this.container);

		// create active image display element
		this.activeImageNode = new Element('img');
		this.activeImageNode.setStyle({position: 'absolute'});
		Element.insert(this.container, this.activeImageNode);

		// create hidden image display element
		this.hiddenImageNode = new Element('img');
		this.hiddenImageNode.setStyle({position: 'absolute', display: 'none'});
		Element.insert(this.container, this.hiddenImageNode);

		// attach resize event handler
		Event.observe(document.onresize ? document : window, "resize", this.resize.bindAsEventListener(this));

		// start the animation if an image has completed loading
		this.next();
	},

	/**
	 * Add array of image URLs to the loaded images list.
	 * @param array [ imageUrl1, imageUrl2, ... ]
	 */
	addImages: function (imageList) {
		var self = this;
		imageList.each(function (imgSrc) {
			// create and preload a new image element
			var img = new Image();
			img.onload = function () {
				// store the image in an array for use in the animations
				self.loadedImages.push({
					image:  img,
					width:  img.width,
					height: img.height
				});

				// callback
				if (typeof self.onChange === 'function') {
					self.onChange((self.hiddenImageIndex !== -1) ? self.hiddenImageIndex : self.activeImageIndex, self.loadedImages.length);
				}

				// start animation if needed
				if (self.hiddenImageIndex === -1 && self.activeImageIndex === -1) {
					self.next();
				}
			};
			img.src = imgSrc;
		});
	},

	/**
	 * Animate to next available image.
	 */
	next: function () {
		if (this.activeTimer) {
			window.clearTimeout(this.activeTimer);
			this.activeTimer = false;
		}
		if (this.activeEffect) {
			this.activeEffect.cancel();
		}

		// get next image
		if (this.loadedImages.length > 0) {
			this.hiddenImageIndex = (this.activeImageIndex < this.loadedImages.length - 1) ? this.activeImageIndex + 1 : 0;
		}
		else {
			return;
		}

		// more than one image loaded?
		if (this.hiddenImageIndex === this.activeImageIndex) {
			this.hiddenImageIndex = -1;
			return;
		}

		// callback
		if (typeof this.onChange === 'function') {
			this.onChange(this.hiddenImageIndex, this.loadedImages.length);
		}

		// prepare animation
		this.hiddenImageNode.setStyle({opacity: 0, display: ''});
		this.hiddenImageNode.src = this.loadedImages[this.hiddenImageIndex].image.src;
		this.resize();

		var self = this;
		var complete = function () {
			self.activeEffect = false;

			// swap active/hidden image nodes
			var tmpNode = self.activeImageNode.parentNode.removeChild(self.activeImageNode);
			self.activeImageIndex = self.hiddenImageIndex;
			self.activeImageNode = self.hiddenImageNode;
			self.activeImageNode.setStyle({opacity: 1});

			// reinsert hidden node before active node
			self.hiddenImageIndex = -1;
			self.hiddenImageNode = tmpNode;
			Element.setStyle(tmpNode, {display: 'none'});
			Element.insert(self.container, tmpNode);

			// callback
			if (typeof self.onChange === 'function') {
				self.onChange(self.activeImageIndex, self.loadedImages.length);
			}

			self.activeTimer = window.setTimeout(function () {
				self.activeTimer = false;
				self.next();
			}, self.delay);
		};

		// fade in new image
		this.activeEffect = new Effect.Opacity(this.hiddenImageNode, {
			from: 0,
			to: 1,
			duration: this.duration / 1000,
			afterFinish: complete
		});
	},

	/**
	 * Resize visible image(s) to optimal screen dimensions.
	 */
	resize: function () {
		var windowSize = Element.getDimensions(this.container);
		var self = this;
		[[this.activeImageIndex, this.activeImageNode],
		 [this.hiddenImageIndex, this.hiddenImageNode]].each(function (img) {
			var index = img[0], node = img[1];
			if (index !== -1 && typeof node !== 'undefined') {
				var imageSize = self.calculateOptimalDimensions(self.loadedImages[index], windowSize);
				Element.setStyle(node, {
					left: Math.floor((windowSize.width - imageSize.width) / 2) + 'px',
					top: Math.floor((windowSize.height - imageSize.height) / 2) + 'px',
					width: Math.floor(imageSize.width) + 'px',
					height: Math.floor(imageSize.height) + 'px'
				});
			}
		});
	},

	/**
	 * returns the image dimensions so the image will fill the screen without distorting the aspect ratio
	 *
	 * @param object { width: [original image width], height: [original image height] }
	 * @param object { width: [window width], height: [window height] }
	 * @return object { width: [optimal image width], height: [optimal image height] }
	 */
	calculateOptimalDimensions: function (image, windowSize) {
		// calculate window ratio
		var windowRatio = windowSize.width / windowSize.height;

		// calculate image dimensions
		var imageSize = {};
		var imageRatio = image.width / image.height;

		if (imageRatio < windowRatio) {
			// resize by width
			imageSize.width  = windowSize.width;
			imageSize.height = imageSize.width / imageRatio;
		} else {
			// resize by height
			imageSize.height = windowSize.height;
			imageSize.width  = imageSize.height * imageRatio;
		}

		// return optional dimensions
		return imageSize;
	}

};

