/*!
 * (c) 2010 SysIQ, Inc. All rights reserved.
 *
 * This product is protected by United States laws,
 * international copyright treaties and all other applicable
 * national or international laws.
 *
 * This software may not, in whole or in part, be copied,
 * photocopied, translated, modified, or reduced to any
 * electronic medium or machine readable form
 * without the prior written consent of SysIQ, Inc.
 *
 * Filename: jquery.balloon.js
 * Author:   Alexander Goldobin
 */

(function($) {

var relaxDiv = '<div style="clear:both;height:0;line-height:0;font-size:0;">&nbsp;</div>';    
var balloonContainerId = "balloonContainer";

var prepareId = function(s) {
    return s.replace(/\./g, "__").replace(/\ /g, "__");
};

var defaults = {
    contentUrl: "",
    html: "",
    progressImage: "images/progress.gif",
    contentWrapper: null,
    clazz: "balloon",
    pointerFns: {
        left: null,
        top: null,
        right: null,
        bottom: null
    },
    pointerImages: {
        left: "images/balloon-pointer-l.gif",
        top: "images/balloon-pointer-t.gif",
        right: "images/balloon-pointer-r.gif",
        bottom: "images/balloon-pointer-b.gif"
    },
    useFadeEffect: false
};

var setDocumentScrollTop = function(v, backlash) {
    var documentScrollTop = $(document).scrollTop();
    v = Math.ceil(v);
    backlash = backlash != null ? backlash : 2;

    if (Math.abs(documentScrollTop - v) > 2) {
        $(document).scrollTop(v);
    }
};


var balloonContainer = function() {

    var container = $("#"+balloonContainerId);

    if (container.size() == 0) {
        container = $('<div id="'+balloonContainerId+'" style="height: 0"></div>');
        $("body").prepend(container);
    }

    return container;
};

    
var loadImage = function(url, fn) {
    return $(new Image())
    .load(function() {
        if (fn != null && $.isFunction(fn)) {
            fn();
        }
    })
    .attr("src", url);
};

var balloonMap = {};    

$.fn.extend({
    updateBalloon: function(preventWindowScroll) {
        return this.trigger("updateBalloon", preventWindowScroll);
    },
    showBalloon: function(position, orientation, align) {
        return this.trigger("showBalloon", [position, orientation, align]);
    },
    hideBalloon: function() {
        return this.trigger("hideBalloon");
    },
    balloonHolder: function() {
        var holder = {
            balloon: null
        };

        this.trigger("balloonHolder", holder);
        return holder.balloon;
    }

});

$.balloon = function(id, opts) {

    if (opts == null) {
        return balloonMap[id];
    }

    var settings = $.extend({}, defaults, opts);

//    balloonContainer().find(".balloonPointer_"+prepareId(id)).remove();
//    balloonContainer().find(".balloon_"+prepareId(id)).remove();
    balloonMap[id] = null;
    var balloonHolderClass = settings.clazz+"Holder_"+prepareId(id);
    balloonContainer().find("."+balloonHolderClass).remove();

    var balloon = $("<div>")
        .css({
            "z-index": 500,
            position: "absolute"
        })
        .addClass(settings.clazz)
        .addClass(settings.clazz + "_" + prepareId(id))
        .html(settings.html).append(relaxDiv)
        .attr("balloonId", id)
        .hide();

    var balloonHolder = $("<div>").addClass(balloonHolderClass);
    balloonContainer().append(balloonHolder);
    balloonMap[id] = balloon;
    balloonHolder.append(balloon);
    //balloonContainer().append(balloon);



    $.each(settings.pointerImages, function(side, url) {
        if (settings.pointerFns[side] == null) {
            settings.pointerFns[side] = function(fn) {
                return loadImage(url, fn);
            };
        }
    });


    var balloonOrientationClasses = {
        left: settings.clazz + "Left",
        top: settings.clazz + "Top",
        right: settings.clazz + "Right",
        bottom: settings.clazz + "Bottom"
    };

    var setBalloonOrientationClass = function(o) {
        $.each(balloonOrientationClasses, function(name, clazz) {
            if (name != o) {
                balloon.removeClass(clazz);
            }
            else {
                balloon.addClass(clazz);
            }
        });
    };

    var loadContent = function(fn) {

        settings.contentHolder = $.trim(settings.contentHolder);

        var url = settings.contentUrl != null ? $.trim(settings.contentUrl) : "";
        if (url.length > 0) {

            balloon.append(relaxDiv).shutter({image: settings.progressImage}).showShutter();
            balloon.updateBalloon();

            $.ajax({
                url: url,
                type: "GET",
	            dataType: "html",
                success: function(html) {
                    balloon.html(html);
                    if (fn != null && $.isFunction(fn)) {
                        fn();
                    }
                },
                error: function() {
                    balloon.html("Content coud not be loaded");
                    if (fn != null && $.isFunction(fn)) {
                        fn();
                    }
                }
            });
        }
        else {
            if (fn != null && $.isFunction(fn)) {
                fn();
            }
        }
    };


    var pointers = {};
    var pos = {left: 0, top: 0};
    var ori = "right";
    var ali = "center";

    var loadPointer = function(side, fn) {
        if (!pointers[side]) {
            var sideFn = settings.pointerFns[side];
            pointers[side] = sideFn(fn);
            //balloonContainer().append(pointers[side]);
            balloonHolder.append(pointers[side]);

            pointers[side]
                .css({
                    position: "absolute",
                    "z-index": 500
                })
                .addClass(settings.clazz + "Pointer")
                .addClass(settings.clazz + "Pointer_"+side)
                .addClass(settings.clazz + "Pointer_"+prepareId(id));
        }

        return pointers[side];
    };

    var isBalloonShowed = false;

    var showPointer = function(side, fn) {
        $.each(pointers,  function(name, pointer) {
            if (name != side) {
                pointer.hide();
            }
        });

        var pointer = loadPointer(side, fn);

        if (isBalloonShowed) {
            pointer.show();
        }
        return pointer;
    };

    var hideAllPointers = function() {
        $.each(pointers,  function(name, image) {
            image.hide();
        });
    };

    //var self = this;

    var update = function(preventWindowScroll) {

        balloon.updateShutter();
        setBalloonOrientationClass(ori);

        var pointer;
        var pointerPos;
        var balloonPos;
        if (ori == 'left' || ori == 'right') {
            var hParam = ori == 'right' ? 'left' : 'right';
            pointer = showPointer(hParam, function() {
                setTimeout(function() {
                    balloon.updateBalloon(false);
                }, 200);
            });

            var pointerHeight = pointer.outerHeight();

            if ($.browser["msie"] && $.browser.version == 6.0) {
                balloon.css("height", pointerHeight + "px");
            }

            balloon.css("min-height", pointerHeight + "px");


            pointerPos = {
                left: ori == 'right' ? pos.left : (pos.left - pointer.outerWidth()),
                top: pos.top - pointer.outerHeight()/2
            };

            pointer.offset(Vec2.ceil(pointerPos));

            balloonPos = {
                left: ori == 'right' ? (pos.left + pointer.outerWidth()) : (pos.left - pointer.outerWidth() - balloon.width())
            };

            if (ali == "top") {
                balloonPos.top = pos.top - pointer.outerHeight()/2;
            }
            else if (ali == "bottom") {
                balloonPos.top = pos.top - (balloon.outerHeight() - pointer.outerHeight()/2);
            }
            else {
                balloonPos.top = pos.top - balloon.outerHeight()/2;
            }

            // Correct positions

            var vpScroll = $.viewportScroll();
            var vpDims = $.viewportDimensions();


            if (balloonPos.top <  vpScroll.top) {
                balloonPos.top = vpScroll.top;
            }
            else if (balloonPos.top + balloon.outerHeight() > vpScroll.top + vpDims.height){
                balloonPos.top = (vpScroll.top + vpDims.height) - balloon.outerHeight();
            }

            if (balloonPos.top > pointerPos.top) {
                balloonPos.top = pointerPos.top;
            }
            else if (balloonPos.top + balloon.outerHeight() < pointerPos.top + pointer.outerHeight()) {
                balloonPos.top = pointerPos.top + pointer.outerHeight() - balloon.outerHeight();
            }

            balloon.css(Vec2.positionCss(Vec2.ceil(balloonPos)));

            if (!preventWindowScroll) {

                if (balloonPos.top < vpScroll.top) {
                    setDocumentScrollTop(balloonPos.top);
                }
                else if (balloonPos.top + balloon.outerHeight() > vpScroll.top + vpDims.height) {
                    setDocumentScrollTop(balloonPos.top + balloon.outerHeight() - vpDims.height);
                }
            }
        }
        else if (ori == 'top' || ori == 'bottom') {
            var vParam = ori == 'bottom' ? 'top' : 'bottom';
            pointer = showPointer(vParam, function() {
                setTimeout(function() {
                    balloon.updateBalloon(false);
                }, 200);
            });

            var pointerWidth = pointer.outerWidth();

            if ($.browser["msie"] && $.browser.version == 6.0) {
                balloon.css("width", pointerWidth + "px");
            }

            balloon.css("min-width", pointerWidth + "px");

            pointerPos = {
                left: pos.left - pointer.outerWidth() / 2,
                top: ori == 'bottom' ? pos.top : (pos.top - pointer.outerHeight())
            };

            pointer.offset(Vec2.ceil(pointerPos));

            balloonPos = {
                left: pos.left - balloon.outerWidth() / 2,
                top: ori == 'bottom' ? (pos.top + pointer.outerHeight()) : (pos.top - pointer.outerHeight() - balloon.height())
            };

            balloon.css(Vec2.positionCss(Vec2.ceil(balloonPos)));

        }
    };


    
    balloon.bind("updateBalloon", function(e, preventWindowScroll) {
        update(preventWindowScroll);
        return true;
    });



    var showingFirstTime = true;
    balloon.bind("showBalloon", function(e, position, orientation, align) {
        isBalloonShowed = true;
        if (position != null) {
            pos = position;
        }

        if (orientation != null) {
            ori = orientation;
        }

        if (align != null) {
            ali = align; 
        }

        $.hideAllBalloons(balloon.attr("balloonId"));

        if (settings.useFadeEffect) {
            balloon.fadeIn();
        }
        else {
            balloon.show();
        }

        balloon.updateBalloon();

        if (showingFirstTime) {
            loadContent(function() {
                balloon.updateBalloon();
                balloon.find("img").load(function() {
                    balloon.updateBalloon();
                });
            });

            showingFirstTime = false;
        }

        return true;
    });

    balloon.bind("hideBalloon", function() {

        hideAllPointers();

        if (settings.useFadeEffect) {
            balloon.fadeOut();
        }
        else {
            balloon.hide();
        }
        
        isBalloonShowed = false;

        return true;
    });

    balloon.bind("balloonHolder", function(e, holder) {
        holder.balloon = balloonHolder;
        return false;
    });

    $(window).scroll(function() {
        if (!isBalloonShowed) {
            return true;
        }
        balloon.updateBalloon(true);
    });

    $(window).resize(function() {
        if (!isBalloonShowed) {
            return true;
        }
        balloon.updateBalloon(true);
    });

    return balloon;
};

$.hideAllBalloons = function(exceptBalloonId) {
    $.each(balloonMap, function(id, balloon) {
        if (exceptBalloonId != null && exceptBalloonId == id) {
            return true;
        }
        balloon.hideBalloon();
    });
};

})(jQuery);

