/**
 * @author Vlad Yakovlev (red.scorpix@gmail.com)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @link www.scorpix.ru
 * @version 0.1.4
 * @date 2010-03-01
 * @requires jQuery
 * @requires jCommon.support
 * @requires photoSizer
 */

/**
 * Управление стопками изображений.
 */
$(function() {

	var
		/** Количество пикселей драг-н-дропа, при котором возможен переход по ссылке — ошибки пользователя. */
		maxUserMistake = 5,
		proportion = 4 / 3,
		minWidth = 120,
		maxWidth = 240,
		pictures = $('.stack_pictures .picture'),
		stacks = [],
		timeoutId,
		bodyEl = $('<div class="stack_pictures_free"></div>').prependTo('body'),
		winEl = $(window);

	pictures.length && init();

	function init() {
		var setScale = function(scale, step) {
			var width = minWidth + (maxWidth - minWidth) * step;

			clearTimeout(timeoutId);

			$.each(stacks, function() {
				this.hideCanvas();
			});

			timeoutId = setTimeout(function() {
				updateCanvases(width);
			}, 200);
		};

		if (!($.browser.mozilla && 1.9 > parseFloat($.browser.version))) {
			pictures.each(function() {
				if (1 < $(this).find('.preview').length) {
					stacks.push(stackPicture(bodyEl, $(this)));
				}
			});
		}

		setScale(0, photoSizer.getStep());
		photoSizer.onScale(setScale);
	}

	function updateCanvases(width) {
		$.each(stacks, function() {
			this.update(width, width / proportion);
			this.showCanvas();
		});
	}

	function stackPicture(bodyEl, rootEl) {

		var
			previewEls = rootEl.find('.preview'),
			lastEl = previewEls.eq(previewEls.length - 1),
			isDnd = false,
			isMoved = false,
			dndXStart,
			dndYStart,
			dndXMouseOffset,
			dndYMouseOffset,
			wrapEl = rootEl.find('.previews'),
			canvas,
			/**
			 * @type {CanvasRenderingContext2D}
			 */
			ctx,
			imagesLoaded = 0,
			images = [];

		init();

		function init() {
			previewEls.each(function(index) {
				$(this).css({
					marginTop: -2 * index,
					marginLeft: -2 * index
				});

				if ($c.support.canvas) {
					var image = new Image();
					$(image).load(onImageLoaded);
					image.src = $(this).attr('src');
					images.push(image);
				}
			});

			$c.draggable(lastEl).bind(startDnd, dnd, stopDnd);
			lastEl.click(disableImage);
		}

		function onImageLoaded() {
			previewEls.length == ++imagesLoaded && createCanvas();
		}

		/**
		 * @param {Event} evt
		 */
		function startDnd(evt) {
			rootEl.height(rootEl.height());

			var offset = rootEl.offset();
			wrapEl.appendTo(bodyEl).css({
				left: Math.round(offset.left),
				top: Math.round(offset.top)
			});

			dndXStart = parseInt(evt.pageX);
			dndYStart = parseInt(evt.pageY);

			var imageOffset = lastEl.offset();
			dndXMouseOffset = dndXStart - Math.round(imageOffset.left) - 1;
			dndYMouseOffset = dndYStart - Math.round(imageOffset.top) - 1;

			return false;
		}

		/**
		 * @param {Event} evt
		 */
		function dnd(evt, poses) {
			var
				x = parseInt(evt.pageX),
				y = parseInt(evt.pageY),
				maxX = winEl.scrollLeft() + winEl.width() + dndXMouseOffset - lastEl.width(),
				maxY = winEl.scrollTop() + winEl.height() + dndYMouseOffset - lastEl.height(),
				pageX = x > maxX ? maxX : x,
				pageY = y > maxY ? maxY : y,
				rX = pageX - dndXStart,
				rY = pageY - dndYStart;

			if (!isDnd && maxUserMistake > Math.abs(dndXStart - x) && maxUserMistake > Math.abs(dndYStart - y)) {
				isDnd = true;
			}

			previewEls.each(function(i) {
				var k = i / (previewEls.length - 1);
				$(this).css({
					left: Math.round(k * rX),
					top: Math.round(k * rY)
				});
			});

			isMoved = true;

			return false;
		}

		/**
		 * @param {Event} evt
		 */
		function stopDnd(evt) {

			var
				x = parseInt(evt.pageX),
				y = parseInt(evt.pageY);

			if (!isDnd && maxUserMistake >= Math.abs(dndXStart - x) && maxUserMistake >= Math.abs(dndYStart - y)) {
				location.href = rootEl.find('a').attr('href');
				return;
			}

			if (isMoved) {
				var count = previewEls.length;

				previewEls.each(function() {
					$(this).animate({
						left: 0,
						top: 0
					}, {
						duration: 300,
						easing: 'easeInOutCubic',
						complete: function() {
							count--;
							count || onAnimateComplete();
						}
					});
				});
			} else if ($.browser.msie) {
				// В IE баг с кликом на изображении в ссылке.
				lastEl.click();
			}

			isDnd = false;
		}

		function onAnimateComplete() {
			previewEls.css({
				left: '',
				top: ''
			});
			isMoved = false;
			wrapEl.appendTo(rootEl.find('.pwrap a')).css({
				left: '',
				top: ''
			});
			rootEl.css('height', '');
			showCanvas();
		}

		function disableImage(evt) {
			if (isMoved && (maxUserMistake < parseInt(evt.pageX) - dndXStart || maxUserMistake < parseInt(evt.pageY) - dndYStart)) {
				return false;
			}
		}

		function createCanvas() {
			var moved = (previewEls.length - 1) * 2;

			canvas = $('<canvas class="shape"></canvas>').insertBefore(previewEls.eq(0));
			canvas.css({
				left: -moved,
				top: -moved
			});
			ctx = canvas.get(0).getContext('2d');
			updateCanvas(previewEls.eq(0).width(), previewEls.eq(0).height());
			previewEls.addClass('hidden');
			canvas.hover(hideCanvas, null);
			rootEl.hover(null, function() {
				isMoved || showCanvas();
			});
		}

		function updateCanvas(width, height) {
			if (!canvas) return;

			var moved = (previewEls.length - 1) * 2;

			canvas.attr({
				height: height + moved,
				width: width + moved
			});

			for (var i = 0; i < previewEls.length; i++) {
				ctx.drawImage(images[i], 0, 0, images[i].width, images[i].height, moved - i * 2, moved - i * 2, width, height);
			}
		}

		function showCanvas() {
			if ($c.support.canvas && canvas) {
				previewEls.addClass('hidden');
				canvas.removeClass('hidden');
			}
		}

		function hideCanvas() {
			if ($c.support.canvas && canvas) {
				previewEls.removeClass('hidden');
				canvas.addClass('hidden');
			}
		}

		return {
			update: updateCanvas,
			hideCanvas: hideCanvas,
			showCanvas: showCanvas
		};
	}
});
