import pipe from '../utils/pipe';
import PubBase from './PubBase';
import PubListen from './PubListen';
import optDrag from '../options/optDrag';
import PubCommon from './PubCommon';
import contains from '../utils/contains';
import getRectPoints from '../utils/getRectPoints';
import prefix from '../global/prefix';
import createEl from '../utils/createEl';
import style from '../utils/style';
import elState from '../utils/elState';
import Gesture from './Gesture';
import elsSort from '../utils/elsSort';
import transformTools from '../utils/transformTools';
import debounce from '../utils/debounce';
import ease from '../utils/ease';
class Drag extends pipe(PubListen, PubCommon)(PubBase) {
    constructor(elem, options = {}, inital = true) {
        super();
        this.options = {};
        this.ready({
            options,
            maps: optDrag,
            host: elem,
            component: false,
            spread: []
        });
        if (!this.targetEl)
            throw new Error('The target node for drag start is missing!');
        this.dragHolder = createEl('div', { class: `${prefix}holder-drag` }, this.options.lang.holderDrag);
        this.dropHolder = createEl('div', { class: `${prefix}holder-drop` }, this.options.lang.holderDrop);
        this.dropArrow = createEl('i', { class: `${prefix}drop-arrow ${this.options.arrow.icon}`, placement: this.options.arrow.placement, point: 'inside' });
        this.orgHolder = null;
        this.drops = [this.dropHolder];
        this.lastDrop = null;
        this.lastPoint = '';
        this.dftParams = { data: this.options.data, from: this.targetEl, holder: this.dropHolder, to: this.lastDrop, mode: this.options.mode, purpose: this.options.purpose, drops: this.drops, point: this.lastPoint };
        this.dropOver = debounce((evt, target) => {
            let drop = this.drops.find((k) => contains(target, k));
            if (drop) {
                if (drop === this.dropHolder) {
                    this.toggleDropHolderAttr('set', this.lastDrop);
                    this.toggleDropAttr();
                }
                else {
                    this.toggleDropHolderAttr('remove');
                    this.toggleDropAttr(drop, 'ing');
                    this.updateHolderSize(this.dropHolder, drop, this.holderAttr === 'width' ? 'height' : this.holderAttr === 'height' ? 'width' : '');
                }
                if (drop === this.dropHolder || drop === this.targetEl)
                    return;
                this.lastDrop = drop;
                this.options.arrow.enable && elState(this.dropArrow).isVirtual && drop.appendChild(this.dropArrow);
                let points = getRectPoints(evt, drop), isBefore = points.find((k) => this.options.point.before.includes(k)), isAfter = points.find((k) => this.options.point.after.includes(k)), params = { ...this.dftParams, orgEvt: evt, to: drop, points };
                if (this.options.purpose === 'sort' || this.options.purpose === 'auto') {
                    if (isBefore) {
                        this.lastPoint = 'before';
                        this.options.holder.to && drop.insertAdjacentElement('beforebegin', this.dropHolder);
                    }
                    else if (isAfter) {
                        this.lastPoint = 'after';
                        this.options.holder.to && drop.insertAdjacentElement('afterend', this.dropHolder);
                    }
                    else {
                        if (this.options.purpose === 'auto') {
                            this.lastPoint = 'inside';
                            elState(this.dropHolder).isCalc && this.dropHolder.remove();
                        }
                    }
                }
                else {
                    this.lastPoint = 'inside';
                }
                drop.setAttribute('point', this.lastPoint);
                this.options.arrow.enable && this.dropArrow.setAttribute('point', this.lastPoint);
                super.listen({ name: 'dropping', params: [{ ...params, point: this.lastPoint }] });
            }
            else {
                this.toggleDropAttr();
                this.toggleDropHolderAttr('remove');
                elState(this.dropHolder).isCalc && this.dropHolder.remove();
            }
        }, this.options.delay);
        this.dropEnd = async (evt, target) => {
            let drop = this.drops.find((k) => contains(target, k)), params = { ...this.dftParams, orgEvt: evt, to: this.lastDrop, point: this.lastPoint };
            if (drop) {
                if (this.options.b4Drop) {
                    let tmp = await this.options.b4Drop.call(this, params);
                    if (tmp === false) {
                        this.goback(this.options.mode, 'fail');
                        return true;
                    }
                }
                this.clearTransform();
                this.toggleDropAttr(drop, 'ed');
                super.listen({ name: 'dropped', params: [params] });
                return false;
            }
            else {
                this.goback(this.options.mode, 'fail');
                return true;
            }
        };
        super.listen({ name: 'ready' });
        inital ? this.init() : null;
    }
    init(cb) {
        super.listen({ name: 'start' });
        this.setAttrs();
        this.holderAttr = ['left', 'right'].includes(this.options.arrow.placement) ? 'height' :
            ['top', 'bottom'].includes(this.options.arrow.placement) ? 'width' : '';
        this.orgVal = transformTools.get(this.targetEl, ['translate'])['translate'];
        this.nowVal = { ...this.orgVal };
        this.orgStyle = this.targetEl.style.cssText;
        this.getGestureIns();
        super.listen({ name: 'init', cb });
    }
    setAttrs() {
        this.targetEl.setAttribute('draggable', '');
    }
    getGestureIns() {
        this.gestureIns = new Gesture(this.targetEl, {
            rotate: false,
            scale: false,
            drift: false,
            wheel: false,
            onTranslate: (data) => {
                this.orgHolder = this.targetEl.cloneNode(true);
                this.orgHolder.classList.add(`${prefix}holder-origin`);
                this.targetEl.setAttribute('drag', this.options.mode);
                document.body.style.cursor = 'grabbing';
                this.getDrops();
                if (this.options.mode === 'cut' && this.options.holder.from) {
                    this.updateHolderSize(this.dragHolder, this.targetEl);
                    this.targetEl.insertAdjacentElement('afterend', this.dragHolder);
                }
                if (this.options.mode === 'copy' && this.options.holder.from) {
                    this.targetEl.insertAdjacentElement('afterend', this.orgHolder);
                }
                super.listen({ name: 'dragStart', params: [{ ...data }] });
            },
            onTranslating: (data) => {
                this.dropOver(data.orgEvt, data.evtTarget);
                transformTools.set({
                    el: this.targetEl,
                    data: {
                        translate: data.translate.value,
                    },
                });
                this.nowVal = data.translate.value;
                super.listen({ name: 'dragMove', params: [{ ...data, }] });
            },
            onTranslated: (data) => {
                this.dropEnd(data.orgEvt, data.evtTarget).then((resp) => {
                    !resp && this.restore();
                });
                super.listen({ name: 'dragEnd', params: [{ ...data }] });
            },
            onCancel: (data) => {
                this.toggleDropAttr();
                this.restore();
                super.listen({ name: 'dragCancel', params: [{ ...data }] });
            }
        });
    }
    updateHolderSize(target, relate, attr) {
        if (!relate)
            return;
        let styleRelate = style(relate), styleTarget = style(target), width = styleRelate.width, height = styleRelate.height;
        if (attr === 'width') {
            target.style.width = width;
            target.style.height = styleTarget.height;
        }
        else if (attr === 'height') {
            target.style.height = height;
            target.style.width = styleTarget.width;
        }
        else {
            target.style.width = width;
            target.style.height = height;
        }
    }
    toggleDropAttr(exclude, type) {
        if (exclude === this.dropHolder)
            return;
        exclude && exclude !== this.dropHolder && exclude.setAttribute('drop', type);
        for (let k of this.drops) {
            if (exclude === k)
                continue;
            k.removeAttribute('drop');
        }
    }
    toggleDropHolderAttr(type = 'set', relate) {
        if (type === 'remove') {
            this.dropHolder.removeAttribute('moving');
        }
        else {
            this.dropHolder.setAttribute('moving', '');
            relate && this.updateHolderSize(this.dropHolder, relate, this.holderAttr);
        }
    }
    clearTransform() {
        this.targetEl.style.cssText = this.targetEl.style.cssText.replace('transform', '');
    }
    getDrops() {
        this.drops = [this.dropHolder, ...elsSort(super.single2Els(this.options.drops, this.options.parent))];
    }
    goback(type, state) {
        if (state === 'succ') {
            if (type === 'copy') {
                this.targetEl.style.transitionDuration = `0ms`;
                transformTools.set({
                    el: this.targetEl,
                    data: {
                        translate: this.orgVal,
                    }
                });
                this.toggleDropAttr();
                this.restore();
            }
        }
        else if (state === 'fail') {
            ease({
                from: this.nowVal,
                to: this.orgVal,
                doing: (data) => {
                    transformTools.set({
                        el: this.targetEl,
                        data: {
                            translate: data.value,
                        }
                    });
                },
                done: () => {
                    this.restore();
                },
                duration: this.options.duration,
            });
            this.toggleDropAttr();
        }
    }
    restore() {
        this.targetEl.removeAttribute('drag');
        this.dropHolder.removeAttribute('moving');
        this.dropHolder.style.cssText = this.dropHolder.style.cssText.replace('width', '').replace('height', '');
        elState(this.dragHolder).isCalc && this.dragHolder.remove();
        elState(this.dropHolder).isCalc && this.dropHolder.remove();
        elState(this.orgHolder).isCalc && this.orgHolder.remove();
        elState(this.dropArrow).isCalc && this.dropArrow.remove();
        this.lastDrop = null;
        this.lastPoint = '';
        document.body.style.cssText = document.body.style.cssText.replace('cursor', '');
    }
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.restore();
        this.toggleDropAttr();
        this.gestureIns.destroy();
        this.destroyed = true;
        super.listen({ name: 'destroy', cb });
        return this;
    }
}
Drag.hostType = 'el';
export default Drag;
