let context_menu: JQuery;

$(document).bind("click", function (e) {
    let menu = context_menu || (context_menu = $(".context-menu"));

    if(!menu.is(":visible")) return;

    if ($(e.target).parents(".context-menu").length == 0) {
        despawn_context_menu();
        e.preventDefault();
    }
});

let contextMenuCloseFn = undefined;
function despawn_context_menu() {
    let menu = context_menu || (context_menu = $(".context-menu"));

    if(!menu.is(":visible")) return;
    menu.animate({opacity: 0}, 100, () => menu.css("display", "none"));
    if(contextMenuCloseFn) contextMenuCloseFn();
}

enum MenuEntryType {
    CLOSE,
    ENTRY,
    HR,
    SUB_MENU
}

class MenuEntry {
    static HR() {
        return {
            callback: () => {},
            type: MenuEntryType.HR,
            name: "",
            icon: ""
        };
    };

    static CLOSE(callback: () => void) {
        return {
            callback: callback,
            type: MenuEntryType.CLOSE,
            name: "",
            icon: ""
        };
    }
}

interface ContextMenuEntry {
    callback?:   () => void;
    type:       MenuEntryType;
    name:       (() => string) | string;
    icon?:       (() => string) | string | JQuery;
    disabled?:  boolean;
    visible?: boolean;

    invalidPermission?:  boolean;
    sub_menu?: ContextMenuEntry[];
}

function generate_tag(entry: ContextMenuEntry) : JQuery {
    if(entry.type == MenuEntryType.HR) {
        return $.spawn("hr");
    } else if(entry.type == MenuEntryType.ENTRY) {
        console.log(entry.icon);
        let icon = $.isFunction(entry.icon) ? entry.icon() : entry.icon;
        if(typeof(icon) === "string") {
            if(!icon || icon.length == 0) icon = "icon_empty";
            else icon = "icon " + icon;
        }

        let tag = $.spawn("div").addClass("entry");
        tag.append(typeof(icon) === "string" ? $.spawn("div").addClass(icon) : icon);
        tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));

        if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
        else {
            tag.click(function () {
                if($.isFunction(entry.callback)) entry.callback();
                despawn_context_menu();
            });
        }
        return tag;
    } else if(entry.type == MenuEntryType.SUB_MENU) {
        let icon = $.isFunction(entry.icon) ? entry.icon() : entry.icon;
        if(typeof(icon) === "string") {
            if(!icon || icon.length == 0) icon = "icon_empty";
            else icon = "icon " + icon;
        }

        let tag = $.spawn("div").addClass("entry").addClass("sub-container");
        tag.append(typeof(icon) === "string" ? $.spawn("div").addClass(icon) : icon);
        tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));

        tag.append($.spawn("div").addClass("arrow right"));

        if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
        else {
            let menu = $.spawn("div").addClass("sub-menu").addClass("context-menu-container");
            for(const e of entry.sub_menu) {
                if(typeof(entry.visible) === 'boolean' && !entry.visible)
                    continue;
                menu.append(generate_tag(e));
            }
            menu.appendTo(tag);
        }
        return tag;
    }
    return $.spawn("div").text("undefined");
}

function spawn_context_menu(x, y, ...entries: ContextMenuEntry[]) {
    let menu_tag = context_menu || (context_menu = $(".context-menu"));
    menu_tag.finish().empty().css("opacity", "0");

    const menu_container = $.spawn("div").addClass("context-menu-container");
    contextMenuCloseFn = undefined;

    for(const entry of entries){
        if(typeof(entry.visible) === 'boolean' && !entry.visible)
            continue;

        if(entry.type == MenuEntryType.CLOSE) {
            contextMenuCloseFn = entry.callback;
        } else
            menu_container.append(generate_tag(entry));
    }

    menu_tag.append(menu_container);
    menu_tag.animate({opacity: 1}, 100).css("display", "block");

    const width = menu_container.visible_width();
    if(x + width + 5 > window.innerWidth)
        menu_container.addClass("left");

    // In the right position (the mouse)
    menu_tag.css({
        "top": y + "px",
        "left": x + "px"
    });
}