/**
 * @projectDescription
 * Управление и анимация карты.
 *
 * @author Vlad Yakovlev (scorpix@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @version 0.1 (06.11.2008)
 * @requires jQuery 1.2
 * @requires jTweener 0.2
 */
$(function() {
	/**
	 * Блоки jQuery.
	 */
	var blocks = {
		placesList: $('#around_map .map .places ul'),
		mapBlock: $('#around_map'),
		map: $('#around_map .map'),
		miniMapBlock: $('#around_mini_map'),
		miniMap: $('#around_mini_map .workarea'),
		miniMapVisible: $('#around_mini_map .visible_area'),
		win: $(window),
		scroller: $('#scroller'),
		layout: $('#layout'),
		body: $('body')
	};

	/**
	 * Размеры карты: ширина (<code>width</code>) высота (<code>height</code>).
	 * @type {Object}
	 */
	var mapSize = {
		height: blocks.map.height(),
		width: blocks.map.width()
	};

	/**
	 * Размеры миникарты: ширина (<code>width</code>) высота (<code>height</code>).
	 * @type {Object}
	 */
	var miniMapSize = {
		height: blocks.miniMap.height(),
		width: blocks.miniMap.width()
	};

	/**
	 * Размеры окна браузера: ширина (<code>width</code>) высота (<code>height</code>).
	 * @type {Object}
	 */
	var winSize;

	/**
	 * Размеры блока карты: ширина (<code>width</code>) высота (<code>height</code>).
	 * @type {Object}
	 */
	var mapBlockSize;

	/**
	 * Смещенеие карты относительно левого верхнего угла окна браузера: ширина (<code>width</code>) высота (<code>height</code>).
	 * @type {Object}
	 */
	var mapOffset;

	/**
	 * Позиция по вертикали блока миникарты.
	 * @type {Number}
	 */
	var miniMapBlockTop;

	var miniDndMouseParams;

	var miniDndMapPos;

	var dndMouseParams;

	var dndMapPos;

	/**
	 * Префикс типа здания.
	 */
	var typePrefix = 'type_';

	/**
	 * Коэффициент подобия большой карты к малой.
	 * @type {Number}
	 */
	var normalToMini = mapSize.width / miniMapSize.width;

	/**
	 * Флаг анимации.
	 */
	var isAnimate = false;

	/**
	 * Флаг D'n'D.
	 */
	var isMiniDnd = false;

	/**
	 * Флаг D'n'D.
	 */
	var isDnd = false;

	/**
	 * Флаг, можно ли перемещать блок миникарты.
	 */
	var isMiniMayMove = true;

	// Событие изменения маркировки для зданий.
	$('#content .filters .filter').click(function(e) {
		if ($(e.target).hasClass('pseudo_link')) {
			setMark($(this));
		}
	});

	blocks.win.load(onLoad).resize(onResize);
	blocks.scroller.scroll(onScroll);

	/**
	 * Событие загрузки окна браузера.
	 */
	function onLoad() {
		updateParams();
		updateMiniMapParams();
		initMiniMap();

		// События клика мышки по области, в которую нужно переместить карту.
		blocks.miniMap.click(function(e) {
			if (!$(e.target).hasClass('visible_area')) {
				moveMap(e);
			}
		});

		blocks.miniMapVisible.mousedown(startMiniDnd).mousemove(miniDnd).mouseout(finishMiniDnd).mouseup(finishMiniDnd);
		blocks.map.mousedown(startDnd).mousemove(dnd).mouseout(miniDnd).mouseup(finishDnd);
	}

	/**
	 * Событие ресайза окна браузера.
	 */
	function onResize() {
		updateParams();
		updateMiniMapParams();
	}

	/**
	 * Событие скроллирования окна браузера.
	 */
	function onScroll() {
		updateParams();
		// Если идет анимация, параметры миникарты обновляются в ней.
		isAnimate || updateMiniMapParams();
	}

	/**
	 * Обновляет основные параметры (размеры и отступы блоков).
	 */
	function updateParams() {
		winSize = {
			height: blocks.win.height(),
			width: blocks.win.width()
		};
		mapOffset = blocks.map.offset();
		mapBlockSize = {
			height: blocks.mapBlock.height(),
			width: blocks.mapBlock.width()
		};

		var newOffsetLeft = 1;

		if (winSize.width > mapSize.width && 0 > mapOffset.left) {
			newOffsetLeft = 0;
		} else if (mapSize.width - winSize.width + 30 < Math.abs(mapOffset.left)) {
			newOffsetLeft = -mapSize.width + winSize.width;
		}

		if (1 > newOffsetLeft) {
			blocks.map.css('left', newOffsetLeft);
			blocks.miniMapVisible.css('left', getMiniParam(newOffsetLeft));
			mapOffset = blocks.map.offset();
		}

		blocks.miniMapVisible.css({
			height: getMiniParam(mapBlockSize.height > winSize.height ? winSize.height : mapBlockSize.height),
			width: getMiniParam(mapBlockSize.width > winSize.width ? winSize.width : mapBlockSize.width)
		});
	}

	/**
	 * Перемещает блок с миникартой за пределы <code>#scroller</code>.
	 */
	function initMiniMap() {
		blocks.body.append(blocks.miniMapBlock);
	}

	/**
	 * Ресайзит у миникарты красную область.
	 */
	function updateMiniMapParams() {
		var mapBlockOffset = blocks.mapBlock.offset();

		blocks.miniMapVisible.css({
			left: getMiniParam(mapBlockOffset.left - mapOffset.left + blocks.scroller.scrollLeft()),
			top: getMiniParam(-mapBlockOffset.top)
		});
	}

	/**
	 * Анимирует перемещение карты.
	 * @type {Event} event Событие мыши.
	 */
	function moveMap(event) {

		isAnimate = true;

		jTweener.removeTween(blocks.map);

		var miniMapOffset = blocks.miniMap.offset();

		var coords = {
			left: event.pageX - miniMapOffset.left,
			top: event.pageY - miniMapOffset.top
		};

		// У нас 4 сектора, куда может переместиться карта. Выясняем, в какой из них перемещаемся.
		var sector = {
			left: coords.left < Math.round(miniMapSize.width / 2),
			top: coords.top < Math.round(miniMapSize.height / 2)
		};

		var newMapVisibleSize = {
			height: winSize.height < mapSize.height ? winSize.height : mapSize.height,
			width: winSize.width < mapSize.width ? winSize.width : mapSize.width
		};

		var newMiniMapVisibleSize = {
			height: getMiniParam(newMapVisibleSize.height),
			width: getMiniParam(newMapVisibleSize.width)
		};

		var startMiniParams = {
			left: parseInt(blocks.miniMapVisible.css('left')),
			top: parseInt(blocks.miniMapVisible.css('top'))
		};

		var finishMiniParams = {
			left: sector.left ? 0 : miniMapSize.width - newMiniMapVisibleSize.width,
			top: sector.top ? 0 : miniMapSize.height - newMiniMapVisibleSize.height
		};

		var layoutOffset = blocks.layout.offset();

		var newParams = {
			left: sector.left ? 0 : mapBlockSize.width - mapSize.width,
			top: sector.top ? mapOffset.top - layoutOffset.top : mapOffset.top - layoutOffset.top + mapSize.height - newMapVisibleSize.height
		};

		/** @type {Number} */
		var startPos = blocks.scroller.scrollTop();

		jTweener.addTween(blocks.map, {
			left: newParams.left,
			transition: 'easeinoutcubic',
			moveX: function(value) {
				blocks.scroller.scrollTop(startPos + Math.round(value * (newParams.top - startPos)));
				blocks.miniMapVisible.css({
					left: startMiniParams.left + Math.round(value * (finishMiniParams.left - startMiniParams.left)),
					top: startMiniParams.top + Math.round(value * (finishMiniParams.top - startMiniParams.top))
				});
			},
			onComplete: function() {
				isAnimate = false;
			}
		});
	}

	function startMiniDnd(event) {
		if (isAnimate) {
			return false;
		}

		isMiniDnd = true;

		miniDndMouseParams = {
			left: event.pageX,
			top: event.pageY
		};

		var mapBlockOffset = blocks.mapBlock.offset();
		var layoutOffset = blocks.layout.offset();

		miniDndMapPos = {
			left: mapBlockOffset.left - mapOffset.left,
			top: blocks.scroller.scrollTop() - mapBlockOffset.top - layoutOffset.top
		};

		return false;
	}

	function miniDnd(event) {
		if (!isMiniDnd) {
			return;
		}

		var mouseParams = {
			left: event.pageX,
			top: event.pageY
		};
		var diffParams = {
			left: mouseParams.left - miniDndMouseParams.left,
			top: mouseParams.top - miniDndMouseParams.top
		};
		miniDndMouseParams = mouseParams;

		var layoutOffset = blocks.layout.offset();
		var mapBlockOffset = blocks.mapBlock.offset();

		var minTop = mapBlockOffset.top - layoutOffset.top;
		var maxTop = mapBlockOffset.top - layoutOffset.top + mapSize.height - winSize.height;

		var borders = {
			maxLeft: 0,
			minLeft: mapBlockSize.width < mapSize.width ? mapBlockSize.width - mapSize.width : 0,
			minTop: miniDndMapPos.top < minTop ? miniDndMapPos.top : minTop,
			maxTop: miniDndMapPos.top > maxTop ? miniDndMapPos.top : maxTop
		};
		var newParams = {
			left: parseInt(blocks.map.css('left')) - diffParams.left * normalToMini,
			top: blocks.scroller.scrollTop() + diffParams.top * normalToMini
		};


		if (borders.maxLeft < newParams.left) {
			newParams.left = borders.maxLeft;
		} else if (borders.minLeft > newParams.left) {
			newParams.left = borders.minLeft;
		}

		if (borders.maxTop < newParams.top) {
			newParams.top = borders.maxTop;
		} else if (borders.minTop > newParams.top) {
			newParams.top = borders.minTop;
		}

		miniDndMapPos = {
			left: newParams.left,
			top: newParams.top
		};

		blocks.map.css('left', newParams.left);
		blocks.scroller.scrollTop(newParams.top);
		onScroll();

		return false;
	}

	function finishMiniDnd() {
		isMiniDnd = false;
	}

	function startDnd(event) {
		if (isAnimate) {
			return false;
		}

		isDnd = true;

		dndMouseParams = {
			left: event.pageX,
			top: event.pageY
		};

		var mapBlockOffset = blocks.mapBlock.offset();
		var layoutOffset = blocks.layout.offset();

		dndMapPos = {
			left: mapBlockOffset.left - mapOffset.left,
			top: blocks.scroller.scrollTop() - mapBlockOffset.top - layoutOffset.top
		};

		return false;
	}

	function dnd(event) {
		if (!isDnd) {
			return;
		}

		var mouseParams = {
			left: event.pageX,
			top: event.pageY
		};
		var diffParams = {
			left: mouseParams.left - dndMouseParams.left,
			top: mouseParams.top - dndMouseParams.top
		};
		dndMouseParams = mouseParams;

		var layoutOffset = blocks.layout.offset();
		var mapBlockOffset = blocks.mapBlock.offset();

		var minTop = mapBlockOffset.top - layoutOffset.top;
		var maxTop = mapBlockOffset.top - layoutOffset.top + mapSize.height - winSize.height;

		var borders = {
			maxLeft: 0,
			minLeft: mapBlockSize.width < mapSize.width ? mapBlockSize.width - mapSize.width : 0,
			minTop: dndMapPos.top < minTop ? dndMapPos.top : minTop,
			maxTop: dndMapPos.top > maxTop ? dndMapPos.top : maxTop
		};
		var newParams = {
			left: parseInt(blocks.map.css('left')) + diffParams.left,
			top: blocks.scroller.scrollTop() - diffParams.top
		};

		if (borders.maxLeft < newParams.left) {
			newParams.left = borders.maxLeft;
		} else if (borders.minLeft > newParams.left) {
			newParams.left = borders.minLeft;
		}

		/*if (borders.maxTop < newParams.top) {
			newParams.top = borders.maxTop;
		} else if (borders.minTop > newParams.top) {
			newParams.top = borders.minTop;
		}*/

		dndMapPos = {
			left: newParams.left,
			top: newParams.top
		};

		blocks.map.css('left', newParams.left);
		blocks.scroller.scrollTop(newParams.top);
		onScroll();

		return false;
	}

	function finishDnd() {
		isDnd = false;
	}

	/**
	 * Возвращает по значению параметра карты значение параметра для миникарты.
	 * @param {Number} normalValue Значение параметры карты.
	 * @return {Number} Значение параметра миникарты.
	 */
	function getMiniParam(normalValue) {
		return Math.round(parseFloat(normalValue) / normalToMini);
	}

	/**
	 * Возвращает тип по классу.
	 * @param {String} classesPlain Значение атрибута <code>class</code>.
	 * @return {String} Идентификатор типа или пустая строка.
	 */
	function getType(classesPlain) {
		/** @type {Array} */
		var classes = classesPlain.split(' ');

		for (var i = 0; i < classes.length; i++) {
			if (typePrefix == classes[i].substr(0, typePrefix.length)) {
				return classes[i].substr(typePrefix.length);
			}
		}

		return '';
	}

	/**
	 * Ставит и снимает отметки подсвеченных мест.
	 * @param {jQuery} section Секция, по которой кликнули.
	 */
	function setMark(section) {
		var type = getType(section.attr('class'));

		if (!type) {
			return;
		}

		/** @type {jQuery} */
		var curPlaces = blocks.placesList.filter('.' + type).parent();

		if (section.hasClass('selected')) {
			section.removeClass('selected');
			curPlaces.removeClass('selected');
		} else {
			section.addClass('selected');
			curPlaces.addClass('selected');
		}
	}
});


$(function() {
	var logo = $('#around_map .logo_container');

	var hover = logo.find('.hover');

	var borderHover = $('#around_map .border_hover');

	if (hover.size()) {
		logo.find('.mask').removeClass('hidden');
		hover.removeClass('hidden');
	}

	var startLeft = 0;

	var finishLeft = logo.width() - hover.width();

	borderHover.hover(onHoverIn, onHoverOut);

	function onHoverIn() {
		jTweener.removeTween(hover);
		jTweener.addTween(hover, {
			left: finishLeft,
			time: 0.3
		});
	}

	function onHoverOut() {
		jTweener.removeTween(hover);
		jTweener.addTween(hover, {
			left: startLeft,
			time: 0.3
		});
	}
});
