var SqueezeBox = {

	defaultOptions: {
		size: {x: 600, y: 450},
		sizeLoading: {x: 200, y: 150},
		marginInner: {x: 20, y: 20},
		marginImage: {x: 100, y: 150},

	
		zIndex: 65555,
		type: null,
		insert: null,
		loadingContent: '',
		closeWithOverlay: true,
		overlayOpacity: 0.7,
		classWindow: '',
		classOverlay: '',
		onOpen: Class.empty,
		onClose: Class.empty,
		onUpdate: Class.empty,
		onResize: Class.empty,
		onMove: Class.empty,
		onShow: Class.empty,
		onHide: Class.empty,
		ajaxOptions: {}
	},

	initialize: function(options) {
		if (this.options) return;
		this.setOptions(this.defaultOptions = $merge(this.defaultOptions, options));

		this.overlay = new Element('div').setProperty('id', 'sbox-overlay')
			.setStyles({'display': 'none', 'position': 'absolute'});
		this.content = new Element('div').setProperty('id', 'sbox-content');
		this.btnClose = new Element('a').setProperties({'id': 'sbox-btn-close', 'href': 'javascript:void(null);'})
			.addEvent('click', this.close.bind(this));
		this.window = new Element('div').setProperty('id', 'sbox-window').adopt(this.btnClose, this.content)
			.setStyles({'display': 'none', 'position': 'absolute', 'width': 0, 'height': 0, 'z-index': this.options.zIndex + 2});
		$(document.body).adopt(this.overlay, this.window);

		this.overlayFx = this.overlay.effect('opacity', {duration: 350, wait: false,
			onStart: this.onOverlayEnd, onComplete: this.onOverlayEnd}).set(0);
		this.windowFx = this.window.effects({duration: 750, wait: false,
			onStart: this.hideContent.bind(this), onComplete: this.showContent.bind(this)});
		this.contentFx = this.content.effect('opacity', {duration: 250, wait: false,
			onComplete: window.ie ? this.content.setStyle.bind(this.content, ['filter', '']) : Class.empty}).set(0);

		this.eventWindow = this.squeezeOverlay.bind(this, [null]);
		this.eventKeys = this.onKey.bindWithEvent(this);

		this.isOpen = this.isLoading = false;
	},

	assignOptions: function() {
		this.overlay.setProperty('class', this.options.classOverlay);
		this.window.setProperty('class', this.options.classWindow);
	},

	onOverlayEnd: function() {
		if (this.now) return;
		this.element.setStyle('display', this.to ? '' : 'none').getParent()[this.to ? 'addClass' : 'removeClass']('body-overlayed');
	},

	click: function(target, options) {
		if (!['string', 'element'].test($type(target))) return false;
		this.initialize();

		this.element = $(target);
		if (this.element && this.element.rel) options = $merge(options || {}, Json.evaluate(this.element.rel));
		this.setOptions(this.defaultOptions, options);
		this.assignOptions();
		this.url = (this.element ? (this.element.href || this.options.target) : target) || '';

		var bits = this.url.match(/(?:\.(jpg|jpeg|png|gif|bmp))?(?:#([\w-]+))?$/i);
		if (this.options.type == 'image' || bits[1]) {
			this.image = new Image();
			this.initializeContent('image');
			$extend(this.image, {
				onload: this.loadingComplete.bind(this),
				onerror: this.loadingFailure.bind(this),
				src: this.url});
		} else if ($(this.options.insert) || ($(this.element) && !this.element.parentNode) || (bits[2] && $(bits[2]))) {
			this.initializeContent('element', ($(this.options.insert) || $(bits[2]) || $(this.element)).clone());
		} else if (this.url && !this.url.test(/^javascript:/i)) {
			this.ajax = new Ajax(this.url, $merge({
				onSuccess: this.loadingComplete.bind(this),
				onFailure: this.loadingFailure.bind(this)
			}, this.options.ajaxOptions));
			this.initializeContent('ajax');
			this.ajax.request();
		} else return false;
		return true;
	},


	close: function() {
		if (!this.isOpen) return;
		this.overlayFx.start(0);
		this.window.setStyle('display', 'none');
		if (this.image) this.resetImage();
		this.toggleListeners();
		this.isOpen = null;
		this.fireEvent('onClose', [this.content]);
	},

	loadingComplete: function(resp) {
		if (this.ajax) {
			this.setContent(resp);
			this.ajax = null;
		} else if (this.image) {
			var win = {x: window.getWidth() - this.options.marginImage.x, y: window.getHeight() - this.options.marginImage.y};
			var size = {x: this.image.width, y: this.image.height};
			for (var i = 0; i < 2; i++)
				if (size.x > win.x) { size.y *= win.x / size.x; size.x = win.x;
				} else if (size.y > win.y) { size.x *= win.y / size.y; size.y = win.y; }
			size = {x: parseInt(size.x), y: parseInt(size.y)};
			this.setContent(this.image, size);
			this.image = this.content.getFirst();
			this.image.setProperty('id', 'sbox-image').setStyles({'width': size.x + 'px', 'height': size.y + 'px'});
			this.resetImage();
		}
	},

	loadingFailure: function() {
		if (this.ajax) {
			this.ajax = null;
			this.initializeContent('error', 'Error during loading the page');
		} else if (this.image) {
			this.resetImage();
			this.initializeContent('error', 'Loading image failed');
		}
	},

	resetImage: function() {
		if (this.image) this.image = this.image.onload = this.image.onerror = null;
	},

	initializeContent: function(type, content) {
		this.loader = this.setContent.delay(100, this, content);
	},

	setContent: function(content, size) {
		$clear(this.loader);
		this.hideContent();
		if (!content) {
			this.toggleLoading(true);
			content = this.options.loadingContent;
		} else if (this.isLoading) this.toggleLoading(false);
		this.content.setHTML('')[($type(content) == 'element') ? 'adopt' : 'setHTML'](content || '');
		if (content) this.fireEvent('onUpdate', [this.content]);
		if (!this.isOpen) {
			this.toggleListeners(true);
			this.overlayFx.start(this.options.overlayOpacity);
			this.squeezeWindow(size);
			this.isOpen = true;
			this.fireEvent('onOpen', [this.content]);
		} else this.squeezeWindow(size);
	},

	toggleListeners: function(toggle) {
		var fn = toggle ? 'addEvent' : 'removeEvent';
		if (this.options.closeWithOverlay) this.overlay[fn]('click', this.close.bind(this));
		document[fn]('keydown', this.eventKeys);
		window[fn]('resize', this.eventWindow);
		window[fn]('scroll', this.eventWindow);
	},

	toggleLoading: function(toggle) {
		this.isLoading = toggle;
		this.window[toggle ? 'addClass' : 'removeClass']('sbox-loading');
		if (toggle) this.fireEvent('onLoading', [this.window]);
	},

	showContent: function() {
		if (this.windowFx.timer || this.contentFx.now) return;
		this.contentFx.start(1);
		this.fireEvent('onShow', [this.window]);
	},

	hideContent: function() {
		if (!this.content.opacity) this.fireEvent('onHide', [this.window]);
		this.contentFx.stop().set(0);
	},

	onKey: function(e) {
		switch (e.key) {
			case 'esc': this.close(); break;
		}
	},

	squeezeWindow: function(size) {
		var sizes = window.getSize();
		this.size = $merge(this.isLoading ? this.options.sizeLoading : this.options.size, size);
		var to = {
			'width': this.size.x + 'px', 'height': this.size.y + 'px',
			'left': Math.round(sizes.scroll.x + (sizes.size.x - this.size.x - this.options.marginInner.x) / 2) + 'px',
			'top': Math.round(sizes.scroll.y + (sizes.size.y - this.size.y - this.options.marginInner.y) / 2) + 'px'};
		if (!this.isOpen) {
			this.window.setStyles(to).setStyle('display', '');
			this.showContent.delay(150, this);
		} else this.windowFx.start(to);
		this.squeezeOverlay(sizes);
	},

	squeezeOverlay: function(sizes) {
		sizes = sizes || window.getSize();
		this.overlay.setStyles({
			'left': sizes.scroll.x + 'px', 'top': sizes.scroll.y + 'px',
			'width': sizes.size.x + 'px', 'height': sizes.size.y + 'px'});
		this.window.setStyles({
			'left': Math.round(sizes.scroll.x + (sizes.size.x - this.window.offsetWidth) / 2) + 'px',
			'top': Math.round(sizes.scroll.y + (sizes.size.y - this.window.offsetHeight) / 2) + 'px'});
		this.fireEvent('onMove', [this.overlay, this.window, sizes]);
	}
};

$extend(SqueezeBox, Events.prototype);
$extend(SqueezeBox, Options.prototype);