/*
 * BetterDiscord Context Menus
 * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
 * All rights reserved.
 * https://betterdiscord.net
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
*/

import { Utils, ClientLogger as Logger } from 'common';
import { ReactComponents, Reflection, MonkeyPatch } from 'modules';
import { VueInjector, Toasts } from 'ui';
import CMGroup from './components/contextmenu/Group.vue';

export class BdContextMenu {

    /**
     * Show a context menu
     * @param {MouseEvent|Object} e MouseEvent or Object { x: 0, y: 0 }
     * @param {Object[]} groups Groups of items to show in context menu
     */
    static show(e, groups) {
        const x = e.x || e.clientX;
        const y = e.y || e.clientY;
        this.activeMenu.menu = { x, y, groups };
    }

    static get activeMenu() {
        return this._activeMenu || (this._activeMenu = { menu: null });
    }

    static install(Vue) {
        Vue.directive('contextmenu', {
            bind(el, binding) {
                el.addEventListener('contextmenu', event => {
                    Logger.log('BdContextMenu', ['Showing context menu', event, el, binding]);
                    BdContextMenu.show(event, binding.value);
                });
            }
        });
    }

}

export class DiscordContextMenu {

    /**
     * add items to Discord context menu
     * @param {any} id unique id for group
     * @param {any} items items to add
     * @param {Function} [filter] filter function for target filtering
     */
    static add(items, filter) {
        if (!this.patched) this.patch();
        const menu = { items, filter };
        this.menus.push(menu);
        return menu;
    }

    static remove(menu) {
        Utils.removeFromArray(this.menus, menu);
    }

    static get menus() {
        return this._menus || (this._menus = []);
    }

    static async patch() {
        if (this.patched) return;
        this.patched = true;
        const self = this;
        MonkeyPatch('BD:DiscordCMOCM', Reflection.module.byProps('openContextMenu')).instead('openContextMenu', (_, [e, fn], originalFn) => {
            const overrideFn = function () {
                const res = fn.apply(this, arguments);
                if (!res.hasOwnProperty('type')) return res;
                if (!res.type.prototype || !res.type.prototype.render || res.type.prototype.render.__patched) return res;
                MonkeyPatch('BD:DiscordCMRender', res.type.prototype).after('render', (c, a, r) => self.renderCm(c, a, r, res));
                res.type.prototype.render.__patched = true;
                return res;
            }
            return originalFn(e, overrideFn);
        });
    }

    static renderCm(component, args, retVal, res) {
        if (!retVal.props || !res.props) return;
        const { target } = component.props;
        const { top, left } = retVal.props.style;
        if (!target || !top || !left) return;
        if (!retVal.props.children) return;
        if (!(retVal.props.children instanceof Array)) retVal.props.children = [retVal.props.children];

        for (const menu of this.menus.filter(menu => !menu.filter || menu.filter(target))) {
            retVal.props.children.push(VueInjector.createReactElement(CMGroup, {
                target,
                top,
                left,
                closeMenu: () => Reflection.module.byProps('closeContextMenu').closeContextMenu(),
                items: typeof menu.items === 'function' ? menu.items(target) : menu.items
            }));
        }
    }

}
