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

    "use strict";

    /**
     * @constructor
     */
    return function (context, metadata) {
        var shellElement,
            hoverElement,
            hoverElementInitialized = false,
            imageElement,
            duration,
            size,
            m_nWidth,
            m_nHeight,
            isInAuthoringMode = false,
            pointerMoveEventNames,
            pointerLeaveEventNames;

        function createHoverpulse() {
            if ( hoverElementInitialized ) {
                return;
            }
            hoverElementInitialized = true;

            var scParams = metadata.parameters,
                imageParams = loadImage( scParams.get( "picture", null ) );

            duration = scParams.asNumber( "speed", "400" );
            size = scParams.asNumber( "size", "40" );
            m_nWidth = scParams.asNumber( "wrapperWidth", "300" );
            m_nHeight = (m_nWidth / imageParams.width) * imageParams.height;

            hoverElement = jq( document.createElement( "div" ) )
                .attr( "id", "thumbnails_" + metadata.getId() )
                .width( m_nWidth + "px" )
                .height( m_nHeight + "px" );

            imageElement = jq( document.createElement( "img" ) )
                .attr( "src", imageParams.fileNameURL )
                .width( m_nWidth + "px" )
                .height( m_nHeight + "px" )
                .addClass( "HoverpulseImage" )

            hoverElement.append( imageElement );
            shellElement.append( hoverElement );

            if ( isInAuthoringMode ) {
                createAuthoringShield( shellElement );
            }
        }

        function loadImage( image ) {

            var result = {};

            if ( image ) {
                result.fileNameURL = context.stage.getSharedPath() + image.fileName;
                result.width = image.width;
                result.height = image.height;
            }
            else {
                result.fileNameURL = context.getPath() + "hoverpulse.jpg";
                result.width = 1024;
                result.height = 683;
            }

            return result;
        }

        function initPointerEventNames() {
            if ( !pointerMoveEventNames ) {
                var nodeName = getHotspotElementNodeName();
                pointerMoveEventNames = context.device.getMoveEvents( nodeName );
                pointerLeaveEventNames = context.device.getReleaseEvents( nodeName );
            }

            // Hotfix: Chrome on iPad iOS does not distinguish pointerMoveEventNames and pointerLeaveEventNames
            if ( ( pointerMoveEventNames == pointerLeaveEventNames ) && ( pointerMoveEventNames == "touchmove" ) ) {
                pointerLeaveEventNames = "pointerout";
            }
        }

        function getHotspotElementNodeName() {
            return hoverElement.get( 0 ).nodeName
                ? hoverElement.get( 0 ).nodeName
                : "document";
        }

        function bindEvents( options ) {
            if ( isInAuthoringMode ) {
                return;
            }

            if ( !hoverElement || !hoverElementInitialized ) {
                return;
            }

            var opts = getOptions( options ),
                jqImage = hoverElement.find( "img" );

            configureImage( jqImage );

            removeEvents();
            context.device.attachFirstSupportedEvent( hoverElement.get( 0 ), pointerMoveEventNames, onMouseOver, { jqImage: jqImage, opts: opts } );
            context.device.attachFirstSupportedEvent( hoverElement.get( 0 ), pointerLeaveEventNames, onMouseLeave, { jqImage: jqImage, opts: opts } );
        }

        function removeEvents() {
            if ( !hoverElement || !hoverElementInitialized ) {
                return;
            }

            initPointerEventNames();
            context.device.removeFirstSupportedEvent( hoverElement.get( 0 ), pointerMoveEventNames, onMouseOver );
            context.device.removeFirstSupportedEvent( hoverElement.get( 0 ), pointerLeaveEventNames, onMouseLeave );
        }

        function onMouseOver( event ) {
            var data = event.data,
                jqElem = jq( data.jqImage ),
                sizeImage = jqElem.data( "hoverEffect.size" ),
                opts = data.opts,
                deltaWidth = opts.size,
                w = sizeImage.w,
                h = sizeImage.h,
                aspectRatio = w / ( h ? h : 1 ),
                deltaHeight = deltaWidth / aspectRatio;

            event.preventDefault();

            jqElem
                .parent()
                .css( {
                    "z-index": opts.zIndexActive
                } );

            jqElem
                .stop()
                .animate( {
                    top: ( ( deltaHeight / -2.0 ) + "px" ),
                    left: ( ( deltaWidth / -2.0 ) + "px" ),
                    height: ( ( h + deltaHeight ) + "px" ),
                    width: ( ( w + deltaWidth ) + "px" )
                }, opts.duration, "swing" );
        }

        function onMouseLeave( event ) {
            var data = event.data,
                jqElem = jq( data.jqImage ),
                size = jqElem.data( "hoverEffect.size" ),
                opts = data.opts,
                w = size.w,
                h = size.h;

            event.preventDefault();

            jqElem
                .stop()
                .animate( {
                    "top": 0,
                    "left": 0,
                    "height": ( h + "px" ),
                    "width": ( w + "px" )
                }, opts.duration, "swing", function () {
                    jqElem
                        .parent()
                        .css( "z-index", opts.zIndexNormal );
                } );
        }

        // create an object shield so we are able to select and drag element
        function createAuthoringShield( shellElement ) {
            shellElement.append( jq( document.createElement( "div" ) )
                .css( {
                    "position": "absolute",
                    "left": 0,
                    "top": 0,
                    "background-color": "transparent",
                    "opacity": "0.4",
                    "width": "100%",
                    "height": "100%",
                    "z-index": "9999"
                } ) );
        }

        function getOptions( options ) {
            var theDefaults = {
                size: 20,
                duration: 200,
                zIndexActive: 100,
                zIndexNormal: 1
            };

            return jq.extend({}, theDefaults, options);
        }

        function configureImage( imgElement ) {
            var jqImage = jq(imgElement);

            // parent must be relatively positioned
            jqImage
                .parent()
                .css( { position: "relative" });

            // pulsing element must be absolutely positioned
            jqImage.css({
                "position": "absolute",
                "top": 0,
                "left": 0
            });

            jqImage.data("hoverEffect.size", {
                w: jqImage.width(),
                h: jqImage.height()
            });
        }

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

                shellElement = jq( context.getElement() );

                isInAuthoringMode = !!context.stage.isInAuthoringMode();

                createHoverpulse();
            },

            onEnterPage: function () {
                bindEvents( {
                    // number of pixels to pulse element (in each direction)
                    size: size,
                    // duration of the animation
                    duration: duration
                } );
            },

            onExitPage: function() {
                removeEvents();
            }
        };
    };
});
