/** section: libraries
 * class DynoBox
 *
 *  v0.5.0-a
 *  for dynamic content boxes creation
 *  (c) 2010 Maciej Taranienko <matt@prayam.com>
 *  All rights reserved
 *
**/
DynoBox = Class.create({

	// globals
	switchers: [],
	targets: [],

	active: null,

	timerObj: undefined,
	timerRunning: false,
	timerPeriod: 6,	// class default (in seconds)

	isSwitching: false,
	isClicked: false,
	isHovered: false,

	// objs
	backslide: undefined,

	/**
	 * start the class
	 */
	initialize: function(params) {

		if($(params.wrapper)) {

			// set portlet
			this.portlet = $(params.wrapper);

			// switchers and targets
			this.switchers = this.portlet.select('li a');
			this.targets = this.portlet.select('.dynobox_content div');

			// add backslide (added in 0.5.0)
			if(params.backslide) this.backslide = params.backslide;

			// load default interval
			// TODO: work on proper isset()
			//if(GlobalSettings.DynoBox && GlobalSettings.DynoBox.speed) this.timerPeriod = GlobalSettings.DynoBox.speed;

			// register behaviours
			if(this.switchers.length == this.targets.length) this.register();
			else throw('DynoBox.js: incorrect switchers/targets amount for #' + portlet);
		}
		else throw('DynoBox.js: DynoBox not found at #' + portlet);
	},

	/**
	 * helper methods
	 */
	findActive: function() {

		// Find active element
		find = function(target, index) {

			if(target.visible()) {

				active = index;
			}
		}
		this.targets.each(find.bind(this)); 

		return active;
	},

	applyEvents: function() {

		// switchers clicks
		apply = function(switcher, index) { switcher.observe('click', this.switchBoxClick.bindAsEventListener(this, index)); }
		this.switchers.each(apply.bind(this));

		// portlet hovers
		portletOver = function() {

			this.timerStop();
			this.isHovered = true;
		}
		portletOut = function() {

			this.timerStart();
			this.isHovered = false;
		}
		this.portlet.observe('mouseover', portletOver.bind(this));
		this.portlet.observe('mouseout', portletOut.bind(this));
	},

	/**
	 * box switching methods
	 */
	switchBoxClick: function(e, index) {

		this.isClicked = true;
		this.switchBox(index);
		e.stop();
	},

	switchBox: function(index) {

		if(index != this.active && !this.isSwitching) {

			// set that we're switching now (disable dummy clicks)
			this.isSwitching = true;
			if(this.isClicked && !this.isHovered) { this.timerStop(); }

			// class names for active switchers
			this.switchers[this.active].removeClassName("selected");
			this.switchers[index].addClassName("selected");

			// backslide support
			if(this.backslide) this.backslide.swap(index);

			// switch content
			notSwitching = function() {
				this.isSwitching = false;
				if(this.isClicked) {

					if(!this.isHovered) { this.timerStart(); }

					this.isClicked = false;
				}
			}
			afterEffect = function(target) {

				new Effect.Appear(target, {
					duration: 0.3,
					afterFinish: notSwitching.bind(this)
				});
			}

			new Effect.Fade(this.targets[this.active], {
				duration: 0.3,
				afterFinish: afterEffect.bind(this, this.targets[index])
			});

			// set new active index
			this.active = index;
		}
	},

	switchNext: function() {

		if(this.active == (this.switchers.length - 1)) { this.switchBox(0); }
		else { this.switchBox(this.active + 1); }
	},

	notSwitching: function() { this.isSwitching = false; },

	/**
	 * timer methods
	 */
	timerStart: function() {

		if(!this.timerRunning) {

			this.timerObj = new PeriodicalExecuter(this.switchNext.bind(this), this.timerPeriod);
			this.timerRunning = true;
		}
	},
	timerStop: function() {

		if(this.timerRunning) {
			this.timerObj.stop();
			this.timerRunning = false;
		}
	},

	/**
	 * runtime methods
	 */
	register: function() {

		// find active (usefull when moving/recreating DynoBoxes HTML instance with other JS classes)
		this.active = this.findActive();

		// highlight it
		this.switchers[this.active].addClassName("selected");

		// apply mouse events
		this.applyEvents();

		// apply timer
		this.timerStart();
	}
});

