/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       visual effects and animations library.
 *
 *    @version rev012.2008-06-16
 *    @requires common.js
 */
/* -------------------------------------------------------------------------- */


var BAEffect = {};



/* -------------------- Constructor : BAEffect.Resize inherits BAObservable -------------------- */

BAEffect.Resize = function(node, duration, interval, func) {
	if (!node || node.instanceOf != 'BAElement') {
		throw 'BAEffect.Resize: first argument must be a BAElement node.';
	}

	this.node          = node;
	this.duration      = (duration > 0) ? duration : 500;
	this.interval      = (interval > 0) ? interval :  10;
	this.fromSize      = { width : 0 , height: 0 };
	this.toSize        = { width : 0 , height: 0 };
	this.easingTimer   = null;
	this.elapseTimer   = null;
	this.baseNodeCName = 'BAEffectBase';

	if (BA.env.isDOMReady) {
		this.init();
		this.setEasingFunc(func);
	}
}

BAEffect.Resize.prototype = new BAObservable;

BAEffect.Resize.prototype.init = function() {
	if (!this.node.__BAEffectBase__) {
		this.node.__BAEffectBase__ = true;
		this.node.appendClassNameBA(this.baseNodeCName);
	}
}

BAEffect.Resize.prototype.setEasingFunc = function(func) {
	if (typeof func == 'function' && typeof func(0, 0, 0, 0) == 'number') {
		this.easingFunc = func;
	}
}

BAEffect.Resize.prototype.easingFunc = function(t, b, c, d) {
	// cubic easing in/out - acceleration until halfway, then deceleration
	if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
	return c / 2 *((t -= 2) * t * t + 2) + b;
}

BAEffect.Resize.prototype.getInnerSize = function() {
	var measures = [document.createElementBA('div'), document.createElementBA('div')];
	measures.forEach(function(node) {
		node.style.position   = 'static';
		node.style.display    = 'block';
		node.style.visibility = 'hidden';
		node.style.width      = 'auto';
		node.style.height     = '100%';
		node.style.margin     = '0 0 -100%';
		node.style.padding    = '0';
		node.style.background = 'red';
	});
	this.node.appendChild (measures[0]);
	this.node.appendChild (measures[1]);
	var ret = {
		width  : measures[0].offsetWidth,
		height : measures[0].offsetHeight
	};

	// in case of parentNode's height is 'auto'
	if (ret.height < 1 || this.node.getCurrentStyleBA('height') == 'auto') {
		measures.forEach(function(node) {
			node.style.height       =  '1px';
			node.style.marginBottom = '-1px';
		});
		this.node.insertBefore(measures[0], this.node.firstChild);
		ret.height = measures[1].getAbsoluteOffsetBA().Y - measures[0].getAbsoluteOffsetBA().Y;
	}

	measures.forEach(function(node) {
		this.node.removeChildBA(node);
	}, this);

	return ret;
}

BAEffect.Resize.prototype.resizeTo = function(width, height, func, aThisObject) {
	this.clearTimer();

	if (width  === null) width  = -1;
	if (height === null) height = -1;

	var size = this.getInnerSize();
	this.fromSize.width  = size.width ;
	this.fromSize.height = size.height;
	this.toSize.width    = (width  >= 0) ? width  - this.node.offsetWidth  : 0;
	this.toSize.height   = (height >= 0) ? height - this.node.offsetHeight : 0;
	this.setDisposablePostProcess(func, aThisObject);

	if (this.toSize.width != 0 || this.toSize.height != 0) {
		this.doCallBackByName('onStart');
		this.elapseTimer = new BATimer;
		this.easingTimer = new BASetInterval(this.mainProcess, this.interval, this);
	} else {
		this.postProcess();
	}
}

BAEffect.Resize.prototype.mainProcess = function() {
	var elapse = Math.min(this.elapseTimer.getTime(), this.duration);
	var width  = this.easingFunc(elapse, this.fromSize.width , this.toSize.width , this.duration);
	var height = this.easingFunc(elapse, this.fromSize.height, this.toSize.height, this.duration);
	if (elapse < this.duration) {
		this.node.style.width  = Math.max(Math.floor(width ), 0) + 'px';
		this.node.style.height = Math.max(Math.floor(height), 0) + 'px';
		this.doCallBackByName('onChange');
	} else {
		this.node.style.width  = width  + 'px';
		this.node.style.height = height + 'px';
		this.doCallBackByName('onChange');
		this.postProcess();
	}
}

BAEffect.Resize.prototype.setDisposablePostProcess = function(func, aThisObject) {
	if (typeof func == 'function') {
		return this.addCallBack('onComplete', func, aThisObject, 'disposable');
	}
}

BAEffect.Resize.prototype.postProcess = function() {
	this.clearTimer();
	this.doCallBackByName('onComplete');
}

