// com.tts.initial.html5.games.pairs2
// HTML4 / HTML5: No differences
define(["jquery","css!./component.css"], function( jq ) {

	"use strict";

	// A pairs game inspired by
	// https://codepen.io/natewiley/pen/HBrbL
	// © 2014 Nate Wiley
	// License -- MIT


	var EventUtil = (function() {

		var
			isIOS = (/iphone|ipad|ipod/i.test( navigator.userAgent.toLowerCase() ) );

		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;
		}

		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;
			},

			/**
			 * 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;
			},

			getPointerDownEvent: function( element ) {
				if( !isIOS ) {
					return getFirstSupportedEvent( element.nodeName, "pointerdown mousedown touchstart" );
				}

				// iOS 9.3 unterstützt zwar click und mouseup, weshalb sich auf diese Events attached wird, aber sie werden
				//  aus unbekannten Gründen nicht gesendet.
				return "touchstart";
			},

			getPointerUpEvent: function( element ) {

				if( !isIOS ) {
					return getFirstSupportedEvent( element.nodeName, "click pointerup mouseup touchend" );
				}

				// iOS 9.3 unterstützt zwar click und mouseup, weshalb sich auf diese Events attached wird, aber sie werden
				//  aus unbekannten Gründen nicht gesendet.
				return "touchend";
			}
		};
	})();

	/**
	 * @constructor
	 */
	return function ( context, metadata ) {

		var m_instance,
			navigationNextAllowed = false,
			shellElement = jq(context.getElement()),
			memoryWrapper,
			playArea,
			playAreaId = "memoryCards_" + metadata.page.getId() + "_" + metadata.getId(),
			resultMessage,
			allCardsA,
			cards,
			missingCards = new Array(),
			restartButton,
			parameters = metadata.parameters,
			guess = null,
			paused = false,
			numberOfTries = 0,
			triesAreLimited = parameters.asBool("limit_tries"),
			maxNumberOfTries = parameters.asNumber("max_number_of_tries", "6"),
			isAutoEvaluate = parameters.asBool("auto_evaluate"),
			isWon = false,
			evaluate = parameters.asBool( "evaluate" ),
			fonts = [
				'Arial, Helvetica, sans-serif',
				'"Times New Roman", Times, serif',
				'Verdana, Geneva, sans-serif',
				'"Courier New", Courier, monospace',
				'Palanquin, Helvetica, sans-serif'
			],
			pointerUpEventName, pointerDownEventName,
			pointerMoveEventName, pointerLeaveEventName;

        function createResultMessage() {
			var resultMsg = parameters.get("success_text", "You rock!"),
                yourResultMsg = parameters.get("your_number_of_tries_text", "Your number of tries:"),
                retryButtonMsg = parameters.get("retry_button_text", "Erneut versuchen?"),
                fontFamily = parameters.asNumber("success_font_family", "1"),
                color = parameters.get("success_color", "#919191"),
                colorLink = parameters.get("success_link_color", "#333333"),
                successBackgroundColor = asRgbaValue("success_background_color", "#ffffff", "success_background_transparency"),
                successBorderColor = asRgbaValue("success_border_color", "#98a7b5", "success_border_transparency"),
                successBorderSize = parameters.get("success_border_size", "1"),
                usePartsOfAvailableSpace = 3,
                numTextRows = 4,
                lineHeightFactor = 1.6,
                successPaddingTop = Math.round(metadata.getHeight() / usePartsOfAvailableSpace),
                successLineHeight = Math.round(metadata.getHeight() / usePartsOfAvailableSpace / numTextRows),
                successFontSize = successLineHeight / lineHeightFactor;


            restartButton =  jq(document.createElement("span"))
                .addClass("restart")
                .text(retryButtonMsg)
                .css({
                    "color": colorLink,
                    "cursor": "pointer"
                });

            resultMessage = jq(document.createElement("div"))
                .css({
                    "font-family": getFont( fontFamily ),
                    "color": color,
                    "border": successBorderSize + "px solid " + successBorderColor,
                    "background-color": successBackgroundColor,
                    "text-align": "center",
                    "padding-top": successPaddingTop,
                    "font-size": successFontSize + "px",
                    "line-height": successLineHeight + "px",
                    "position": "absolute",
                    "left": 0,
                    "top": 0,
                    "width": "100%",
                    "height": "100%",
                    "z-index": "9999"
                })
                .html(
					'<div class="winner" id="resultMessage' + metadata.getId() + '" style="padding: 0; margin: 0; font-weight: normal; ' +
                    'font-size: ' + (successFontSize * 2) + 'px; ' +
                    'line-height: ' + (successLineHeight * 2) + 'px;">' + resultMsg + '</div>' +
                    '<div class="result">' + yourResultMsg + ' ' + numberOfTries + '</div>'
                )
                .hide();

            resultMessage.append( restartButton );
		}

        function createPlayArea() {
            cards = getCardsDOM();

            playArea = jq(document.createElement("div"))
                .addClass("playArea")
                .attr("id", playAreaId);

            playArea.append(cards);
        }

		function createDOM() {
            createPlayArea();
			createResultMessage();

			memoryWrapper = jq(document.createElement("div"))
				.addClass("memoryGame")
				.append(playArea)
				.append(resultMessage);

			shellElement.css({
				width : metadata.getWidth() + "px",
				height : metadata.getHeight() + "px"
			});

			shellElement.append( memoryWrapper );

			if( context.stage.isInAuthoringMode() ) {
				createAuthoringShield();
			}
		}

		function getFont(index) {
			var fontIndex = (index > 0 && index <= fonts.length) ? index - 1 : 0;
			return fonts[fontIndex];
		}

		function asRgbaValue( colorParameterName, defaultColor, transparencyParameterName ) {

			var color = hexToRgb( parameters.get(colorParameterName, defaultColor)),
				alpha = asAlpha( parameters.asNumber(transparencyParameterName, 0) );

			return "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + alpha + ")";
		}

		function hexToRgb( hex ) {

			var parsingResult = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
			if( parsingResult ) {
				return {
					r: parseInt(parsingResult[1], 16),
					g: parseInt(parsingResult[2], 16),
					b: parseInt(parsingResult[3], 16)
				};
			}

			return {
				r: 0,
				g: 0,
				b: 0
			};
		}

		function asAlpha( percentValue ) {
			return (100 - percentValue) / 100;
		}

		function getCardsDOM() {

			var parameters = metadata.parameters,
				borderSize = parameters.get("border_size", "1") + "px",
				borderColor = parameters.get("border_color", "#000000"),
				cardsMargin = parameters.get("margin", "5") + "px",
				rowCount = parameters.asNumber("rows", 2),
				cardWidth = Math.floor(100 / Math.ceil( allCardsA.length / rowCount )) + "%",
				cardHeight = Math.floor(100 / rowCount) + "%",
				backCoverImage = parameters.get("bg_image", null),
				backCoverURI = (backCoverImage) ? context.stage.getSharedPath() + backCoverImage.fileName : context.getPath() + "cover.jpg",
                cardsHTML = '';

			jq(allCardsA).each(function (k, v) {
				cardsHTML +=
					'<div class="card" data-id="' + v.id + '" style="padding: ' + cardsMargin + '; width: ' + cardWidth + '; height: ' + cardHeight + ';">' +
						'<div class="inside">' +
							'<div class="front" style="' +
                                'background-image: url(' + v.img + '); background-size: cover; background-position: center center; ' +
                                'border: ' + borderSize + ' solid ' + borderColor + ';">' +
                                '<div class="highlight">&nbsp;</div>' +
                            '</div>' +
							'<div class="back" style="background-image: url(' + backCoverURI + '); background-size: cover; ' +
                                'background-position: center center; border: ' + borderSize + ' solid ' + borderColor + ';">&nbsp;' +
                            '</div>' +
						'</div>' +
					'</div>';
			});

			return jq( cardsHTML );
		}

		function getAllCardParameters(typeA, typeB) {
			var cardsParameter = [],
				max = 10,
				currentImageA,
				currentImageB;

			for (var i = 1; i <= max; i++) {
				currentImageA = parameters.get("card_" + typeA + i + "_image", null);
				currentImageB = parameters.get("card_" + typeB + i + "_image", null);

				if (currentImageA && !currentImageB) {
					cardsParameter.push(
						{
							name: parameters.get('card_' + typeA + i + '_name', ""),
							img: context.stage.getSharedPath() + currentImageA.fileName,
							id: i
						}
					);
					cardsParameter.push(
						{
							name: parameters.get('card_' + typeA + i + '_name', ""),
							img: context.stage.getSharedPath() + currentImageA.fileName,
							id: i
						}
					);
					continue;
				}

				if (!currentImageA && currentImageB) {
					cardsParameter.push(
						{
							name: parameters.get('card_' + typeB + i + '_name', ""),
							img: context.stage.getSharedPath() + currentImageB.fileName,
							id: i
						}
					);
					cardsParameter.push(
						{
							name: parameters.get('card_' + typeB + i + '_name', ""),
							img: context.stage.getSharedPath() + currentImageB.fileName,
							id: i
						}
					);
					continue;
				}

				if (!currentImageA && !currentImageB) {
					continue;
				}

				cardsParameter.push(
					{
						name: parameters.get('card_' + typeA + i + '_name', ""),
						img: context.stage.getSharedPath() + currentImageA.fileName,
						id: i
					}
				);
				cardsParameter.push(
					{
						name: parameters.get('card_' + typeB + i + '_name', ""),
						img: context.stage.getSharedPath() + currentImageB.fileName,
						id: i
					}
				);
			}

			// get default images if empty
			if (cardsParameter.length === 0) {
				for (var i = 1; i <= 3; i++) {
					cardsParameter.push(
						{
							name: "Default image " + typeA + i,
							img: context.getPath() + "default_" + typeA + i + ".jpg",
							id: i
						}
					);
					cardsParameter.push(
						{
							name: "Default image " + typeB + i,
							img: context.getPath() + "default_" + typeB + i + ".jpg",
							id: i
						}
					);
				}
			}

			return cardsParameter;
		}

		// create an object shield so we are able to select and drag element
		function createAuthoringShield() {

			shellElement.append( jq(document.createElement("div") )
				.css({
					"position": "absolute",
					"left": 0,
					"top": 0,
					"background-color": "#ffffff",
					"opacity": "0.4",
					"width": "100%",
					"height": "100%",
					"z-index": "9999"
				}));
		}

		function shuffle() {
			var cards = document.getElementById( playAreaId );
			for (var i = cards.children.length; i >= 0; i--) {
				cards.appendChild(cards.children[Math.random() * i | 0]);
			}
		}

		function cardClicked() {
			var card = jq(this),
                cardInside = card.find(".inside");

            if (paused || cardInside.hasClass("matched") || cardInside.hasClass("picked")) {
            	return;
            }

			cardInside.addClass("picked");

            //
            card.find(".inside").css({"transition-duration":""});
            card.find(".inside > .front").css({"transition-duration":""});
            card.find(".inside > .back").css({"transition-duration":""});

            if (!guess) {
				guess = card.attr("data-id");
			}
			else if (guess === card.attr("data-id") && !card.hasClass("picked")) {
				numberOfTries++;
				card.parent().find(".picked").addClass("matched");
				guess = null;
				if (triesAreLimited) {
					if (numberOfTries >= maxNumberOfTries) {
						isWon = false;
						win();
					}
				}
			}
			else {
				numberOfTries++;
				guess = null;
				paused = true;

				setTimeout(function () {
					card.parent().find(".picked").removeClass("picked");
					paused = false;
				}, parameters.asNumber("delay_on_wrong_pair", 600));

				if (triesAreLimited && numberOfTries >= maxNumberOfTries) {
					isWon = false;
					win();
				}
			}

			if (card.parent().find(".matched").length === allCardsA.length) {
				isWon = true;
				win();
			}

		}

		function reset() {
			context.stage.variable.set("tts_memoryGameResult" + metadata.getId(), false, "tts.memoryGame");

			playArea.hide();
			numberOfTries = 0;
			guess = null;
			paused = false;
			hideModal();
			cards.find(".inside").removeClass("picked");
			cards.find(".inside").removeClass("matched");
			shuffle();
			playArea.show( "slow" );

			// ToDo: turn feature back on once API method context.stage.navigation.logic.getNextPageAccess() is working
			// This feature has been disabled as long as the API method
			// context.stage.navigation.logic.getNextPageAccess()
			// is not yet working correctly. Next button, Evaluate button and pairs game can interact and block the
			// next button navigation. Without the mentioned method working, user is still able to navigate to next page
			// by using the arrow keys on the keyboard
			/*
			registerNavigationControlElement( m_instance, metadata.parameters.asBool("disableNextButton",false) );
			*/
		}

		function win() {
			$("#resultMessage" + metadata.getId()).text(isWon ? parameters.get("success_text", "You rock!") : parameters.get("lost_text", "Unfortunately not successful!"));

			context.stage.variable.set("tts_memoryGameResult" + metadata.getId(), numberOfTries, "tts.memoryGame");

			var yourResultMsg = parameters.get("your_number_of_tries_text", "Your number of tries:");

			resultMessage.find(".result").first().text(yourResultMsg + ' ' + numberOfTries);

			paused = true;
			setTimeout(function () {
				showModal();
				if (triesAreLimited && isAutoEvaluate) {
					setTimeout(function () {
						context.page.quiz.evaluate();
					}, 10);
				}
			}, 300);


			// ToDo: turn feature back on once API method context.stage.navigation.logic.getNextPageAccess() is working
			// This feature has been disabled as long as the API method
			// context.stage.navigation.logic.getNextPageAccess()
			// is not yet working correctly. Next button, Evaluate button and pairs game can interact and block the
			// next button navigation. Without the mentioned method working, user is still able to navigate to next page
			// by using the arrow keys on the keyboard
			/*
			setAllowNavigationNext( m_instance, metadata.parameters.asBool("disableNextButton",false) );
			*/
		}

		function showModal() {
			resultMessage.fadeIn( "slow" );
			if ( evaluate ) {
				context.page.quiz.setSelected( m_instance, isWon );
			}
		}

		function hideModal() {
			resultMessage.hide();
		}

		function initPointerEventNames() {
			if (!pointerUpEventName) {
				var nodeName = getHotspotElementNodeName();

				pointerUpEventName = context.device.getUpEvents(nodeName);
				pointerDownEventName = context.device.getDownEvents(nodeName);
				pointerMoveEventName = context.device.getMoveEvents(nodeName);
				pointerLeaveEventName = context.device.getReleaseEvents(nodeName);
			}
		}

		function getHotspotElementNodeName() {
			return "document";
		}

		function bindEvents() {
			if( context.stage.isInAuthoringMode() ){
				return;
			}

			cards.on(pointerUpEventName, cardClicked );

			restartButton.on(pointerUpEventName, reset);
			context.device.attachFirstSupportedEvent(restartButton.get(), pointerMoveEventName, buttonHighlightOn);
			context.device.attachFirstSupportedEvent(restartButton.get(), pointerLeaveEventName, buttonHighlightOff);
		}

		function buttonHighlightOn(){
			var colorLinkHover = parameters.get("success_link_color_touch", "#000000");

			restartButton.css({
				"color" : colorLinkHover
			});
		}

		function buttonHighlightOff(){
			var colorLink = parameters.get("success_link_color", "#333333");

			restartButton.css({
				"color" : colorLink
			});
		}

		// ToDo: turn feature back on once API method context.stage.navigation.logic.getNextPageAccess() is working
		// This feature has been disabled as long as the API method
		// context.stage.navigation.logic.getNextPageAccess()
		// is not yet working correctly. Next button, Evaluate button and pairs game can interact and block the
		// next button navigation. Without the mentioned method working, user is still able to navigate to next page
		// by using the arrow keys on the keyboard
		/*
		function setAllowNavigationNext( controlInstance, navigationControlActive ){
			var buttonNextControlGroupId = "tts_button_next_control_" + metadata.page.getId();
			if( navigationControlActive ) {
				context.page.group.event.fire(controlInstance, buttonNextControlGroupId, "onActivateButtonNext", controlInstance);
			}
		}

		function registerNavigationControlElement( controlInstance, navigationControlActive ){
			var buttonNextControlGroupId = "tts_button_next_control_" + metadata.page.getId();
			if( navigationControlActive ) {
				context.page.group.event.fire(controlInstance, buttonNextControlGroupId, "onDeactivateButtonNext", controlInstance);
			}
		}
		*/

		return {
			onLoading : function () {
				m_instance = this;

				context.page.event.bindOnEnter(m_instance);

				allCardsA = getAllCardParameters("a", "b");
				createDOM();
				initPointerEventNames();
				bindEvents();
				shuffle();

				if( context.stage.isInAuthoringMode() ) {
					context.stage.variable.set("tts_memoryGameResult" + metadata.getId(), false, "tts.memoryGame");
					return;
				}

                // check if this pairs game has already been done
                var formerSuccessResult = context.stage.variable.get("tts_memoryGameResult" + metadata.getId(), "tts.memoryGame");

                if (formerSuccessResult) {
                    numberOfTries = formerSuccessResult;
                    win();
				}

				if ( evaluate ) {
					context.page.quiz.add( m_instance, "tts.memoryGame", true );
					context.page.quiz.setSelected( m_instance, false );
                }
			},

			onEnterPage : function () {
				// ToDo: turn feature back on once API method context.stage.navigation.logic.getNextPageAccess() is working
				// This feature has been disabled as long as the API method
				// context.stage.navigation.logic.getNextPageAccess()
				// is not yet working correctly. Next button, Evaluate button and pairs game can interact and block the
				// next button navigation. Without the mentioned method working, user is still able to navigate to next page
				// by using the arrow keys on the keyboard
				/*
				// check if this pairs game has already been done
				var formerSuccessResult = context.stage.variable.get("tts_memoryGameResult" + metadata.getId(), "tts.memoryGame");

				if( !formerSuccessResult ){
					registerNavigationControlElement( m_instance, metadata.parameters.asBool("disableNextButton", false) );
				}
				*/
				missingCards.length = 0;
			},

			allowNavigationNext: function(){
				return navigationNextAllowed;
			}
		};
	};
});