/**
 * class DynoBox.Aautoloader
 **/
DynoBox.Loader = Class.create({

	// register dynoboxes inside a wrapper
	// TODO: make it much more sexy, give some controll over created dynoboxes, destroy unused ones, etc
	initialize: function(wrapper) {

		if($(wrapper)) {

			this.wrapper = $(wrapper);

			this.dynoboxes = this.wrapper.select('div.dynobox');

			this.dynoboxes.each(function(dynobox) {

				dynobox.identify();
				new DynoBox({wrapper: dynobox.id});
			});
		}
		else { throw("DynoLoader: DynoBox wrapper not found at #" + wrapper); }
	}
});

/** section: library helpers
 * class BackSlide
 *
 *  v0.2.1-a
 *  HTML element background image slider
 *  (c) 2010 Maciej Taranienko <matt@prayam.com>
 *  All rights reserved
 *
**/
BackSlide = Class.create({

	// cfg
	delay: 0.01,	// timer interval
	step: 20,	// showing/hiding step in px

	// elms
	elmWrapper: undefined,

	// objs
	objImage: undefined,
	animTimer: undefined,

	// data
	imgPrefix: null,
	images: [],

	// controllers
	isProcessing: false,
	curYoffset: 0,
	safeYoffset: 0,
	curImage: 0,
	nextImage: 0,

	// init
	initialize: function(params) {

		if($(params.elm)) {

			// parse params
			this.elmWrapper = $(params.elm);
			this.imgPrefix = params.prefix;
			this.images = params.images;

			// set objs
			this.objImage = new Image();
			this.objImage.src = this.imgPrefix + this.images[0];
		}
		else throw('BackSlide: No valid elm found at #' + params.elm);
	},

	// public interface
	next: function() {

		var next = this.curImage + 1;

		if(next > (this.images.length - 1)) next = 0;

		this.swap(next);
	},
	swap: function(offset) {

		if(!this.isProcessing) {

			this.isProcessing = true;
			this.nextImage = offset;

			// run the sequence
			this._startHiding();
		}

		// later: store 'next' switch in 1 dimensional queue and fire up after the whole run
		// else { }
	},

	// hiding
	_startHiding: function() {

		// set safe offset for this image
		this.safeYoffset = (this.objImage.height - 2 * this.objImage.height) - 10;

		this.animTimer = new PeriodicalExecuter(this._hideLoop.bind(this), this.delay);
	},
	_hideLoop: function() {

		// advance
		this.curYoffset -= this.step;
		this.elmWrapper.setStyle({ backgroundPosition: 'right ' + this.curYoffset + 'px' });

		// check if not too high already
		if(this.curYoffset <= this.safeYoffset) {

			this.animTimer.stop();
			this.onHideComplete();
		}
	},
	onHideComplete: function() {

		this._loadImage(this.nextImage);
	},

	// loading
	_loadImage: function(offset) {

		if(this.images[offset]) {

			// load new image to the wrapper
			var imagePath = this.imgPrefix + this.images[offset]
			this.elmWrapper.setStyle({ backgroundImage: "url('" + imagePath + "')" });

			// reset object and load new
			this.objImage = new Image();
			this.objImage.src = imagePath;

			// IE? run callback rightaway, normal browser - bruteforce load event
			if(!Prototype.Browser.IE) this.objImage.onload = this.onLoadComplete.bindAsEventListener(this);
			else this.onLoadComplete();
		}
		else throw('BackSlide: Offset out of range, ' + offset);
	},
	onLoadComplete: function(e) {

		this.curImage = this.nextImage;
		this._startShowing();
	},

	// showing
	_startShowing: function() {

		this.animTimer = new PeriodicalExecuter(this._showLoop.bind(this), this.delay);
	},
	_showLoop: function() {		// more precise in-position pixel checking, to avoid disabilities on strange movement loops

		// advance
		this.curYoffset += this.step;
		if(this.curYoffset >= 0) {

			var bPos = 'right top';
			var finished = true;
		}
		else {
			var bPos = 'right ' + this.curYoffset + 'px';
			var finished = false;
		}

		// apply elm
		this.elmWrapper.setStyle({ backgroundPosition: bPos });

		// check
		if(finished) {

			this.animTimer.stop();
			this.onShowComplete();
		}
	},
	onShowComplete: function() {

		this.isProcessing = false;
		this.nextImage = 0;
	}
});


