/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2011 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Matthias Biggeleben <matthias.biggeleben@open-xchange.com>
 * 
 */

/*jslint white: true, browser: true, devel: true, evil: true, forin: true, undef: true, eqeqeq: true, immed: true */

/*global ox, window, jQuery, _, dpgettext, I18nString, changeView, urlify, Join, bindtextdomain */

// namespace
ox.api.help = {};

(function ($, window) {
    
    "use strict";
    
    // add static function
    $.highlight = (function () {
        
        var tmpl = $("<div/>").css({
            position: "absolute",
            top: "0px",
            right: "0px",
            bottom: "0px",
            left: "0px",
            backgroundColor: "black",
            opacity: 0.5,
            zIndex: 65000
        });
        
        var overlays = $();
        
        var init = function () {
            
            overlays.remove();
            
            overlays = tmpl.clone().add(tmpl.clone()).add(tmpl.clone()).add(tmpl.clone()).add(tmpl.clone());
            
            overlays.eq(4).css({
                right: "",
                bottom: "",
                border: "0px none",
                backgroundColor: "transparent",
                opacity: 1.0,
                MozBoxShadow: "0px 0px 40px white",
                webkitBoxShadow: "0px 0px 40px white",
                boxShadow: "0px 0px 40px white"
            });
        };
        
        var update = function (selector, cssCenter, cssOverlay) {
            // get first node
            var node = $(selector).eq(0);
            var offset = node.offset() || {};
            var x = offset.left || 0;
            var y = offset.top || 0;
            var w = node.outerWidth() || 0;
            var h = node.outerHeight() || 0;
            var b = 0;
            
            // left
            overlays.eq(0).css($.extend({ width: x + "px", right: "" }, cssOverlay || {}));
            // top
            overlays.eq(1).css($.extend({ height: y + "px", left: x + "px" }, cssOverlay || {}));
            // right
            overlays.eq(2).css($.extend({ left: (x + w) + "px", top: y + "px", height: h + "px", bottom: "" }, cssOverlay || {}));
            // bottom
            overlays.eq(3).css($.extend({ left: x + "px", top: (y + h) + "px" }, cssOverlay || {}));
            // center
            overlays.eq(4).css($.extend({
                left: (x - b) + "px",
                top: (y - b) + "px",
                width: (w) + "px",
                height: (h) + "px"
            }, cssCenter || {}));
        };
        
        var resize = function (e) {
            update(e.data.selector);
        };
        
        return function (selector, cssCenter, cssOverlay) {
            
            // unbind
            $(window).unbind("resize", resize);
            
            if (selector === undefined) {
                // remove overlays
                overlays.remove();
            } else {
                // init
                init();
                // update
                update(selector, cssCenter, cssOverlay);
                // add to document
                overlays.appendTo(document.body);
                // bind
                $(window).bind("resize", { selector: selector }, resize);
            }
        };
        
    }());
    
    // jquery plugin
    $.fn.popup = (function () {
        
        var inset = "20px", shadow = "0px 0px 25px black, 0px 0px 10px 3px black";
        
        // create underlay
        var underlay = $("<div/>", { id: "ox-popup-underlay" }).css({
            position: "absolute",
            top: "0px",
            left: "0px",
            right: "0px",
            bottom: "0px",
            zIndex: 65001,
            opacity: 0.01,
            backgroundColor: "white",
            display: "none"
        });
        
        // create container
        var container = $("<div/>", { id: "ox-popup-container" }).css({
            position: "absolute",
            top: "0px",
            left: "0px",
            width: "500px",
            height: "200px",
            backgroundColor: "white",
            border: "1px solid #111",
            borderWidth: ox.browser.IE ? "3px" : "1px",
            MozBorderRadius: inset,
            webkitBorderRadius: inset,
            borderRadius: inset,
            MozBoxShadow: shadow,
            webkitBoxShadow: shadow,
            boxShadow: shadow,
            zIndex: 65002,
            display: "none"
        }).append(
            $("<div/>").css({
                position: "absolute",
                top: "15px",
                right: "25px",
                bottom: "15px",
                left: "25px",
                overflow: "auto"
            }).append(
                $("<div/>")
            )
        );
        
        return function (options, fn) {
            
            container.stop().hide();
            
            if (options === "disable") {
                // hide
                underlay.hide();
            } else {
                // get options
                var opt = $.extend({
                    top: "0px",
                    left: "0px",
                    width: "400px",
                    modal: false,
                    backgroundColor: "black",
                    opacity: 0.5
                }, options || {});
                // modal?
                if (opt.modal) {
                    var css = $.extend({
                        backgroundColor: "black",
                        opacity: 0.5
                    }, opt.overlay || {});
                    underlay.css(css);
                } else {
                    underlay.css({
                        backgroundColor: "white",
                        opacity: 0.01
                    });
                }
                // add nodes
                var target = container.find("div").eq(1);
                target.empty().append(this);
                // set css
                container.css({
                    top: opt.top,
                    left: opt.left,
                    width: opt.width
                });
                // show
                underlay.appendTo(document.body).show();
                container.appendTo(document.body).show();
                // auto height?
                var height = opt.height ? opt.height : target.outerHeight(true);
                container.css("height", (height + 40) + "px");
                // center?
                if (opt.center) {
                    var dw = $(document).width(), dh = $(document).height();
                    var cw = container.outerWidth(), ch = container.outerHeight();
                    container.css({
                        top: (dh - ch >> 1) + "px",
                        left: (dw - cw >> 1) + "px"
                    });
                } else {
                    // callback?
                    if (ox.util.isFunction(fn)) {
                        fn(container);
                    }
                }
            }
        };
        
    }());
    
    
    
    /**
     * Tour step class
     */
    ox.api.help.TourStep = function (options) {
        
        var opt = options || {};
        
        this.element = opt.selector;
        this.content = opt.content || $("<span/>").text("");
        this.width = opt.width;
        this.height = opt.height;
        this.align = opt.align || "center";
        this.onShow = opt.onShow || $.noop;
        
        this.draw = function (tour, back, forward, stop) {
            
            // align?
            var opt = {}, self = this, content = this.content;
            
            // content is string?
            if (typeof content === "string" || content instanceof I18nString) {
                content = $("<div/>").html(dpgettext(tour.domain, "", content + "") + "");
            }
            
            // center?
            if (this.align && !this.element) {
                opt.center = true;
            }
            
            // highlight?
            if (this.element) {
                // highlight!
                $.highlight(this.element, tour.options.highlight, tour.options.overlay);
            } else {
                // use overlay
                $.highlight();
                opt.modal = true;
                opt.overlay = tour.options.overlay;
            }
            
            // show popup?
            if (content && content.popup) {
                // add page
                var page = tour.current(), numPages = tour.count();
                var position = numPages <= 1 ? $() : $("<div/>").css({
                    fontSize: "8pt",
                    color: "#888",
                    textAlign: "right",
                    margin: "0px 0px 5px 0px"
                }).text(page + " / " + numPages);
                // controls
                var controls = $("<div/>").css({
                    margin: "20px 0px 10px 0px",
                    textAlign: "right"
                });
                // back?
                if (back) {
                    controls.append(
                        $.button({
                            title: _("Back"),
                            click: back
                        }).css("marginRight", "5px")
                    );
                }
                // forward?
                if (forward) {
                    controls.append(
                        $.button({
                            title: _("Next"),
                            click: forward
                        }).css("marginRight", "5px")
                    );
                }
                // stop?
                if (stop) {
                    controls.append(
                        $.button({
                            title: _("Close"),
                            click: stop
                        }).css("marginRight", "5px")
                    );
                }
                position.add(content.clone(true))
                    .add(controls)
                    .addClass("ox-tour")
                    .popup(opt, function (node) {
                        // alignment
                        if (self.align && self.element) {
                            // get bounds
                            var elem = $(self.element).eq(0);
                            var offset = elem.offset() || {};
                            var ew = elem.outerWidth() || 0;
                            var eh = elem.outerHeight() || 0;
                            var nw = node.outerWidth() || 0;
                            var nh = node.outerHeight() || 0;
                            switch (self.align) {
                            case "top":
                                opt.left = (offset.left);
                                opt.top = (offset.top - nh - 20);
                                break;
                            case "right":
                                opt.left = (offset.left + ew + 20);
                                opt.top = (offset.top);
                                break;
                            case "bottom":
                                opt.left = (offset.left);
                                opt.top = (offset.top + eh + 20);
                                break;
                            case "left":
                                opt.left = (offset.left - nw - 20);
                                opt.top = (offset.top);
                                break;
                            }
                            // cut off
                            opt.top = Math.max(20, Math.min(opt.top, $(document).height() - 20));
                            opt.left = Math.max(20, Math.min(opt.left, $(document).width() - 20));
                            // set
                            node.css({
                                top: opt.top + "px",
                                left: opt.left + "px"
                            });
                        }
                    });
            } else {
                // hide
                $().popup("disable");
            }
        };
    };
    /**
     * Tour class
     */
    var runningTour = null;
    ox.api.help.Tour = function (options) {
        
        this.options = $.extend({
            overlay: {},
            start: $.noop,
            stop: $.noop,
            root: "",
            title: ""
        }, options || {});
        
        // i18n
        this.domain = "";
        
        var steps = [];
        var current = 0;
        var self = this;
        var helper = {};
        
        // keys
        var fnKey = function (e) {
            switch (e.which) {
            case 27: self.stop(); return false; // Escape
            case 39: self.forward(); return false; // Cursor right
            case 37: self.back(); return false; // cursor left
            }
        };
        
        this.current = function () {
            return current + 1;
        };
        
        this.count = function () {
            return steps.length;
        };
        
        this.add = function (step) {
            steps.push(step);
        };
        
        var show = function (index) {
            if (index >= 0 && index < steps.length) {
                // set current
                current = index;
                // get functions
                var back = index > 0 ? self.back : undefined;
                var forward = index < steps.length - 1 ? self.forward : undefined;
                var stop = self.stop;
                // on show
                if (ox.util.isFunction(steps[index].onShow)) {
                    $.proxy(steps[index].onShow, self)();
                }
                // draw
                steps[index].draw(self, back, forward, stop);
            }
        };
        
        this.start = function () {
            // not running?
            if (runningTour !== this) {
                // stop another one?
                if (runningTour) {
                    runningTour.stop();
                }
                // mark as running
                runningTour = this;
                // on start
                $.proxy(self.options.start, this)(helper);
                // bind key press
                $(window).bind("keydown", fnKey);
                // continuation
                var cont = function () {
                    // show first step
                    show(0);
                };
                // set module?
                var module = this.options.module || undefined;
                var view = this.options.view || undefined;
                if (module) {
                    ox.UIController.setModule({
                        module: module,
                        folder: "auto",
                        success: function () {
                            if (view) {
                                changeView(view);
                            }
                            cont();
                        }
                    });
                } else {
                    cont();
                }
            }
        };
        
        this.stop = function () {
            // bind key press
            $(window).unbind("keydown", fnKey);
            // remove highlight
            $.highlight();
            // remove popup
            $().popup("disable");
            // on stop
            $.proxy(self.options.stop, this)(helper);
            // remove helper
            var id;
            for (id in helper) {
                helper[id].remove();
                delete helper[id];
            }
            runningTour = null;
        };
        
        this.back = function () {
            if (current > 0) {
                show(current - 1);
            }
        };
        
        this.forward = function () {
            if (current < steps.length - 1) {
                show(current + 1);
            }
        };
    };
    
    // registry
    
    var tours = {}, tourCount = 0;
    
    ox.api.help.Tour.register = function (options) {
        if (options || options.id) {
            tours[options.id] = options;
        }
    };
    
    ox.api.help.Tour.list = function () {
        return tours;
    };
    
    ox.api.help.Tour.instances = {};
    
    ox.api.help.Tour.load = function (id) {
        // registered?
        if (tours[id] !== undefined) {
            // already loaded?
            var instances = ox.api.help.Tour.instances;
            if (instances[id] !== undefined) {
                // start
                instances[id].start();
            } else {
                // load tour data
                var tour = tours[id];
                var domain = tour.plugin ? tour.plugin + ".tours" : "";
                var src = tour.plugin ? urlify("plugins/" + tour.plugin + "/tours/" + tour.src) : "";
                var dev = !!ox.util.getHash("dev");
                // join
                var join = new Join(function () {
                    if (instances[id] !== undefined) {
                        // set proper i18n domain
                        instances[id].domain = domain;
                        // start
                        instances[id].start();
                    }
                });
                var lock = join.add();
                // load PO file?
                if (tour.plugin) {
                    bindtextdomain(domain, "plugins/" + tour.plugin + "/lang/tours/%s.po", join.add());
                }
                // js or xml?
                if (/\.js$/.test(src)) {
                    // js
                    $.ajax({
                        method: "GET",
                        url: src,
                        dataType: "text",
                        cache: !dev,
                        success: join.add(function (js) {
                            try {
                                // wrap Tour class to catch new instance
                                var Tour = function () {
                                    ox.api.help.Tour.apply(this, arguments);
                                    instances[id] = this;
                                };
                                // translate function
                                var _ = function (id) {
                                    return dpgettext(domain, "", id);
                                };
                                // run script
                                (new Function("$", "_", "Tour", "Step", js))($, _, Tour, ox.api.help.TourStep);
                            } catch (e) {
                                if (dev) {
                                    // warn
                                    ox.UINotifier.warn("Script error in tour '" + src + "': " + e);
                                }
                            }
                        })
                    });
                } else if (/\.xml$/.test(src)) {
                    $.ajax({
                        method: "GET",
                        url: src,
                        dataType: "xml",
                        cache: !dev,
                        success: join.add(function (xml) {
                            // classes
                            var Tour = ox.api.help.Tour, Step = ox.api.help.TourStep;
                            var options = {
                                module: $.trim($("module", xml).text()),
                                view: $.trim($("view", xml).text())
                            };
                            // has options?
                            if ($("tour", xml).has("options")) {
                                try {
                                    var s = "return {" + $("options", xml).text() + "}";
                                    var o = (new Function("$", s)($));
                                    options = $.extend(options, o);
                                } catch (e) {
                                    if (dev) {
                                        ox.UINotifier.warn("JavaScript error in tour constructor. " + e);
                                    }
                                }
                            }
                            // create tour
                            var tour = instances[id] = new Tour(options);
                            // add steps
                            $("step", xml).each(function (i, node) {
                                var step = $(node);
                                tour.add(new Step({
                                    selector: step.attr("selector"),
                                    align: step.attr("align"),
                                    content: $.trim(step.text() + "").replace(/\s+/g, " ")
                                }));
                            });
                        }),
                        error: function (xhr, status, error) {
                            if (dev) {
                                // warn
                                ox.UINotifier.warn("Tour '" + src + "' could not be loaded: " + error);
                            }
                        }
                    });
                }
                // unlock join
                lock();
            }
        }
    };
    
}(jQuery, window));
