define(["jquery"], function (jq) {

	"use strict";

	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";
			}
		};
	})();

	var	CounterSenderListenerFactory = function( options, context, metadata ){

		var type = metadata.parameters.get("type", "sender"),
			id = metadata.parameters.get("id", 1),
			globalValueCounter = new GlobalCountProvider( context, {
				"scInstance" : options.scInstance,
				"variableId" : "senderListenerCounter" + id,
				"groupId" : "tts.senderListener",
				"behaviour" : metadata.parameters.get("behaviour", "once"),
				"onceClickedId": "senderListenerCounterClickedOnce" + metadata.getId(),
				"maximum" : metadata.parameters.asNumber("maximum", 1)
			});

		function getFont(index) {
			var fonts = [
					'Arial, Helvetica, sans-serif',
					'"Times New Roman", Times, serif',
					'Verdana, Geneva, sans-serif',
					'"Courier New", Courier, monospace',
					'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif',
					'"Droid Serif", "Times New Roman", Times, serif'
				],
				fontIndex = (index > 0 && index < fonts.length) ? index - 1 : 0;

			return fonts[fontIndex];
		}

		return {
			create: function () {

				if( context.stage.isInAuthoringMode() ){
					return new AuthoringView( globalValueCounter, context, {
						"bgImage" : "url(\"" + context.getPath() + "invis.png\")",
						"caption" : type === "sender" ? "sender" : "listener"
					});
				}

				if (type === "listener") {
					return new ListenerView( globalValueCounter, context, {
						"textAlign" : metadata.parameters.get("text_orientation", "center"),
						"lineHeight" : metadata.getHeight() + "px",
						"color": metadata.parameters.asCssColor("color", "#000000"),
						"font": {
							"size": metadata.parameters.asNumber("font_size", 14) + "px",
							"family": getFont( metadata.parameters.asNumber("font_family", 0) )
						}
					});
				}

				return new SenderButton( globalValueCounter, context, {
					"cursor" : metadata.parameters.get("cursor", "pointer"),
					"targetListenerElementClass" : options.listenerClassId
				});
			}
		};
	},

	GlobalCountProvider = function( context, options ){

		function countUp(){

			var counter = getCounter();

			counter++;
			context.stage.variable.set( options.variableId, counter, options.groupId);
		}

		function getCounter(){
			var counter = context.stage.variable.get( options.variableId, options.groupId);
			if( counter > 0 ){
				return parseInt(counter);
			}
			return 0;
		}

		return {
			reset : function(){
				context.stage.variable.set(options.onceClickedId, false, options.groupId);
				context.stage.variable.set( options.variableId, 0, options.groupId );
			},
			applyValue : function(){

				var isClickedOnce = context.stage.variable.get(options.onceClickedId, options.groupId);
				if( options.behaviour === "once" && isClickedOnce ) {
					return;
				}

				context.stage.variable.set( options.onceClickedId, true, options.groupId);
				countUp();
			},
			checkValue : function(){

				var currentCounter = getCounter(),
					buttonText = (currentCounter >= options.maximum) ? options.maximum : currentCounter;

				if( currentCounter >= options.maximum ) {
					setTimeout(function () {
						context.page.event.trigger( options.scInstance, "triggered");
					}, 10);
				}

				return buttonText;
			}
		};
	},

	SenderButton = function( globalValueMediator, context, options ){

		var	button = jq(document.createElement("span"))
			.css({
				"width" : "100%",
				"height" : "100%",
				"display" : "block",
				"background": "transparent url('./invis.png') repeat center top"
			}),
            EVENT_THRESHOLD = 100,	// ignore events for x ms.
            lastEventTime;

		jq(context.getPositioningElement())
			.css( "cursor", options.cursor );

        function isValidEvent( ) {
            return typeof lastEventTime === "undefined" || new Date().getTime() - lastEventTime > EVENT_THRESHOLD;
        }

		function onUserAction( e ) {
			if( !isValidEvent() ) {
				return;
			}

            lastEventTime = new Date().getTime();

			globalValueMediator.applyValue(e);
			notifyListenerOnCurrentPage();
		}

		function notifyListenerOnCurrentPage() {
			jq("." + options.targetListenerElementClass).trigger("senderListenerSamePageRefresh");
		}

		return {
			getElement : function(){
				return button;
			},
			bindEvents : function(){
				var buttonElement = context.getPositioningElement();
				jq(buttonElement).on( EventUtil.getPointerUpEvent( buttonElement ), onUserAction );
			},
			removeEvents : function(){
				var buttonElement = context.getPositioningElement();
				jq( buttonElement ).off( EventUtil.getPointerUpEvent( buttonElement ), onUserAction );
			},
			count : function(){},
			refresh : function(){
				notifyListenerOnCurrentPage();
			}
		};
	},

	ListenerView = function( globalValueMediator, context, options ){

		var shellElement = jq(context.getElement()),
			view = jq(document.createElement("span"))
				.css({
					"width" : "100%",
					"height" : "100%",
					"display" : "block",
					"text-align": options.textAlign,
					"line-height" : options.lineHeight,
					"color": options.color,
					"font-size": options.font.size,
					"font-family": options.font.family
				});

		function refresh(){
			view.text( globalValueMediator.checkValue() );
		}

		return {
			getElement : function(){
				return view;
			},
			bindEvents : function(){
				// if sender and listener are on same page, we have to make sure listener
				// will refresh after sender action occured
				shellElement.on("senderListenerSamePageRefresh", refresh);
			},
			removeEvents : function(){
				shellElement.off("senderListenerSamePageRefresh", refresh);
			},
			count : function(){},
			refresh : refresh
		};
	},

	AuthoringView = function( globalValueMediator, context, options ){

		var view = jq(document.createElement("div"))
			.css({
				"width": "100%",
				"height": "100%",
				"display": "block",
				"opacity": "0.15",
				"background-image": options.bgImage,
				"overflow" : "hidden"
			}),
			caption = jq(document.createElement("span"))
				.css({
					"position" : "absolute",
					"display" : "block",
					"top" : "100%",
					"left" : "5px",
					"color" : "#000000",
					"font-size" : "16px",
					"line-height" : "100%"
				})
				.text( options.caption );

		view.append( caption );
		globalValueMediator.reset();

		return {
			getElement : function(){
				return view;
			},
			bindEvents : function(){
			},
			removeEvents : function(){
			},
			count : function(){
			},
			refresh : function(){
			}
		};
	};

	return function (context, metadata) {

        var scElementId_prefix = "senderListenerCounter_",
            scElementId_postfix = "_" + metadata.parameters.get("id", 1) + "_" + metadata.page.getId(),
	        eventsAdded = false,
            component;

        function createHTML( scInstance ) {

	        var objElement = jq( context.getElement() )
				.addClass( scElementId_prefix + metadata.parameters.get("type", "sender") + scElementId_postfix )
				.css({
					"width" : metadata.getWidth(),
					"height" : metadata.getHeight()
				});

	        component = new CounterSenderListenerFactory( {
					"listenerClassId" : scElementId_prefix + "listener" + scElementId_postfix,
                	"scInstance" : scInstance
            	}, context, metadata ).create();
	        objElement.append( component.getElement() );
        }

        function bindEvents(){
            eventsAdded = true;
	        component.bindEvents();
		}

        function removeEvents(){
            eventsAdded = false;
	        component.removeEvents();
		}

        return {
            onLoading: function () {
				context.page.event.bindOnEnter(this);
                context.page.event.bindOnExit(this);
                createHTML( this );
            },

			onEnterPage: function () {
                if( !eventsAdded ) {
                    bindEvents();
                }

				component.refresh();
			},

            onExitPage: function () {
                if( eventsAdded ) {
                    removeEvents();
                }
            },

			onAllComponentsReady: function () {
				component.refresh();
			}
        };
    };
});