/** section: library helpers
 * class SmallBlend
 *
 *  v0.1.1-a
 *  HTML div/img image blender
 *  (c) 2010 Maciej Taranienko <matt@prayam.com>
 *  All rights reserved
 *
**/
SmallBlend = Class.create({

	// cfg
	duration: 3.0,	// blending process duration :>

	// elms
	elmWrapper: undefined,
	elmImage: undefined,

	// objs
	objImage: undefined,

	// data
	imgPrefix: null,
	images: [],

	// controllers
	isProcessing: false,
	curImage: 0,
	nextImage: 0,
	queuedImage: -1,

	// init
	initialize: function(params) {

		if($(params.elm)) {

			// parse params
			this.elmWrapper = $(params.elm);
			this.imgPrefix = params.prefix;
			this.images = params.images;

			// set objs
			this.objImage = new Image();
			this.objImage.src = this.imgPrefix + this.images[0];
		}
		else throw('BackSlide: No valid elm found at #' + params.elm);
	},

	// public interface
	next: function() {

		var next = this.curImage + 1;

		if(next > (this.images.length - 1)) next = 0;

		this.swap(next);
	},
	swap: function(offset) {

		if(!this.isProcessing) {

			this.isProcessing = true;

			this.nextImage = offset;

			// preload the new image
			this._loadImage(this.nextImage);
		}

		// store 'next in queue' and fire up after the whole run (if needed)
		else this.queuedImage = offset;
	},

	// loading
	_loadImage: function(offset) {

		if(this.images[offset]) {

			//alert('preloading call for: ' + offset);

			// load new image as an imgelm
			var imagePath = this.imgPrefix + this.images[offset]

			this.elmImage = Builder.node('img', { src: imagePath });
			this.elmWrapper.insert({ top: this.elmImage });
			this.elmImage.hide();

			// reset object and load new
			this.objImage = new Image();
			this.objImage.src = imagePath;

			// IE? run callback rightaway, normal browser - bruteforce load event
			if(!Prototype.Browser.IE) this.objImage.onload = this.onLoadComplete.bindAsEventListener(this);
			else this.onLoadComplete();
		}
		else throw('BackSlide: Offset out of range, ' + offset);
	},

	onLoadComplete: function(e) {

		//alert('load complete');

		// fade it in
		new Effect.Appear(this.elmImage, {
			duration: this.duration,
			afterFinish: this.onAppearImage.bindAsEventListener(this)
		});
	},

	onAppearImage: function(e) {

		// set current
		this.curImage = this.nextImage;

		// swap to background and destroy the image elm
		this.elmWrapper.setStyle({ backgroundImage: "url('" + this.imgPrefix +  this.images[this.curImage] + "')" });
		this.elmImage.remove();

		// unset processing
		this.isProcessing = false;
		this.nextImage = 0;

		// check queue
		if(this.queuedImage > -1) {
			if(this.queuedImage != this.curImage) this.swap(this.queuedImage);
			else this.queuedImage = -1;
		}
	}
});

