function OverlayHandler() {
    function createOverlay( overlayData, $overlayContentElement ) {
        var renderProperties = overlayData.renderProperties;
        if ($overlayContentElement == undefined)
            $overlayContentElement = $("<div id='" + overlayData.id + "'><\/div>");

        createDialog( $overlayContentElement, overlayData );
        var $outerDialogDiv = setupDialogOuterStructure( $overlayContentElement, overlayData );
        setupDialogCloseBehavior( $outerDialogDiv, $overlayContentElement, overlayData );

        display( $overlayContentElement, renderProperties );

        if ( overlayData.staticOverlay )
            $outerDialogDiv.hide();
    }

    function refreshOverlay( overlayData )
    {
        var renderProperties = overlayData.renderProperties;
        var $overlayContentElement = $( "#" + overlayData.id );
        // only for comments in overlays
        var tempCommentNodeCache = {};
        ajaxEngine.addCommentsToCache( $overlayContentElement[0], tempCommentNodeCache );
        ajaxEngine.replaceChangedContent( overlayData, tempCommentNodeCache );
        if ( renderProperties.refreshPosition )
            setPosition( $overlayContentElement, renderProperties )
    }

    function removeOverlay( overlayData )
    {
        if ( $( "#" + overlayData.id ).hasClass( 'ui-dialog-content' ) ) {
            $( "#" + overlayData.id ).dialog( "close" );
        }
    }

    //http://bugs.jqueryui.com/ticket/11776
    function fixFirefoxDraggingObjectsHeightBug( $divWithOverlayContent, renderProperties ) {
        if ( renderProperties.height == "auto" )
            $divWithOverlayContent.dialog( {dragStop: function () {
                $( this ).dialog( {height: 'auto'} );
            }} );
    }

    function createDialog( $overlayContentElement, overlayData ) {
        var renderProperties = overlayData.renderProperties;
        var $jqueryDialogElement = $overlayContentElement.dialog( {
            appendTo: (overlayData.staticOverlay ? $overlayContentElement.parent() : "body"),
            autoOpen: false,
            closeOnEscape: renderProperties.showXButton,
            closeText: "",
            modal: renderProperties.modal,
            open: function(event, ui) {
                if (renderProperties.disableScrolling)
                    WebcoreUtils.disableScrolling();
            },
            close: ( !overlayData.staticOverlay ? function ( event, ui ) {
                if (renderProperties.disableScrolling)
                    WebcoreUtils.enableScrolling();
                $( document ).unbind( "click." + overlayData.id );
                $jqueryDialogElement.remove();
                ajaxEngine.sendServerCommand("overlayRemoved",{ id:overlayData.id });
            } : function() {
                if (renderProperties.disableScrolling)
                    WebcoreUtils.enableScrolling();
                StaticOverlay.hide( overlayData.id );
            } )
        } );

        fixFirefoxDraggingObjectsHeightBug( $overlayContentElement, renderProperties );

        if ( overlayData.content != undefined )
            $jqueryDialogElement.html( overlayData.content );

        prepareTransferEffect( $overlayContentElement, renderProperties );
        $overlayContentElement.dialog( "option", renderProperties );

        return $jqueryDialogElement;
    }


    function setupDialogOuterStructure( $overlayContentElement, overlayData ) {
        var renderProperties = overlayData.renderProperties;

        var $outerDialogDiv = $( $overlayContentElement ).parents( "div[role=dialog]" );
        $outerDialogDiv.attr( "id", overlayData.id + "Container" );
        $outerDialogDiv.addClass( "wc-LayerContainer" );

        var $customTitlebar = $overlayContentElement.find( "div[class*='ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix']" );

        if ( !renderProperties.showTitleBar || $customTitlebar.length == 1 ) {
            $outerDialogDiv.children( "div[class*='ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix']" ).remove();
            if ( $customTitlebar.length == 1 && renderProperties.draggable )
                $outerDialogDiv.draggable( { handle: $customTitlebar } );
        }
        else if ( !renderProperties.showXButton ) {
            $outerDialogDiv.find( "button[class='ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only ui-dialog-titlebar-close']" ).remove();
        }

        // it's necessary if we will show content over the bounds of dialog
        $overlayContentElement.css( "overflow", renderProperties.overflow );
        $outerDialogDiv.css( "overflow", renderProperties.overflow );

        return $outerDialogDiv;
    }

    function setupDialogCloseBehavior( $outerDialogDiv, $overlayContentElement, overlayData ) {
        var renderProperties = overlayData.renderProperties;

        var closeDialogIfJQueryNotActive = function ( $outerDialogDiv, $overlayContentElement, staticOverlay ) {
            if ( jQuery.active == 0 ) {
                if ( !staticOverlay ) {
                    if ( $overlayContentElement.hasClass( 'ui-dialog-content' ) ) {
                        $overlayContentElement.dialog( "close" );
                    }
                }
                else {
                    $outerDialogDiv.parent().hide();
                    $outerDialogDiv.hide();
                }
            }
            else
                setTimeout( function () {
                    closeDialogIfJQueryNotActive( $outerDialogDiv, $overlayContentElement, staticOverlay );
                }, 10 );
        };

        if ( renderProperties.closeOnMouseOut )
            $outerDialogDiv.bind( "mouseleave", function () {
                closeDialogIfJQueryNotActive( $outerDialogDiv, $overlayContentElement, overlayData.staticOverlay );
            } );

        if ( renderProperties.closeOnOutsideClick ) {
            $( document ).bind( "click." + overlayData.id + ".close", function ( event ) {
                if ( $( event.target ).parents( "#" + overlayData.id ).length == 0 ) {
                    closeDialogIfJQueryNotActive( $outerDialogDiv, $overlayContentElement, overlayData.staticOverlay );
                    if ( !overlayData.staticOverlay )
                        $( document ).unbind( "click." + overlayData.id + ".close" );
                }
            } );
        }
    }

    function display( $overlayContentElement, renderProperties ) {
        setPosition( $overlayContentElement, renderProperties );
        $overlayContentElement.dialog( "open" );
        fixPosition($overlayContentElement, renderProperties);

        var focusableElem = $overlayContentElement.find( "input:focusable:first" );
        if ( focusableElem && focusableElem.attr( "id" ) ) {
            webcore.setElementIdToFocusAfterLoad( null );
            focusableElem.focus();
        }

        $( '.ui-effects-wrapper' ).css( "z-index", $overlayContentElement.css( "z-index" ) );
    }

    function fixPosition($overlayContentElement, renderProperties) {
        var $outerDialogDiv = $($overlayContentElement).parents("div[role=dialog]");

        var widthDiff = $( window ).width() - ($outerDialogDiv.offset().left + $outerDialogDiv.width());
        if ( widthDiff < 0 )
          $outerDialogDiv.css({ left: $outerDialogDiv.offset().left + widthDiff });

        if ($outerDialogDiv.offset().top < 0)
            $outerDialogDiv.css({ top: 0 });
        if ($outerDialogDiv.offset().left < 0)
            $outerDialogDiv.css({ left: 0 });

        if (renderProperties.fitPositionHorizontally) {
            if ($outerDialogDiv.offset().left + $outerDialogDiv.width() > $(window).width())
                $outerDialogDiv.css({ left: ($(window).width() - $outerDialogDiv.width()) });
        }
    }

    function setPosition( $overlayContentElement, renderProperties ) {
        var topPos = renderProperties.topPos;
        var leftPos = renderProperties.leftPos;

        var positionIsSet = topPos != null && leftPos != null;
        var position;

        switch ( renderProperties.positionType ) {
            case 'ABSOLUTE':
                if ( positionIsSet )
                    position = { position: {
                        my: "left top",
                        at: "left+" + leftPos + " top+" + topPos,
                        of: $( document ),
                        collision: "none"
                    }};
                break;

            case 'VIEWPORT_RELATIVE':
                if ( positionIsSet )
                    position = { position: {
                      my: "left top",
                      at: "left+" + leftPos + " top+" + topPos,
                      of: $( window ),
                      collision: "none"
                    }};
                else
                    position = {
                        position: "center"
                    };
                break;

            case 'WIDGET_RELATIVE':
                if ( renderProperties.anchorId ) {
                    var $positionAnchor = $( renderProperties.anchorId );
                    if ( $positionAnchor.length == 1 ) {
                        leftPos = leftPos ? leftPos : 0;
                        topPos = topPos ? topPos : 0;
                        position = {
                            position: {
                                my: renderProperties.localOrigin,
                                at: renderProperties.originAtAnchorWidget,
                                of: $positionAnchor,
                                collision: "none"
                            }
                        };
                    }
                }
                break;

            case 'APP_CENTERED':
                var $positionAnchor = $( ".wc-Application" );
                if ( $positionAnchor.length == 1 ) {

                    if ( $positionAnchor.height() > $( window ).height() ) {
                        position = { position: {
                            my: "center",
                            at: "center-" + (($( window ).width() - $positionAnchor.width()) / 2 - $positionAnchor.offset().left) + " center",
                            of: $( window ),
                            collision: "flipfit"
                        }};
                    } else {
                        position = { position: {
                            my: "center",
                            at: "center",
                            of: $positionAnchor,
                            collision: "flipfit"
                        }};
                    }
                }
                break;

            case 'FIXED':
                if ( positionIsSet )
                  position = { position: {
                    my: "left top",
                    at: "left+" + leftPos + " top+" + topPos,
                    of: $( window ),
                    collision: "none"
                  }};
                else
                    position = {
                        position: "center"
                    };
                $overlayContentElement.parent().css( "position", "fixed" );
                break;

            case 'MOUSE_RELATIVE':
                leftPos = leftPos ? leftPos : 0;
                topPos = topPos ? topPos : 0;
                position = {
                    position: {
                        my: "left top",
                        at: "left+" + (leftPos + webcore.getMousePosX()) + " top+" + (topPos + webcore.getMousePosY()),
                        of: $( document ),
                        collision: "none"
                    }
                };
                break;
        }

        if ( position ) {
            $overlayContentElement.dialog( "option", position );
        }
    }

    function prepareTransferEffect( dialog, renderProps ) {
        if ( renderProps.show == "transfer" ) {
            renderProps.show = "fade";
            dialog.dialog( "option", "open", function () {
                $( renderProps.anchorId ).effect( "transfer", {
                        to: dialog.parent(),
                        className: "ui-effects-transfer"
                    }, 300
                );
            } );
        }
        if ( renderProps.hide == "transfer" ) {
            renderProps.hide = null;
            dialog.dialog( "option", "beforeClose", function () {
                dialog.parent().effect( "transfer", {
                    to: $( renderProps.anchorId ),
                    className: "ui-effects-transfer"
                }, 300 );
            } )
        }
    }

    return {
        createOverlay: createOverlay,
        setPosition: setPosition,
        fixPosition: fixPosition,
        removeOverlay: removeOverlay,
        refreshOverlay: refreshOverlay
    }
}

var overlayHandler = new OverlayHandler();

ajaxEngine.registerAjaxCallbackFunction( "createOverlay", overlayHandler.createOverlay );
ajaxEngine.registerAjaxCallbackFunction( "removeOverlay", overlayHandler.removeOverlay );
ajaxEngine.registerAjaxCallbackFunction( "refreshOverlay", overlayHandler.refreshOverlay );
