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

	"use strict";

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

			var type = metadata.parameters.get("type", "sender"),
				id = metadata.parameters.get("id", 1),
				globalValueCounter = new GlobalClickProvider( context, {
					"scInstance" : options.scInstance,
					"listenerClassId": options.listenerClassId,
					"variableId" : "senderListenerClick" + id,
					"groupId" : "tts.senderListener",
					"toggle" : metadata.parameters.get("trigger_action", "click") === "clickToggle"
				});

			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 HiddenListener(globalValueCounter, context);
					}

					return new SenderButton(globalValueCounter, context, {
						"cursor": metadata.parameters.get("cursor", "pointer"),
						"activationEvent": metadata.parameters.get("trigger_action", "click")
					});
				}
			};
		},

		GlobalClickProvider = function( context, options ){

			function isTriggered() {
				return (context.stage.variable.get( options.variableId, options.groupId ));
			}

			return {
				reset : function(){
					context.stage.variable.set( options.variableId, 0, options.groupId );
				},
				applyValue : function(){
					var clickState = options.toggle ? !isTriggered(): true;
					context.stage.variable.set( options.variableId, clickState, options.groupId );
					jq("." + options.listenerClassId).trigger("senderListenerSamePageRefresh");
				},
				checkValue : function(){
					setTimeout(function(){
						context.page.event.trigger( options.scInstance, isTriggered() ? "triggered" : "nottriggered");
					}, 10);
				}
			};

		},

		SenderButton = function( globalValueMediator, context, options ){
			var EVENT_THRESHOLD = 100,	// ignore events for x ms.
				lastEventTime;

			var	button = jq(document.createElement("span"))
					.css({
						"width" : "100%",
						"height" : "100%",
						"display" : "block"
					}),
				supportedEvents = getSupportedEvents( context.getPositioningElement() ),
				userEvent = getActivationEvent(),
				rightClickTouchTimoutId;

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


			function getSupportedEvents( hotspotElement ) {
				var supportsPointerEvents = window && window.PointerEvent,
					canFireTouchstart = "ontouchstart" in document,
					canFireTouchcancel = "ontouchcancel" in document;

				return {
					touchStart: context.device.getDownEvents(hotspotElement.nodeName),
					// TTPS-14518 Several SCs do not react on mouse click on a hybrid device under Firefox
					// FF 62.x & 63.x is going crazy if additional pointerevents are registered - wo we use mouseup
					touchEnd: context.device.getUpEvents(hotspotElement.nodeName),
					hover: canFireTouchstart ? "touchstart mouseenter" : (supportsPointerEvents ? "pointerenter" : "mouseenter"),

					touchCancel: canFireTouchcancel ? "touchcancel" : (supportsPointerEvents ? "pointerout" : "mouseout")
				};
			}

			function getActivationEvent(){
				if( options.activationEvent === "onTouch" ){
					return supportedEvents.hover;
				}

				return supportedEvents.touchEnd;
			}

			function onUserTouchForRightClick( event ) {

				var pointerType = getPointerType(event);
				if( rightClickTouchTimoutId || pointerType === "mouse" ){
					return;
				}

				rightClickTouchTimoutId = setTimeout( function(){
					globalValueMediator.applyValue();
					rightClickTouchTimoutId = null;
				}, 650 );
			}

			function getPointerType(e) {
				if (e.type === "touchstart") {
					return "touch";
				}
				if (e.type === "pointerdown" && e.originalEvent && e.originalEvent.pointerType) {
					return e.originalEvent.pointerType;
				}
				return "";
			}

			function cancelUserTouchForRightClick() {
				if( rightClickTouchTimoutId ){
					clearTimeout( rightClickTouchTimoutId );
					rightClickTouchTimoutId = null;
				}
			}

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

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

				lastEventTime = new Date().getTime();

				if( options.activationEvent === "clickRight" && event.which !== 3) {
					return;
				}

				globalValueMediator.applyValue();

				return cancelEvent( event );
			}

			function bindActivationEvents( hotSpotElement ) {

				jq( hotSpotElement ).on( userEvent, onUserAction );

				if( !supportedEvents.touchStart ) {
					return;
				}

				if (options.activationEvent === "click" || options.activationEvent === "clickToggle") {
					jq(hotSpotElement).on(supportedEvents.touchStart, cancelEvent);
					return;
				}

				if (options.activationEvent === "clickRight" && supportedEvents.touchStart) {
					jq(hotSpotElement).on(supportedEvents.touchStart, onUserTouchForRightClick);
					jq(hotSpotElement).on(supportedEvents.touchEnd + " " + supportedEvents.touchCancel, cancelUserTouchForRightClick);
				}
			}

			function removeActivationEvents( hotSpotElement ) {

				jq( hotSpotElement ).off( userEvent, onUserAction );

				if( !supportedEvents.touchStart ) {
					return;
				}

				if (options.activationEvent === "click" || options.activationEvent === "clickToggle") {
					jq(hotSpotElement).off(supportedEvents.touchStart, cancelEvent);
					return;
				}

				if (options.activationEvent === "clickRight" ) {
					jq(hotSpotElement).off(supportedEvents.touchStart, onUserTouchForRightClick);
					jq(hotSpotElement).off(supportedEvents.touchEnd + " " + supportedEvents.touchCancel, cancelUserTouchForRightClick);
				}
			}

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

				if(e && e.stopPropagation) {
					e.stopPropagation();
				}

				return false;
			}

			return {
				getElement : function(){
					return button;
				},
				bindEvents : function(){

					var hotSpotElement = context.getPositioningElement();
					bindActivationEvents( hotSpotElement );

					// HTML5. On HTML4 it has no effect.
					context.page.interaction.provokesFeedback( hotSpotElement, false );
					context.page.interaction.provokesAutosolve( hotSpotElement, false );

					jq( hotSpotElement ).on( "mousedown pointerdown dblclick", cancelEvent );
				},
				removeEvents : function(){

					var hotSpotElement = context.getPositioningElement();

					removeActivationEvents( hotSpotElement );

					jq( hotSpotElement ).off( "mousedown pointerdown dblclick", cancelEvent );
				},
				count : function(){},
				refresh : function(){}
			};
		},

		HiddenListener = function( globalValueMediator, context ){

			var shellElement = jq( context.getElement() ),
				view = jq(document.createElement("span"))
					.css({
						"width" : "100%",
						"height" : "100%",
						"display" : "block",
						"visibility" : "hidden"
					});

			function refresh(){
				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(){},
				count : function(){},
				refresh : function(){}
			};
		};

	return function (context, metadata) {

		var scElementId_prefix = "senderListenerClick_",
			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 SLSenderListenerFactory({
					"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();
			}
		};
	};
});
