(function() {

	SL.effects.hideEmbededObjects = function() {
		$$('object', 'embed', 'iframe').each(function(node) {
			node.setStyle({
				visibility : 'hidden'
			});
			// node.style.display = 'none';
		});
	};

	SL.effects.showEmbededObjects = function() {
		$$('object', 'embed', 'iframe').each(function(node) {
			node.setStyle({
				visibility : 'visible'
			});
			// node.style.display = 'block';
		});
	};

	SL.effects.resetStyles = function(e, hidden) {
		e.setOpacity(1.0);
		e.setStyle({
			height : 'auto',
			overflow : hidden ? 'hidden' : 'visible'
		});
	};

	SL.effects.resetCurrent = function(e) {
		var currentEffect = e.retrieve('effect');
		if (currentEffect) {
			currentEffect.cancel();
			SL.effects.resetStyles(e);
			if (currentEffect.options.slAfterFinish) {
				currentEffect.options.slAfterFinish();
			}
			e.store('effect', undefined);
			return true;
		}
		return false;
	};

	SL.effects.registerCurrent = function(effect) {

		SL.effects.resetCurrent(effect.element);

		if (effect.options.afterFinish) {
			effect.options.slAfterFinish = effect.options.afterFinish;
		}

		effect.options.afterFinish = function(e) {
			var effect = e.retrieve('effect');
			if (effect && effect.options.slAfterFinish) {
				effect.options.slAfterFinish();
			}
			e.store('effect', undefined);
		}.curry(effect.element);
		effect.element.store('effect', effect);
	};

	SL.effects.registerHover = function(e, className) {
		if (!className) {
			className = 'hover';
		}
		e.observe('mouseenter', function() {
			e.addClassName(className);
		});

		e.observe('mouseleave', function() {
			e.removeClassName(className);
		});
	};

	SL.effects.reparentToOffscreen = function(element) {
		element.remove();
		var body = document.getElementsByTagName('body').item(0);
		body.appendChild(element);

		element.setStyle({
			position : 'absolute',
			top : '0px',
			left : '-999em'
		});
		element.show();
	};

	SL.effects.reparentAndReset = function(parent, element) {
		element.remove();
		parent.appendChild(element);
		element.setStyle({
			position : 'static',
			top : 'auto',
			left : 'auto'
		});
		element.show();
	};

	SL.effects.getBodyHeight = function() {
		var body = $$('body')[0];

		var height = body.getHeight();
		var height2 = document.viewport.getHeight() + document.viewport.getScrollOffsets().top;

		if (height2 > height) {
			height = height2;
		}

		if (height <= 0) {
			height = 1000;
		}

		return height;
	};

	SL.effects.Preloader = Class.create({

		initialize : function(element, finishCallback) {

			if (!element) {
				console.log("No element");
				return;
			}

			this.element = element;
			this.finishCallback = finishCallback;

			this.run.bind(this).defer();
		},

		run : function() {

			if (this.element.retrieve('sl_preloaded')) {
				this._emitFinished();
				return;
			}

			var images = this.element.select('img');

			if (images.length == 0) {
				this._emitFinished();
				return;
			}

			this.remaining = images.length;

			images.each(function(e) {
				if (e.readAttribute('width') && e.readAttribute('height')) {
					this.remaining--;
					return;
				}

				var url = e.readAttribute('src');
				if (!url) {
					this.remaining--;
					return;
				}

				var loader = new Image();
				loader.onload = this._onLoad.bind(this);
				loader.onerror = this._onLoad.bind(this);
				loader.onabort = this._onLoad.bind(this);

				loader.src = url;

			}.bind(this));

			if (this.remaining <= 0) {
				this._emitFinished();
			} else {
				setTimeout(this._emitFinished.bind(this), 20000);
			}
		},

		_onLoad : function() {
			this.remaining--;
			if (this.remaining <= 0) {
				this._emitFinished();
			}
		},

		_emitFinished : function() {
			if (!this.finished) {
				this.finished = true;
				this.element.store('sl_preloaded', true);
				if (this.finishCallback) {
					this.finishCallback.defer();
				}
			}
		}
	});

	SL.effects.Queue = Class.create({

		initialize : function(config) {
			this.config = $H(config);
			this.effects = $H();
			this.steps = $A([ 'first', 'last' ]);
			this.currentStep = -1;
		},

		add : function(step, effect) {
			var e = this.effects.get(step);
			if (e == undefined) {
				e = $A();
				this.effects.set(step, e);
			}
			e.push(effect);
			this.effects.set(step, e);
		},

		start : function() {
			if (this.steps.length == 0) {
				_finished();
				return;
			}

			this._iter();
		},

		isEmpty : function() {
			return this.effects.size() <= 0;
		},

		_iter : function() {
			this.currentStep++;

			console.log("Step:", this.currentStep);

			if (this.currentStep >= this.steps.length) {
				this._finished();
				return;
			}

			var effects = this.effects.get(this.steps[this.currentStep]);
			if (!effects || effects.length == 0) {
				this._iter();
				return;
			}

			var duration = 0;

			effects.each(function(e) {
				if (duration < e.options.duration) {
					duration = e.options.duration;
				}
			});

			if (duration <= 0) {
				console.log("No duration:", this.effects.get(this.steps[this.currentStep]));
				this._iter();
				return;
			}

			new Effect.Parallel(effects, {
				duration : duration,
				afterFinish : this._iter.bind(this)
			});
		},

		_finished : function() {
			console.log("Queue finished:", this.config);
			if (this.config.get('afterFinish')) {
				this.config.get('afterFinish')(this);
			}
		}
	});

	SL.effects.PointerState = Class.create({

		initialize : function(e, config) {

			this.e = $(e);
			if (this.e) {
				this.onEnterHandler = this.onMouseEnter.bind(this);
				this.onLeaveHandler = this.onMouseLeave.bind(this);

				this.e.observe('mouseenter', this.onEnterHandler);
				this.e.observe('mouseleave', this.onLeaveHandler);
			}

			this.cfg = config;

			this.hasPointer = false;
		},

		onMouseEnter : function(e) {
			this.hasPointer = true;
			if (this.cfg && this.cfg.enter) {
				this.cfg.enter(e);
			}
		},

		onMouseLeave : function(e) {
			this.hasPointer = false;
			if (this.cfg && this.cfg.leave) {
				this.cfg.leave(e);
			}
		},

		stop : function() {
			if (this.e) {
				this.e.stopObserving(this.onEnterHandler);
				this.e.stopObserving(this.onLeaveHandler);
			}
		}
	});

	SL.effects._initEffect = function(e, wrapped, cfg) {
		var eff = e.retrieve('sl_e_effect');
		if (eff) {
			return eff;
		}

		eff = {};
		eff.visible = e.visible();
		e.store('sl_e_effect', eff);

		if (wrapped) {
			eff.element = e.wrap('div');
			if (!eff.visible) {
				e.setStyle('display:block');
				eff.element.setStyle({
					display : 'none'
				});
			}
		} else {
			eff.element = e;
		}

		if (cfg) {
			if (cfg.duration) {
				eff.duration = cfg.duration / 1000.0;
			} else {
				eff.duration = 0.5;
			}
		}

		return eff;
	};

	SL.effects.toggleVisibility = function(id, actionID, cfg) {

		var e = $(id);
		if (!e) {
			console.log("Element not found:", id);
			return;
		}

		var wrap;

		if (cfg && cfg.mode) {
			wrap = true;
		} else {
			wrap = false;
		}

		var eff = SL.effects._initEffect(e, wrap, cfg);
		e = eff.element;

		if (cfg && cfg.mode) {
			if (cfg.mode == 'slide') {
				SL.effects.resetCurrent(e);

				if (eff.visible) {
					SL.effects.registerCurrent(Effect.BlindUp(e, {
						duration : eff.duration
					}));
					eff.visible = false;
					if ($(actionID)) {
						$(actionID).addClassName('inactive');
						$(actionID).removeClassName('active');
					}
				} else {
					SL.effects.registerCurrent(Effect.BlindDown(e, {
						duration : eff.duration
					}));
					eff.visible = true;
					if ($(actionID)) {
						$(actionID).removeClassName('inactive');
						$(actionID).addClassName('active');
					}
				}
				return;
			}
		}

		if (eff.visible) {
			e.hide();
			if ($(actionID)) {
				$(actionID).addClassName('inactive');
				$(actionID).removeClassName('active');
			}
			eff.visible = false;
		} else {
			e.show();
			if ($(actionID)) {
				$(actionID).removeClassName('inactive');
				$(actionID).addClassName('active');
			}
			eff.visible = true;
		}
	};

	SL.effects.connectHover = function(mouseOverElement, controlledElement, visibility) {

		var c = $(controlledElement);
		var e = $(mouseOverElement);

		if (visibility) {
			c.setStyle({
				visibility : 'hidden'
			});
			e.observe('mouseenter', function() {
				c.setStyle({
					visibility : 'visible'
				});
			});

			e.observe('mouseleave', function() {
				c.setStyle({
					visibility : 'hidden'
				});
			});
			return;
		}

		c.hide();
		e.observe('mouseenter', function() {
			c.show();
		});

		e.observe('mouseleave', function() {
			c.hide();
		});
	};

})();

