/**
 * 
 * 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) Open-Xchange Inc., 2006-2011
 * Mail: info@open-xchange.com 
 * 
 * @author Matthias Biggeleben <matthias.biggeleben@open-xchange.com>
 * 
 */

/*jslint bitwise: false, nomen: false, onevar: false, plusplus: false, regexp: false, white: true, browser: true, devel: true, evil: true, forin: true, undef: true, eqeqeq: true, immed: true */

/*global ox, jQuery, $, equals, register, triggerEvent, menuswitchMailView */

ox.gui.initToolBar = function () {

    /*
     * namespaces for toolbars (for easy global access)
     */
    ox.widgets.toolBar = {
        viewControl: undefined, // view controller that switched between all toolbar instances, e.g. mail, calendar, contacts.
        views: {}, // all toolbar instances
        meta: {}, // all meta data
        tabs: {}, // all tab instances, e.g. "mail-edit"
        sections: {}, // all section instances
        menuItems: {} // all menu item instances
    };
    
    /**
     * Short description of meta objects
     * title {String} The title/name/label of a menu item
     * icons {Array} Icons of that menu item. The first one is the enabled icon, the second is the disabled one.
     * big {Boolean} True if the icon should be a large one
     * requires {Array|function} Either an array of requirements (see ability object; below) or a function.
     * 
     * Example for requires: 
     *     requires: ["some", "modify"]
     *     This says that the menu items need some selected elements (> 0) and write privileges.
     *     requires: ["one"]
     *     A menu item is only enabled if one element is selected (=== 0)
     *     
     * Using a function: [function (ability, selection)]
     *     When using a function, it is called with the ability object as first
     *     and the current selection as second parameter.
     *     Must return true to enable the menu item.
     *     
     */
    
    /**
     * Ability object
     * @example
     * ability = {
     *   "read": true,
     *   "modify": false,
     *   "delete": false,
     *   "some": true,
     *   "one": false,
     *   "none": false,
     *   "multiple": true
     * };
     */
    
    /*
     * Common meta data
     */
    var meta = ox.widgets.toolBar.meta = {
        /*
         * Create
         */
        create: {
            "mail": {
                title: _("E-Mail"),
                icons: ["mod_mail.png"],
                big: true,
                requires: ["mail"],
                mousedown: function(options) {
                    if (ox.api.ui.getModule("mail").disabled) {
                        triggerEvent("Feature_Not_Available", "modules/mail");
                    }
                }
            },
            "calendar": {
                title: _("Appointment"),
                icons: ["mod_calendar.png"],
                big: true,
                requires: ["calendar"],
                mousedown: function(options) {
                    if (ox.api.ui.getModule("calendar").disabled) {
                        triggerEvent("Feature_Not_Available", "modules/calendar");
                    }
                }
            },
            "contact": {
                title: _("Contact"),
                icons: ["mod_contacts.png"],
                big: true,
                requires: ["contacts"],
                mousedown: function(options) {
                    if (ox.api.ui.getModule("contacts").disabled) {
                        triggerEvent("Feature_Not_Available", "modules/contacts");
                    }
                }
            },
            "distributionlist": {
                title: _("Distribution list"),
                icons: ["distributionlist.png"],
                big: true,
                requires: ["contacts"],
                mousedown: function(options) {
                    if (ox.api.ui.getModule("contacts").disabled) {
                        triggerEvent("Feature_Not_Available", "modules/contacts");
                    }
                }
            },
            "task": {
                title: _("Task"),
                icons: ["mod_tasks.png"],
                big: true,
                requires: ["tasks"],
                mousedown: function(options) {
                    if (ox.api.ui.getModule("tasks").disabled) {
                        triggerEvent("Feature_Not_Available", "modules/tasks");
                    }
                }
            },
            "file": {
                title: _("Infoitem"),
                icons: ["mod_infostore.png"],
                big: true,
                requires: ["infostore"],
                mousedown: function(options) {
                    if (ox.api.ui.getModule("infostore").disabled) {
                        triggerEvent("Feature_Not_Available", "modules/infostore");
                    }
                }
            }
        },
        /*
         * View
         */
        view: {
            "vsplit": {
                title: _("V-Split"),
                icons: ["v_split_view.png"],
                big: true,
                behavior: "radio",
                group: "split-views",
                checked: function () {
                    return currentpath[1] === "vsplit";
                }
            },
            "hsplit": {
                title: _("H-Split"),
                icons: ["h_split_view.png"],
                big: true,
                behavior: "radio",
                group: "split-views",
                checked: function () {
                    return currentpath[1] === "hsplit";
                }
            },
            "list": {
                title: _("List"),
                icons: ["list_view.png"],
                big: true,
                behavior: "radio",
                group: "split-views",
                checked: function () {
                    return currentpath[1] === "list";
                }
            }
        }
    };
    
    /*
     * Flags
     */
    meta.flags = {};
    meta.flags["0"] = {
        title: _("Remove flag"),
        icons: ["tag_remove.png"],
        big: true,
        requires: ["some", "modify"]
    };
    for (var i = 1; i <= 10; i++) {
        meta.flags["" + i] = {
            number: i,
            //#. %d is the flag number
            title: format(_("Flag #%d"), i),
            icons: ["tag_" + i + ".png"],
            big: true,
            requires: ["some", "modify"],
            behaviour: "radio",
            group: "flags-radio",
            checked : function (ability,selection) {
                // show radio state just for one seleted item
                if (currentpath[0] === "mail" && ability.one) {
                    return selection[0].color_label == this.number;
                } else false;
            }
        };
    }
    
    /*
     * Helper: Mass adds menu items based on their meta data
     */
    
    // full list of toolbar menu items
    var items = ox.widgets.toolBar.menuItems = {};
    
    /**
     * Mass add menu items
     * @param {Object} The target widget
     * @param {Object} Meta data (usually some subset)
     * @param {String} A prefix that is added to all IDs
     * @param {Array} An array of all IDs that should be added
     * @example:
     *     add(section, meta.flags, "mail-flag",
     *         ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
     *     );
     *     This one adds menu items defined in meta.flags to a section (widget).
     *     All added items get the prefix "mail-flag-". The IDs to add are defined by the forth parameter.
     */
    var add = function (target, namespace, prefix, ids) {
        // prefix
        prefix = prefix !== "" ? prefix + "-" : "";
        // default action
        var fnAction = function (e) {
            // call controller
            return ox.ToolBarController.trigger(e.widget.id);
        };
        // loop
        var i = 0, $l = ids.length, id, options;
        var j, icons, $k,icon;
        for (; i < $l; i++) {
            id = ids[i];
            if ((options = namespace[id])) {
                // set id
                options.id = prefix + id;
                // selection name
                if (options.selection === undefined) {
                    options.selection = "default";
                }
                // set default action (unless has own)
                if (options.action === undefined) {
                    options.action = fnAction;
                }
                // fix icon paths
                if (options.icons !== undefined) {
                    for (j = 0, icons = options.icons, $k = icons.length; j < $k; j++) {
                        // contains no dash?
                        if (!/[\/]/.test(icons[j])) {
                            icons[j] = "icons/" + (options.big === true ? "24/" : "16/") + icons[j];
                        }
                    }
                }
                // add as menu item
                target.add((items[options.id] = new ox.gui.MenuItem(options)));
            } else {
                console.error("Cannot find meta data for", id, namespace);
            }
        }
    };
    
    /**
     * ToolBarController
     */
    ox.ToolBarController = (function () {
        
        // get menu items
        var getItems = function (namespace, selection, keysOnly) {
            // grep
            var id, tmp = [], m, regex = new RegExp("^" + namespace + "-"), item;
            for (id in items) {
                m = id.match(regex);
                if (m && m.length) {
                    item = items[id];
                    if (selection === undefined || (selection === item.options.selection)) {
                        tmp.push(keysOnly === true ? id : item);
                    }
                }
            }
            return tmp;
        };
        
        // satisfies requirements
        var satisfies = function (requires, ability, selection, cont) {
            // is individual function?
            if (ox.util.isFunction(requires)) {
                var result = requires(ability, selection, cont);
                if (typeof result != "undefined") cont(result);
            } 
            else if (requires === undefined || requires.length === 0) {
                // no requirements, so... true
                cont(true);
            } else {
                // loop over requirements
                var i = 0, $l = requires.length, result = true;
                for (; i < $l && result; i++) {
                    result = result && (!! ability[requires[i]]); // !! = get boolean value, might be undefined
                }
                cont(result);
            }
        };
        
        // apply
        var applySelection = function (name, ability, selection, folders) {
            
            // get affected menu items (look for current module)
            var list = getItems(ox.UIController.getModule(), name);
            
            // helper
            var isFunction = ox.util.isFunction;
            var asyncEnable = function (item) {
                return function (flag) {
                    item.setEnabled(flag);
                };
            };
            // loop over items and enable or disable them properly
            var i = 0, $l = list.length, item, options;
            for (; i < $l; i++) {
                // get item
                item = list[i];
                options = item.options;
                // proper selection?
                if (options.selection === name || (options.selection === undefined && name === "default")) {
                    // checked?
                    if (options.checked !== undefined) {
                        if (isFunction(options.checked)) {
                            item.setChecked(options.checked(ability, selection));
                        } else {
                            item.setChecked(!!options.checked);
                        }
                    } 
                    // visibility
                    if (options.visible !== undefined) {
                        item.setVisible(options.visible(ability, selection));
                    }
                    // requirements satisfied?
                    satisfies(options.requires, ability, selection, asyncEnable(item));
                }
            }
        };
        
        // prepare a modules object for extending abilities later
        var modules = { };
        var tmp = ox.api.ui.listModules();
        for (var i in tmp) {
            modules[tmp[i].name] = !tmp[i].disabled;
        }
        
        // get ability (requires that all affected folders are cached)
        var getAbility = function (selection) {
            // get id of current user
            var myself = ox.api.config.get("identifier");
            // selection length
            var $l = selection.length;
            /**
             * Ability object
             */
            var ability = {
                "read": true,
                "modify": true,
                "delete": true,
                "none": $l === 0,
                "some": $l > 0,
                "one": $l === 1,
                "multiple": $l > 1
            };
            
            $.extend(ability, modules);
            
            // shortcut
            var api = ox.api.folder;
            // helper
            var getRight = function (folder, owner, offset) {
                // get bits
                var bits = api.derive("bits", folder, offset);
                // check
                if (bits === 0) {
                    // no permissions
                    return false;
                } else if (bits === 1) {
                    // only own objects
                    return owner === myself;
                } else {
                    // all objects or admin
                    return true;
                }
            };
            // loop over selection
            var i = 0, item, folder, id;
            for (; i < $l; i++) {
                // get item
                item = selection[i];
                // get folder
                if (item) {
                    id = item.folder || item.folder_id;
                    folder = id === undefined ? undefined : ox.api.folder.get({ folder: id });
                }
                // get owner (i.e. created_by)
                if (item && folder) {
                    // get abilities
                    ability.read = ability.read && getRight(folder, item.created_by, 7); // read
                    ability.modify = ability.modify && getRight(folder, item.created_by, 14); // write
                    ability["delete"] = ability["delete"] && getRight(folder, item.created_by, 21); // delete
                } else {
                    // folder unknown
                    ability.read = false;
                    ability.modify = false;
                    ability["delete"] = false;
                    break;
                }
            }
            return ability;
        };
        
        // default tabs
        var defaultTab = {};
        
        // switch to default tab (on selection change)
        var switchToDefaultTab = function () {
            // loop over tab controls
            var id, d;
            for (id in defaultTab) {
                // get default
                d = defaultTab[id];
                // visible?
                if (d.widget.isVisible()) {
                    d.widget.setPosition(d.position);
                }
            }
        };
        
        // current selection state
        var current = {};
        
        // process selection
        var processSelection = function (name, hasFolders, selection, force) {
            // current view
            var view = ox.UIController.getView();
            // has changed?
            if (force === true || current[view + ":" + name] === undefined || !equals(current[view + ":" + name].selection, selection)) {
                // vars
                var i = 0, $l = selection.length, obj, ids = {};
                // get all affected folders?
                if (hasFolders === true) {
                    for (; i < $l; i++) {
                        obj = selection[i];
                        ids[obj.folder || obj.folder_id] = true;
                    }
                }
                // switch to default tab (if at least one element is selected)
                if ($l > 0) {
                    switchToDefaultTab();
                }
                // remember state
                current[view + ":" + name] = {
                    ids: ox.util.keys(ids),
                    count: $l,
                    selection: selection,
                    hasFolders: hasFolders,
                    name: name
                };
                // continuation
                var cont = function (data) {
                    // turn result into an array
                    var folders = ox.util.values(data);
                    // get ability
                    var ability = getAbility(selection);
                    // apply selection
                    applySelection(name, ability, selection, folders);
                };
                // fetch folders?
                if (hasFolders === true) {
                    ox.api.folder.getMultiple({
                        list: current[view + ":" + name].ids,
                        success: cont
                    });
                } else {
                    cont({});
                }
            }
        };
        
        // toolbar handlers
        var handlers = [];
        
        return {
            
            /**
             * Add handler
             * @param {function (id)} Handler. Just called with the id (string) of the clicked menu item.
             * A handler returns true, if it feels responsible for that item.
             * @returns {Boolean} True/False/Undefined. Just true matters here.
             */
            addHandler: function (fn) {
                if (ox.util.isFunction(fn)) {
                    // enqueue
                    handlers.push(fn);
                } else {
                    console.error("ToolBarController.addHandler() Not a function: ", fn);
                }
            },
            
            /**
             * Trigger the event processing. All registered handlers will be called in order to find one that
             * can handle the id.
             * @param {String} id
             * @returns {Boolean} True/False. Just False matters here, as it says that nothing happed.
             * This is used by nested menu items, so that they know that no action was taken and do need
             * need to close all hovering elements.
             */
            trigger: function (id) {
                
                // make sure it's a string
                id = String(id);
                
                // get current selections
                var name, obj, selections = {}, view = ox.UIController.getView(), len = (view || "").length;
                for (name in current) {
                    if (name.substr(0, len) === view) {
                        obj = current[name];
                        selections[obj.name] = obj.selection;
                    }
                }
                
                // event
                if (ox.widgets.toolBar.menuItems[id] !== undefined) {
                    track({
                        type: "MenuItem ",
                        text: "Click on menu item \"" + ox.widgets.toolBar.menuItems[id].text + "\" (" + id + ")"
                    });
                }
                
                // loop over handlers
                var i = 0, $l = handlers.length, result = false;
                for (; i < $l; i++) {
                    // call handler
                    result = handlers[i](id, selections);
                    if (result === true) {
                        return true;
                    }
                }
                
                // when reaching this point, no action has been taken, so...
                return false;
            },
            
            /**
             * Get items
             * @param {String} namespace Namespace. Works like a prefix that runs through a grep 
             * in order to find all matching menu items
             * @returns {Array} A list of all menu item instanced that meet the namespace prefix
             */
            getItems: function (namespace, selection) {
                return getItems(namespace, selection, false);
            },
            
            /**
             * Get item IDs
             * @param namespace {String} Namespace. See getItems()
             * @returns {Array} A list of all menu item IDs
             */
            getItemIds: function (namespace, selection) {
                return getItems(namespace, selection, true);
            },
            
            /**
             * Get ability based on a given selection
             * @param {Array} selection A selection array
             * @returns {Object} An object describing the abilities, like READ, WRITE, or possible actions respectively
             */
            getAbility: function (selection) {
                return getAbility(selection);
            },
            
            /**
             * Apply current selection to current toolbar
             */
            applyCurrentSelection: function () {
                // loop over current selections
                var name, item;
                for (name in current) {
                    // get item
                    item = current[name];
                    // call internal fn
                    processSelection(item.name, item.hasFolders, item.selection, true);
                }
            },
            
            /**
             * Get current selection
             */
            getCurrentSelection: function (name) {
                if (name === undefined) {
                    return current;
                } else {
                    // get current view
                    var view = ox.UIController.getView();
                    return current[view + ":" + name];
                }
            },
            
            /**
             * Clear current selection
             */
            clearCurrentSelection: function () {
                for (var name in current) {
                    $.extend(current[name], {
                        ids: [],
                        count: 0,
                        selection: []
                    });
                }
            },
            
            /**
             * process selection
             */
            processSelection: function (name, hasFolders, selection, force) {
                // call internal fn
                return processSelection(name, hasFolders, selection, force);
            },
            
            /**
             * Set default tab
             */
            setDefaultTab: function (tabControl, position) {
                if (tabControl !== undefined) {
                    defaultTab[tabControl.id] = {
                        widget: tabControl,
                        position: position
                    };
                }
            },
            
            showDefaultTab: function () {
                switchToDefaultTab();
            },
            
            initialResize: function () {
                var collapsed =  ox.api.window.isNested ? false : ox.api.config.get("gui.menu.collapsed", false);
                // get module
                var ctrl = ox.UIController;
                var module = ctrl.getModule() || ctrl.initialModule;
                // get height
                var height = collapsed || module === "portal" ? "26px" : "90px";
                jQuery("#toolbar_container, #window_toolbar_container").height(height);
                resizeSplit("all_of_menu", height);
            },
            
            
            
            resize: function (e) {
                // get views
                var views = ox.widgets.toolBar.views;
                // state
                var collapsed = e.data === true;
                // resize
                var height = collapsed ? "26px" : "90px";
                $("#toolbar_container, #window_toolbar_container").height(height);
                resizeSplit("all_of_menu", height);
                // get module
                var ctrl = ox.UIController;
                var module = ctrl.getModule() || ctrl.initialModule;
                // store state
                if (module !== "portal") {
                    ox.api.config.set("gui.menu.collapsed", collapsed);
                    // loop over all tab controls
                    for (var id in views) {
                        if (views[id] !== this) {
                            views[id].toggleHover(collapsed, false);
                        }
                    }
                }
            }
        };
    }());
    
    var resize = function (e) {
        ox.ToolBarController.resize(e);
    };
    
    var postInit = function () {
        /*
         * Bind to all tab controls
         */
        var views = ox.widgets.toolBar.views;
        // loop over all tab controls
        for (var id in views) {
            // bind
            views[id].addListener("widget:togglehover", resize);
        }
    };
    
    // return shared vars/functions
    return {
        // resize handler
        resize: resize,
        // mass "add" handler
        add: add,
        // post init handler
        postInit: postInit,
        // default options
        getOptions: function (options) {
            return $.extend({
                // section names
                showSectionNames: false,
                // open in hover (main window: use user config; sub windows: always show toolbar)
                inHover: ox.api.window.isNested ? false : ox.api.config.get("gui.menu.collapsed", false)
            }, options || {});
        }
    };
};
