// HTML4 / HTML5: No differences
define( ["jquery" ], function (jq ) {

	"use strict";

        var EventUtil = (function() {

            function getFirstSupportedEvent(tagName, eventNames) {
                var result = "";

                jq.each(eventNames.split(" "), function (i, evtName) {
                    if (isEventSupported(evtName, tagName)) {
                        result = evtName;
                        return false;
                    }
                });
                return result;
            }

            function isEventSupported(eventName, tagName) {
                var elem = tagName ? document.createElement(tagName) : document,
                    onEventName = "on" + eventName,
                    isSupported;

                isSupported = !!(onEventName in elem);
                if (!isSupported && typeof elem.setAttribute !== "undefined") {
                    elem.setAttribute(onEventName, "return;");
                    isSupported = typeof elem[onEventName] === "function";
                }

                return isSupported;
            }

            function attachSupportedEvents(element, eventNames, handler) {
                var jqObject = jq(element);
                jq.each(eventNames.split(" "), function (i, evtName) {
                    if (isEventSupported(evtName, jqObject.get(0).nodeName)) {
                        jqObject.on(evtName, handler);
                    }
                });
            }

            return {
                /**
                 * Attach the event handler <b>handler</b> for the first supported event specified by <b>eventNames</b>
                 * to the element <b>jqObject</b>.
                 * Example: attachFirstSupportedEvent( myObject, "pointerdown mousedown touchend", myHandler );
                 *
                 * @param element
                 * @param {String} eventNames
                 * @param handler
                 * @return {String} the event name or ""
                 */
                attachFirstSupportedEvent: function (element, eventNames, handler) {
                    var jqObject = jq(element),
                        evtName = getFirstSupportedEvent(element.nodeName, eventNames);

                    if (!evtName) {
                        return "";
                    }

                    jqObject.on(evtName, handler);
                    return evtName;
                },

                attachSupportedEvents : attachSupportedEvents,

                /**
                 * Remove attached event handler <b>handler</b> for the first supported event specified by <b>eventNames</b>
                 * from the element <b>jqObject</b>.
                 * Example: removeFirstSupportedEvent( myObject, "pointerdown mousedown touchend", myHandler );
                 *
                 * @param element
                 * @param {String} eventNames
                 * @param handler
                 * @return {String} the event name or ""
                 */
                removeFirstSupportedEvent: function (element, eventNames, handler) {
                    var jqObject = jq(element),
                        evtName = getFirstSupportedEvent(element.nodeName, eventNames);

                    if (!evtName) {
                        return "";
                    }

                    jqObject.off(evtName, handler);
                    return evtName;
                }
            };
        })();

	/**
	 * @constructor
	 */
	return function (context, metadata) {
		var shell = null,
			shield = null,
			picWidth = 1024,
			picHeight = 682,
			shellWidth = 300,
			shellHeight = 0,
			magnifierSize = 180,
			imageFileNamePath,
			isInAuthoringMode = false,
			hasStageTrigger = true;

		function initializeParameters(parameters) {
			isInAuthoringMode = !!(context.stage.isInAuthoringMode());
			hasStageTrigger = !!context.stage.trigger;
			shellWidth = parameters.asNumber("wrapperWidth", "300");
			if (shellWidth === 0) {
				return;
			}

			var pic = parameters.get("picture", "");
			if (pic) {
				imageFileNamePath = context.stage.getSharedPath() + pic.fileName;
				picWidth = parseFloat(pic.width);
				picHeight = parseFloat(pic.height);
			} else {
				imageFileNamePath = context.getPath() + "lens.jpg";
			}

			shellHeight = picWidth !== 0 ? (shellWidth / picWidth) * picHeight : 0;
		}

		function getStageZoom() {
			var stageElement = document.getElementById("stage-viewport");
			return stageElement ? stageElement.getBoundingClientRect().width / stageElement.offsetWidth : 1;
		}

		function setDimensionsOfShellElement() {
			shell = jq(context.getElement())
				.css({
					width: shellWidth + "px",
					height: shellHeight + "px"
				});
			shell.parents(".ttkfPresObjRefFG")
				.css({
					width: shellWidth + "px",
					height: shellHeight + "px"
				});
		}

		function createShieldInAuthoringMode() {
			if (!isInAuthoringMode) {
				return;
			}
			shield = jq(document.createElement("div"))
				.css({
					"position": "absolute",
					"left": 0,
					"background-color": "#fff",
					"opacity": "0.4",
					"width": shellWidth + "px",
					"height": shellHeight + "px",
					"z-index": "9999"
				});
			shell.append(shield);
		}

		function createMarkup() {
			shell.append(createMagnifierWrapper());
		}

		function createMagnifierWrapper() {
			return jq(document.createElement("div"))
				.addClass("magnifierWrapper")
				.css({
					"position": "absolute",
					"width": "100%",
					"height": "100%",
					"left": 0,
					"top": 0
				})
				.append(createMagnifierImage(), createRetina());
		}

		function createMagnifierImage() {
			return jq(document.createElement("img"))
				.attr({
					"id": "magnifierImage" + metadata.getId(),
					"src": imageFileNamePath,
					"width": shellWidth,
					"height": shellHeight,
					"alt": "Image"
				})
				.css({
					"position": "absolute",
					"left": 0,
					"top": 0
				})
				.on("touchmove", function(e) {e.preventDefault ? e.preventDefault() : true;});
		}

		function createRetina() {
			var cursorFile = context.getPath() + "blank.cur",
				cursor,
				halfMagnifierSize = magnifierSize / 2;

			if (navigator.userAgent.toLowerCase().indexOf("chrome") !== -1) {
				/*	Applying a special chrome cursor,
				 as it fails to render completely blank cursors. */
				cursorFile = context.getPath() + "blank_google_chrome.cur";
			}
			cursor = "url(\"" + cursorFile + "\")";

			return jq(document.createElement("div"))
				.addClass("retina")
				.attr("id", "retina" + metadata.getId())
				.css({
					"-moz-box-shadow": "0 0 5px #777, 0 0 10px #aaa inset",
					"-webkit-box-shadow": "0 0 5px #777",
					"-moz-border-radius": halfMagnifierSize + "px",
					"-webkit-border-radius": halfMagnifierSize + "px",
					"-webkit-tap-highlight-color": "rgba(0,0,0,0)",
					"background": "url(\"" + imageFileNamePath + "\") no-repeat center center white",
					"border": "2px solid white",
					"position": "absolute",
					"height": magnifierSize + "px",
					"width": magnifierSize + "px",
					"display": "none",
					"box-shadow": "0 0 5px #777, 0 0 10px #aaa inset",
					"border-radius": halfMagnifierSize + "px",
					"cursor": cursor + ", default"
				});
		}

		function calculateRetinaPosition(e) {
			var touch,
				image = getImage(),
				imageDocOffsets = image.offset(),
				offset = {
					left: imageDocOffsets.left,
					top: imageDocOffsets.top
				},
				x,
				y;

			if (e.originalEvent && e.originalEvent.touches) {
				touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
				x = touch.pageX;
				y = touch.pageY;
			} else {
				x = e.pageX;
				y = e.pageY;
			}
			return {
				left: x - offset.left,
				top: y - offset.top
			};
		}

		function moveRetina(left, top) {
			var zoom = getStageZoom(),
				backgroundPositionLeft = left * (magnifierSize - picWidth) * (2 - zoom) / shellWidth,
				backgroundPositionTop = top * (magnifierSize - picHeight) * (2 - zoom) / shellHeight,
				backgroundPosition = backgroundPositionLeft + "px " + backgroundPositionTop + "px";

			getRetinaDisplay().css({
				"left": (left - magnifierSize / 2) * (2 - zoom) + "px",
				"top":  (top - magnifierSize / 2) * (2 - zoom) + "px",
				"background-position": backgroundPosition
			});
		}

		function closeMagnifier() {
			unlockNavigation();
			jq(".retina.animated", document.body).each(function(index, element) {
				fadeOut(jq(element));
			});
		}

		function openMagnifier(e) {
			fadeIn(e);
			lockNavigation();
		}

		function bindEvents() {

            EventUtil.attachSupportedEvents( getImage(), "pointerdown mousedown", function(e) {
                return cancelEvent(e);
            });
            EventUtil.attachSupportedEvents( getImage(), "touchstart pointerenter mouseenter dragstart", openMagnifier );
            EventUtil.attachSupportedEvents( getImage(), "touchend pointerleave pointerout mouseleave mouseout", closeMagnifier );

            EventUtil.attachSupportedEvents( getRetinaDisplay(), "pointerdown mousedown", function(e) {
                lockNavigation();
                return cancelEvent(e);
            });
            EventUtil.attachSupportedEvents( getRetinaDisplay(), "touchend pointerleave pointerout mouseleave mouseout", closeMagnifier );

            EventUtil.attachSupportedEvents( getImage(), "touchcancel", unlockNavigation );
            EventUtil.attachSupportedEvents( getRetinaDisplay(), "touchcancel", unlockNavigation );

            EventUtil.attachSupportedEvents( document.body, "touchmove pointermove mousemove", onMove );

            EventUtil.attachSupportedEvents( document.body, "touchend", closeMagnifier );
		}

        function onMove( e ) {
            var zoom = getStageZoom(),
                pos = calculateRetinaPosition(e),
                retina = getRetinaDisplay(),
                jqImage = getImage();

            if (pos.left < 0 || pos.top < 0 || pos.left > zoom * jqImage.outerWidth() || pos.top > zoom * jqImage.outerHeight()) {
                if (retina.hasClass("animated")) {
                    unlockNavigation();
                    fadeOut(retina);
                }
                return;
            }
            if (!retina.hasClass("animated")) {
                fadeIn(e);
            }
            moveRetina(pos.left, pos.top);
        }

		function lockNavigation() {
			if (isInAuthoringMode || !hasStageTrigger) {
				return;
			}
			context.stage.trigger.lock(metadata.getId());
		}

		function unlockNavigation() {
			if (isInAuthoringMode || !hasStageTrigger) {
				return;
			}
			context.stage.trigger.unlock(metadata.getId());
		}

		function getRetinaDisplay() {
			return jq("#retina" + metadata.getId());
		}

		function getImage() {
			return jq("#magnifierImage" + metadata.getId());
		}

		function fadeOut(jqElement) {
			jqElement
				.removeClass("animated")
				.stop(true, true)
				.fadeOut("fast");
		}

		function fadeIn(e) {
			var pos = calculateRetinaPosition(e);

			moveRetina(pos.left, pos.top);

			getRetinaDisplay()
				.addClass("animated")
				.stop(true, true)
				.fadeIn("fast");
		}

		function cancelEvent(e) {
			if(e && e.preventDefault) {
				e.preventDefault();
			}
			if(e && e.stopPropagation()) {
				e.stopPropagation();
			}
			return false;
		}

		// revealing public API
		return {
			onLoading: function () {
				context.page.event.bindOnExit(this);

				initializeParameters(metadata.parameters);
				setDimensionsOfShellElement();
				createShieldInAuthoringMode();
				createMarkup();

				if (!isInAuthoringMode) {
					bindEvents();
				}
			},

			onExitPage: closeMagnifier
		};
	};
});
