(function() {
	
	var ef = LSF.UI.Effects = {};
	
	var setPosition = function (div, x, y) {
		div.style.left = x + 'px';
		div.style.top = y + 'px';
	}
	
	var setSize = function (div, w, h) {
		div.style.width = w + 'px';
		div.style.height = h + 'px';
	}
	
	var setOpacity = function (div, opacity) {
		div.style.opacity = opacity;
	};
	
	getOpacity = function (div) {
		return div.style.opacity;
	};

	/*@cc_on
	setOpacity = function (div, opacity) {
		div.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
	},
	
	getOpacity = function (div) {
		if (div.filters) {
			var value = 1;
			try {
				value = div.filters.item('DXImageTransform.Microsoft.Alpha').opacity / 100;
			} catch(e) {
				try {
					value = div.filters.item('alpha').opacity / 100;
				} catch(e) {}
			}		
			return value;
		}
		return 1;
	},
	@*/
	
	ef.Iterator = new Class ({
		
		effects : null,
		duration : 1000,
		steps : 10,
		active : false,
		onEnd : null,
		currentStep : null,
				
		__construct : function (effects, duration, steps) {
			effects = effects instanceof Array ? effects : [effects];
			this.effects = effects;
			duration && (this.duration = duration);
			steps && (this.steps = steps);
		},
		
		run : function (onEnd) {
			if (this.active) throw "Previous effect is not finished yet";
			this.active = true;
			this.currentStep = 0;
			this.onEnd = typeof onEnd == 'function' ? onEnd : LSF.emptyFunction;
			LSF.each (this.effects, function (effect) {
				effect.beginEffect (this.steps);
			}, this);
			this.interval = setInterval (Delegate (this, this.doStep), parseInt (this.duration / this.steps));
		},
		
		doStep : function() {
			LSF.each (this.effects, function (effect) {
				effect.doStep (this.currentStep, this.steps);
			}, this);
			
			this.currentStep++;
			if (this.steps == this.currentStep) {
				clearInterval (this.interval);
				this.active = false;
				LSF.each (this.effects, function (effect) {
					effect.endEffect (this.steps);
				}, this);
				this.onEnd (this);
			}
			
		}
		
	});
	
	ef.Sequence = new Class ({
		
		effectList : null,
		currentEffect : null,
		
		__construct : function (effectList) {
			effectList = effectList instanceof Array ? effectList : [effectList];
			this.effectList = effectList;
		},
		
		run : function (onEnd) {
			this.onEnd = typeof onEnd == 'function' ? onEnd : LSF.emptyFunction;
			this.currentEffect = 0;
			this.runEffect();
		},
		
		runEffect : function() {
			if (this.currentEffect < this.effectList.length) {
				var effect = this.effectList[this.currentEffect];
				effect.run (Delegate (this, 'runEffect'));
				this.currentEffect++;
			} else {
				this.onEnd (this);
			}
		}
		
	});
	
	ef.base = new Class ({
		
		target : null,
		options : null,
		iterator : null,
		__construct : function (target, options, iterator) {
			this.target = target;
			
			options = options || {};
			this.options = options;
			
			if (typeof iterator == 'number') iterator = new ef.Iterator (this, duration);
			this.iterator = iterator;
		},
		
		run : function (onEnd) {
			(this.iterator || new ef.Iterator (this)).run (onEnd);
		},
		
		interpolate : function (start, end, currentStep, totalSteps) {
			return start + (end - start) * currentStep / (totalSteps - 1);
		},
		
		beginEffect : function (totalSteps) {},		
		endEffect : function (totalSteps) {},
		
		doStep : function (currentStep, totalSteps) {
			throw "LSF.Effects.base.doStep is abstract. Please override.";
		}
		
	});
	
	ef.InstantCommand = new Class ({

		callback : null,
		iterator : null,
		__construct : function (callback, iterator) {
			this.callback = callback;	
			this.iterator = iterator || new ef.Iterator (this);
			this.iterator.duration = 1;
			this.iterator.steps = 1;
		},
		beginEffect : function (totalSteps) {
			this.callback();
		},		
		endEffect : function (totalSteps) {},
		doStep : function (currentStep, totalSteps) {},
		run : function (onEnd) {
			this.iterator.run (onEnd);
		}
		
	});
	
	ef.ghostMessage = function (text, options, onEnd) {
		options = options || {};
		var div = document.createElement ('DIV');
		div.className = options.className || 'lsf_effect_ghost_message';
		div.appendChild (document.createTextNode (text));
		document.body.appendChild (div);
		var cw = (document.body.clientWidth - div.clientWidth) / 2;
		
		var f = new LSF.Effect.Iterator (new LSF.Effect.Fade (div, {startOpacity:1,endOpacity:0}));
		var m = new LSF.Effect.Iterator ([
			new LSF.Effect.Move (div, {x:cw, y:0},{x:cw, y:-div.clientHeight}),
			//new LSF.Effect.Resize (div, {width:div.clientWidth, height:div.clientHeight},{width:0, height:div.clientHeight})
		]);
		var w = new LSF.Effect.Iterator (new LSF.Effect.Wait());
		var s = new LSF.Effect.Sequence ([m, w, f]);
		s.run (function() {
			document.body.removeChild (div);
			typeof onEnd == 'function' && onEnd();
		});
	}
	
	ef.Fade = new Class ({

		__construct : function (target, options, iterator) {
			options = options || {};
			if (options.startOpacity === undefined) options.startOpacity = 1;
			if (options.endOpacity === undefined) options.endOpacity = 0;
			this.__callParent (target, options, iterator);
		},
		beginEffect : function (totalSteps) {
			setOpacity (this.target, this.options.startOpacity);
		},		
		endEffect : function (totalSteps) {
			setOpacity (this.target, this.options.endOpacity);
		},		
		doStep : function (currentStep, totalSteps) {
			setOpacity (this.target, this.interpolate (
				this.options.startOpacity, this.options.endOpacity,
				currentStep, totalSteps
			));
		}
		
	}, ef.base);
	
	ef.Wait = new Class ({
		doStep : function() {}
		
	}, ef.base);
	
	ef.Move = new Class ({

		__construct : function (target, to, from, iterator) {
			if (from === undefined) {
				//TODO
			}
			options = {
				from : from,
				to : to
			};
			this.__callParent (target, options, iterator);
		},
		beginEffect : function (totalSteps) {
			this.target.style.position = 'absolute';
			setPosition (this.target, this.options.from.x, this.options.from.y);
		},		
		endEffect : function (totalSteps) {
			setPosition (this.target, this.options.to.x, this.options.to.y);
		},		
		doStep : function (currentStep, totalSteps) {
			setPosition (this.target, this.interpolate (
					this.options.from.x, this.options.to.x,
					currentStep, totalSteps
				), this.interpolate (
					this.options.from.y, this.options.to.y,
					currentStep, totalSteps
				)
			);
		}
		
	}, ef.base);
	
	ef.Resize = new Class ({

		__construct : function (target, to, from, iterator) {
			if (from === undefined) {
				//TODO
			}
			options = {
				from : from,
				to : to
			};
			this.__callParent (target, options, iterator);
		},
		beginEffect : function (totalSteps) {
			this.target.style.position = 'absolute';
			setSize (this.target, this.options.from.width, this.options.from.height);
		},		
		endEffect : function (totalSteps) {
			setSize (this.target, this.options.to.width, this.options.to.height);
		},		
		doStep : function (currentStep, totalSteps) {
			setSize (this.target, this.interpolate (
					this.options.from.width, this.options.to.width,
					currentStep, totalSteps
				), this.interpolate (
					this.options.from.height, this.options.to.height,
					currentStep, totalSteps
				)
			);
		}
		
	}, ef.base);
	
})();
