import ngModule from '../../module';
import {buildFilter} from 'vn-loopback/util/filter';
import './style.scss';

export default class Contextmenu {
    constructor($element, $, $transclude) {
        this.$element = $element;
        this.element = $element[0];
        this.$ = $;
        this.$transclude = $transclude;
    }

    get targets() {
        return this._targets;
    }

    set targets(value) {
        this._targets = value;

        if (!value) return;

        for (let selector of value) {
            const target = document.querySelector(selector);
            if (!target) continue;

            target.addEventListener('contextmenu', event => {
                this.target = event.target;

                if (!event.defaultPrevented)
                    event.preventDefault();

                if (!this.isMenuEnabled()) return;

                const parent = this.$.contextmenu;
                parent.style.top = event.pageY + 'px';
                parent.style.left = event.pageX + 'px';

                this.$.menu.show(parent);
            });
        }
    }

    get row() {
        if (!this.target) return null;

        return this.target.closest('[ng-repeat]');
    }

    get rowIndex() {
        if (!this.row) return null;

        const table = this.row.closest('table, vn-table, .vn-table');
        const rows = table.querySelectorAll('[ng-repeat]');

        return Array.from(rows).findIndex(
            rowItem => rowItem == this.row
        );
    }

    get rowData() {
        const model = this.model;
        const rowData = model.data[this.rowIndex];

        return rowData;
    }

    get cell() {
        if (!this.target) return null;

        return this.target.closest('td, vn-td, .vn-td, vn-td-editable');
    }

    get cellIndex() {
        if (!this.row) return null;

        const cells = this.row.querySelectorAll('td, vn-td, .vn-td, vn-td-editable');
        return Array.from(cells).findIndex(
            cellItem => cellItem == this.cell
        );
    }

    get rowHeader() {
        if (!this.row) return null;

        const table = this.row.closest('table, vn-table, .vn-table');
        const headerCells = table && table.querySelectorAll('thead th, vn-thead vn-th');
        const headerCell = headerCells && headerCells[this.cellIndex];

        return headerCell;
    }

    /**
     * Selected model field name
     *
     * @return {string}
     */
    get fieldName() {
        if (!this.rowHeader) return null;

        return this.rowHeader.getAttribute('field');
    }

    /**
     * Selected field value
     *
     * @return {any}
     */
    get fieldValue() {
        return this.rowData[this.fieldName];
    }

    /**
     * Returns true if filter is not disabled
     *
     * @return {Boolean}
     */
    isFilterEnabled() {
        if (!this.rowHeader) return true;
        const isEnabled = this.rowHeader.getAttribute('filter-enabled');

        return isEnabled != 'false';
    }

    isMenuEnabled() {
        if (!this.rowHeader) return true;
        const isEnabled = this.rowHeader.getAttribute('menu-enabled');

        return isEnabled != 'false';
    }

    /**
     * Returns true if filter
     * by selection is enabled and
     * the menu can be interacted
     *
     * @return {Boolean}
     */
    isFilterAllowed() {
        return this.isActionAllowed() && this.isFilterEnabled();
    }

    /**
     * Returns true if the
     * context menu can be interacted
     *
     * @return {Boolean}
     */
    isActionAllowed() {
        if (!this.target) return false;
        const isTableCell = this.target.closest('td, vn-td, .vn-td');

        return isTableCell && this.fieldName;
    }

    /**
     * Filter by current field selection
     */
    filterBySelection() {
        let where = {[this.fieldName]: this.fieldValue};
        if (this.exprBuilder) {
            where = buildFilter(where, (param, value) =>
                this.exprBuilder({param, value})
            );
        }

        this.model.addFilter({where});
    }

    /**
     * Exclude by current field selection
     */
    excludeSelection() {
        let where = {[this.fieldName]: {neq: this.fieldValue}};
        if (this.exprBuilder) {
            where = {[this.fieldName]: this.fieldValue};
            where = buildFilter(where, (param, value) => {
                const expr = this.exprBuilder({param, value});
                const props = Object.keys(expr);
                let newExpr = {};
                for (let prop of props) {
                    if (expr[prop].like) {
                        const operator = expr[prop].like;
                        newExpr[prop] = {nlike: operator};
                    } else if (expr[prop].between) {
                        const operator = expr[prop].between;
                        newExpr = {
                            or: [
                                {[prop]: {lt: operator[0]}},
                                {[prop]: {gt: operator[1]}},
                            ]
                        };
                    } else
                        newExpr[prop] = {neq: this.fieldValue};
                }
                return newExpr;
            });
        }

        this.model.addFilter({where});
    }

    removeFilter() {
        const userFilter = this.model.userFilter;
        const userParams = this.model.userParams;
        const where = userFilter && userFilter.where;

        let filterKey = this.fieldName;
        if (this.exprBuilder) {
            const param = this.exprBuilder({
                param: filterKey,
                value: null
            });
            if (param) [filterKey] = Object.keys(param);
        }

        if (!where) return;

        const whereKeys = Object.keys(where);
        for (let key of whereKeys) {
            removeProp(where, filterKey, key);

            if (!Object.keys(where))
                delete userFilter.where;
        }

        function removeProp(obj, targetProp, prop) {
            if (prop == targetProp)
                delete obj[prop];

            if (prop === 'and' || prop === 'or') {
                const arrayCopy = obj[prop].slice();
                for (let param of arrayCopy) {
                    const [key] = Object.keys(param);
                    const index = obj[prop].findIndex(param => {
                        return Object.keys(param)[0] == key;
                    });
                    if (key == targetProp)
                        obj[prop].splice(index, 1);

                    if (param[key] instanceof Array)
                        removeProp(param, filterKey, key);

                    if (Object.keys(param).length == 0)
                        obj[prop].splice(index, 1);
                }

                if (obj[prop].length == 0)
                    delete obj[prop];
            }
        }

        this.model.applyFilter(userFilter, userParams);
    }

    /**
     * Removes all applied filters
     */
    removeAllFilters() {
        const userParams = this.model.userParams;
        this.model.applyFilter(null, userParams);
    }

    /**
     * Copies the current field
     * value to the clipboard
     */
    copyValue() {
        const cell = angular.element(this.cell);
        if (navigator && navigator.clipboard)
            navigator.clipboard.writeText(cell.text());
    }
}

Contextmenu.$inject = ['$element', '$scope', '$transclude'];

ngModule.vnComponent('vnContextmenu', {
    controller: Contextmenu,
    template: require('./index.html'),
    bindings: {
        targets: '<?',
        model: '<?',
        exprBuilder: '&?'
    },
    transclude: {
        menu: '?slotMenu'
    }
});