/* -------------------------------------------------------------------------- */
/**
 *    @fileoverview
 *       privides crossfade transition
 *
 *    @version 2.1.20100517
 *    @requires jquery.js
 *    @requires bajl.js
 *    @requires bajl.fontSizeObserver.js  (optional)
 */
/* -------------------------------------------------------------------------- */
(function($) {



/* -------------------- settings for BAJL.Crossfader -------------------- */
/**
 * default setting for {@link BAJL.Crossfader}
 * @namespace default setting for {@link BAJL.Crossfader}
 * @fieldOf BAJL.settings
 * @property {Boolean}                  autoSetup.enabled          autosetup is enabled or not.
 * @property {Object}                   presets                    for autosetup; pairs of jQuerySelector and {@link BAJL.Crossfader.Setting} object
 * @property {BAJL.Crossfader.Setting}  presets[jQuerySelector]    for autosetup; crossfader setting object for an element which is indicated by jQuerySelector
 */
BAJL.settings.Crossfader = {
	  'autoSetup' : {
		  'enabled'  : false
	}
	, 'presets'   : {
		  'div.bajl-crossfader' : {
			  'autoStart' : true
			, 'interval'  : 3000
			, 'duration'  : 1000
			, 'group'     : 'div.bajl-crossfader-group'
			, 'units'     : 'div.bajl-crossfader-unit'
			, 'prevBtn'   : 'li.bajl-crossfader-prev-btn a'
			, 'nextBtn'   : 'li.bajl-crossfader-next-btn a'
			, 'selectBtn' : 'li.bajl-crossfader-select-btn a'
		}
	}
	, 'className' : {
		  'enabled'   : 'bajl-crossfader-enabled'
		, 'discarded' : 'bajl-crossfader-discarded'
		, 'selected'  : 'bajl-slidecanvas-selected-unit'
	}
	, 'buttons' : {
		  'className' : {
			  'disabled' : 'pseudo-disabled'
			, 'selected' : 'pseudo-selected'
		}
	}
};



/* -------------------- AutoSetup : BAJL.Crossfader -------------------- */

(function() {
	var settings = BAJL.settings.Crossfader;
	var sheets   = BAJL.StyleSheets();
	if (settings.autoSetup.enabled) {
		$.each(settings.presets, function(expr, setting) {
			sheets.insertRule(expr + '{ visibility : hidden }');
			$(function() {
				$(expr).each(function() { new BAJL.Crossfader(this, setting) });
			});
		});
	}
})();



/* -------------------- Class : BAJL.Crossfader -------------------- */
/**
 * crossfader behavior controller.
 * @class crossfader behavior controller
 * @extends BAJL.Observable
 * @param {Element|jQuery|String}   node       a base element node to apply crossfader
 * @param {BAJL.Crossfader.Setting} setting    setting object for the crossfader
 */
BAJL.Crossfader = function(node, setting) {
	if (!BAJL.env.isDOMReady) return;
	setting = $.extend(new BAJL.Crossfader.Setting, setting);

	/** jQuery object indicating base element of this instance.
	    @type Element
	    @private */
	this.$node      = $(node);
	/** jQuery object indicating container element ot the content-units.
	    @type Element
	    @private */
	this.$group     = $(setting.group);
	/** jQuery object indicating the content-unit elements which are switched.
	    @type Element
	    @private */
	this.$units     = $(setting.units);
	/** jQuery object indicating the element nodes which are switched with crossfade.
	    @type Element
	    @private */
	this.$selectBtn = $(setting.selectBtn);
	/** an array of instances of prev buttons.
	    @type BAJL.SlideCanvas.StepButton[]
	    @private */
	this.$prevBtn   = $(setting.prevBtn);
	/** an array of instances of next buttons.
	    @type BAJL.SlideCanvas.StepButton[]
	    @private */
	this.$nextBtn   = $(setting.nextBtn);
	/** index number of currently selected content-unit.
	    @type Number
	    @private */
	this.index      = -1;
	/** an array of the content-units elements reverse-sorted by z-order.
	    @type Element[]
	    @private */
	this.order      = this.$units.get();
	/** duration (ms) for crossfade transision.
	    @type Date
	    @private */
	this.duration   = setting.duration;
	/** interval (ms) for switching.
	    @type Date
	    @private */
	this.interval   = setting.interval;

	this.init();
	if (setting.autoStart) this.start();
}

BAJL.Crossfader.prototype = new BAJL.Observable;

/* ---------- class methods/props ---------- */

/**
 * an array of instances of this class.
 * @type BAJL.Crossfader[]
 */
BAJL.Crossfader.instances = [];

/**
 * store an instance created from this class
 * @param {BAJL.Crossfader} instance    an instance object to store
 * @return an instance object stored
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.storeInstance = function(instance) {
	if (!instance || !(instance instanceof BAJL.Crossfader)) {
		throw new TypeError('BAJL.Crossfader.storeInstance: first argument must be an instance of BAJL.Crossfader');
	} else {
		$(instance.$node).data('BAJL.Crossfader.InstanceID', this.instances.push(instance) - 1);
	}
}

/**
 * get an instance created from this class
 * @param {Number|Element|jQuery|String} arg    instance-ID number, or element node which was applied to this class
 * @return BAJL.Crossfader instance
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.getInstance = function(arg) {
	if (typeof arg == 'number') {
		return this.instances[arg];
	} else if (arg && (arg.nodeType == Node.ELEMENT_NODE || typeof arg.jquery == 'string' || typeof arg == 'string')) {
		return this.instances[$(arg).data('BAJL.Crossfader.InstanceID')];
	} else {
		throw new TypeError('BAJL.Crossfader.getInstance: first argument must be an ID number, element node, jQuery object, or jQuery selector text.');
	}
}

/**
 * dipose an instance created from this class
 * @param {BAJL.Crossfader} instance    an instance object to delete
 * @return an instance object stored
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.disposeInstance = function(instance) {
	if (!instance || !(instance instanceof BAJL.Crossfader)) {
		throw new TypeError('BAJL.Crossfader.disposeInstance: first argument must be an instance of BAJL.Crossfader');
	} else if (instance.$node) {
		BAJL.Crossfader.instances.splice(instance.$node.data('BAJL.Crossfader.InstanceID'), 1, undefined);
		instance.dispose(true);
	}
}

/* ---------- instance methods ---------- */

/**
 * initialize
 * @private
 */
BAJL.Crossfader.prototype.init = function() {
	new BAJL.Timeout(function() { this.$node.css('visibility', 'visible') }, 1, this);
	
	var blockCName = BAJL.settings.Crossfader.className;
	var btnCname   = BAJL.settings.Crossfader.buttons.className;

	switch (this.$units.size()) {
		case 0 :
			break;
		case 1 :
			this.index = 0;
			this.$node.addClass(blockCName.enabled).addClass(blockCName.discarded);
			break;
		default :
			this.$node.addClass(blockCName.enabled)

			// hide blocks to prepare fade-in effect.
			this.$units.hide();

			// adjust height on changing displaying fontsize of the browser
			if (BAJL.FontSizeObserver) {
				BAJL.Singleton(BAJL.FontSizeObserver).addCallback('onChange', this.flatHeights, this);
			}

			// setup prev buttons
			this.$prevBtn.click(BAJL.Delegate(function(e) {
				e.preventDefault();
				e.currentTarget.blur();
				if (!this.$prevBtn.hasClass(btnCname.disabled)) {
					this.prev();
				}
			}, this));

			// setup next buttons
			this.$nextBtn.click(BAJL.Delegate(function(e) {
				e.preventDefault();
				e.currentTarget.blur();
				if (!this.$nextBtn.hasClass(btnCname.disabled)) {
					this.next();
				}
			}, this));

			// setup control buttons
			this.$selectBtn
				.each (function(i) { $(this).data('BAJL.Crossfader.Button', i) })
				.click(BAJL.Delegate(function(e) {
					e.preventDefault();
					this.select($(e.currentTarget).blur().data('BAJL.Crossfader.Button'));
				}, this));

			// start rotation
			this.flatHeights(0).select(0);
			break;
	}
	BAJL.Crossfader.storeInstance(this);
}

/**
 * dispose this instace
 * @param {Boolean} preventRecursion    'true' to prevent recursion
 */
BAJL.Crossfader.prototype.dispose = function(preventRecursion) {
	if (!preventRecursion) {
		BAJL.Crossfader.disposeInstance(this);
	} else {
		$.each(this, BAJL.Delegate(function(prop) {
			delete this[prop];
			if (typeof this[prop] == 'function') this[prop] = new Function;
		}, this));
	}
}

/**
 * start rotation
 * @return this instance
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.prototype.start = function() {
	this.stop();
	this.timer = new BAJL.Interval(this.next, this.interval, this);
	return this;
}

/**
 * stop rotation
 * @return this instance
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.prototype.stop = function() {
	if (this.timer) {
		this.timer.clear();
		this.timer = null;
	}
	return this;
}

/**
 * switch to specified content-unit
 * @param {Number} [index=0]    index number to select
 * @return this instance
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.prototype.select = function(index) {
	var running  = Boolean(this.timer);
	var btnCname = BAJL.settings.Crossfader.buttons.className;
	var index    = Math.max(index, 0) || 0;
	var unit     = this.$units.get(index);
	if (unit && index != this.index) {
		this.stop();

		// enable/disable buttons
		this.$selectBtn.removeClass(btnCname.selected).eq(index).addClass(btnCname.selected);
		this.$prevBtn  .toggleClass(btnCname.disabled, index == 0);
		this.$nextBtn  .toggleClass(btnCname.disabled, index == this.$units.size() - 1);

		// reorder units and fade in.
		this.order.unshift(this.order.splice(this.order.indexOf(unit), 1)[0]);
		this.order.reverse().forEach(function(node, i) { $(node).css('z-index', i + 1) });
		this.order.reverse();
		$(this.order[0]).stop(false, true).hide().fadeIn(this.duration);
		if (this.index >= 0) {
			$(this.order[1]).stop(false, true).show().fadeOut(this.duration);
		}
		this.index = index;

		// postprocess
		if (running) {
			this.start();
		}
		this.doCallback('onSelect', index);
	}
	return this;
}

/**
 * switch to previous content-unit
 * @return this instance
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.prototype.prev = function() {
	var index = this.index - 1;
	if (index < 0) {
		index = this.$units.size() - 1;
	}
	this.select(index);
	return this;
}

/**
 * switch to next content-unit
 * @return this instance
 * @type BAJL.Crossfader
 */
BAJL.Crossfader.prototype.next = function() {
	var index = this.index + 1;
	if (!this.$units.get(index)) {
		index = 0;
	}
	this.select(index);
	return this;
}

/**
 * flat all height of pages to max height of the pages.
 * @param {Number} [duration=500]    duration of animation
 * @return this instance
 * @type BAJL.Crossfader
 * @private
 */
BAJL.Crossfader.prototype.flatHeights = function(duration) {
	    duration = (duration >= 0) ? duration : 500;
	var $node    = this.$group;
	var current  = $node.height();
	var target   = this.$units
	               	.map (function() { return $(this).height() })
	               	.sort(function(a, b) { return a < b })[0];
	var callback = BAJL.Delegate(function() {
		this.$node.hide().show();
	}, this);
	$node.height(current).stop().animate({ 'height' : target }, duration, 'swing', callback);

	return this;
}



/* -------------------- Class : BAJL.Crossfader.Setting -------------------- */
/**
 * setting data object for {@link BAJL.Crossfader}
 * @class setting data object for {@link BAJL.Crossfader}
 */
BAJL.Crossfader.Setting = function() {
	/** container element ot the content-units elements.
	    @type NodeList|Element[]|jQuery|String */
	this.group     = 'div.bajl-crossfader-group';
	/** content-unit elements which are switched.
	    @type NodeList|Element[]|jQuery|String */
	this.units     = 'div.bajl-crossfader-unit';
	/** element nodes which are switched with crossfade effect.
	    @type NodeList|Element[]|jQuery|String */
	this.buttons   = 'ul.bajl-crossfader-btns a';
	/** if true, autostart switching with crossfade effect.
	    @type Boolean */
	this.autoStart = true;
	/** duration (ms) for crossfade transision.
	    @type Number */
	this.duration  = 1000;
	/** interval (ms) for switching.
	    @type Number */
	this.interval  = 5000;
}



/* -------------------- for JSDoc toolkit output -------------------- */
/**
 * callback functions for {@link BAJL.Crossfader}
 * @name BAJL.Crossfader.callback
 * @namespace callback functions for {@link BAJL.Crossfader}
 */
/**
 * a callback for when changed current displaying content-unit
 * @name BAJL.Crossfader.callback.onSelect
 * @function
 * @param {Number} index    index number of current displaying content-unit
 */



})(jQuery);