BAEffect.Resize.prototype.clearTimer = function() {
	if (this.elapseTimer) {
		this.elapseTimer = null;
	}
	if (this.easingTimer) {
		this.easingTimer.clearTimer();
		this.easingTimer = null;
	}
}

BAEffect.Resize.prototype.doCallBackByName = function(name) {
	this.doCallBack(name, this.node.offsetWidth, this.node.offsetHeight);
}






/* -------------------- Constructor : BAEffect.Opacity inherits BAObservable -------------------- */

BAEffect.Opacity = function(node, duration, interval, func) {
	if (!node || node.instanceOf != 'BAElement') {
		throw 'BAEffect.Opacity: first argument must be a BAElement node.';
	}

	this.node           = node;
	this.duration       = (duration > 0) ? duration : 750;
	this.interval       = (interval > 0) ? interval :  10;
	this.opacity        = 100;
	this.fromValue      = 0;
	this.toValue        = 0;
	this.easingTimer    = null;
	this.elapseTimer    = null;
	this.baseNodeCName  = 'BAEffectBase';

	if (BA.env.isDOMReady) {
		this.init();
		this.setEasingFunc(func);
	}
}

BAEffect.Opacity.prototype = new BAObservable;

BAEffect.Opacity.prototype.init = function() {
	if (!this.node.__BAEffectBase__) {
		this.node.__BAEffectBase__ = true;
		this.node.appendClassNameBA(this.baseNodeCName);
	}
	this.setOpacity(this.opacity);
}

BAEffect.Opacity.prototype.setEasingFunc = function(func) {
	if (typeof func == 'function' && typeof func(0, 0, 0, 0) == 'number') {
		this.easingFunc = func;
	}
}

BAEffect.Opacity.prototype.easingFunc = function(t, b, c, d) {
	// cubic easing in/out - acceleration until halfway, then deceleration
	if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
	return c / 2 *((t -= 2) * t * t + 2) + b;
}

BAEffect.Opacity.prototype.fadeIn = function(func, aThisObject) {
	this.fadeTo(100, func, aThisObject);
}

BAEffect.Opacity.prototype.fadeOut = function(func, aThisObject) {
	this.fadeTo(0, func, aThisObject);
}

BAEffect.Opacity.prototype.fadeTo = function(opacity, func, aThisObject) {
	if (opacity === null || isNaN(opacity) || opacity < 0 || opacity > 100) {
		throw 'BAEffect.Opacity.fadeTo: first argument must be a percent number.';
	} 
	
	this.clearTimer();
	this.fromValue   = this.opacity;
	this.toValue     = opacity;
	this.elapseTimer = new BATimer;
	this.easingTimer = new BASetInterval(this.mainProcess, this.interval, this);
	this.doCallBackByName('onStart');
	this.setDisposablePostProcess(func, aThisObject);
}

BAEffect.Opacity.prototype.mainProcess = function() {
	var elapse  = Math.min(this.elapseTimer.getTime(), this.duration);
	var opacity = this.easingFunc(elapse, this.fromValue, this.toValue - this.fromValue, this.duration);
	if (elapse < this.duration) {
		this.setOpacity(opacity);
		this.doCallBackByName('onChange');
	} else {
		this.setOpacity(this.toValue);
		this.doCallBackByName('onChange');
		this.postProcess();
	}
}

BAEffect.Opacity.prototype.changeTo = function(opacity, func, aThisObject) {
	if (opacity === null || isNaN(opacity) || opacity < 0 || opacity > 100) {
		throw 'BAEffect.Opacity.changeTo: first argument must be a percent number.';
	} 
	this.setOpacity(opacity);
	this.setDisposablePostProcess(func, aThisObject);
	this.postProcess();
}

BAEffect.Opacity.prototype.setOpacity = function(opacity) {
	this.opacity            = opacity;
	this.node.style.opacity = String(opacity / 100);
	if (this.node.runtimeStyle) {
		this.node.runtimeStyle.filter = 'Alpha(opacity=' + opacity + ')';
	}
}

BAEffect.Opacity.prototype.setDisposablePostProcess = function(func, aThisObject) {
	if (typeof func == 'function') {
		return this.addCallBack('onComplete', func, aThisObject, 'disposable');
	}
}

BAEffect.Opacity.prototype.postProcess = function() {
	this.clearTimer();
	this.doCallBackByName('onComplete');
}

BAEffect.Opacity.prototype.clearTimer = function() {
	if (this.elapseTimer) {
		this.elapseTimer = null;
	}
	if (this.easingTimer) {
		this.easingTimer.clearTimer();
		this.easingTimer = null;
	}
}

BAEffect.Opacity.prototype.doCallBackByName = function(name) {
	this.doCallBack(name, this.opacity);
}

