import extend from '../utils/extend';
import instance from '../utils/instance';
import contains from '../utils/contains';
import getEls from '../utils/getEls';
import getContent from '../utils/getContent';
import Position from './Position';
import isMobi from '../utils/isMobi';
import style from '../utils/style';
import easeHeight from '../utils/easeHeight';
import PubBase from './PubBase';
import pipe from '../utils/pipe';
import PubListen from './PubListen';
import PubUpdate from './PubUpdate';
import PubCommon from './PubCommon';
import Hover from './Hover';
import ax from '../ax';
import classes from '../utils/classes';
import elState from '../utils/elState';
import delay from '../utils/delay';
import optPopup from '../options/optPopup';
import prefix from '../global/prefix';
import Bubble from './Bubble';
class Popup extends pipe(Bubble, PubListen, PubUpdate, PubCommon)(PubBase) {
    constructor(elem, options = {}, inital = ax.config.inital) {
        super();
        this.options = {};
        super.ready({
            options,
            host: elem,
            maps: optPopup,
            component: false,
            spread: ['arrow', 'tools', 'footer']
        });
        /**
        * @name Popup.targetEl
        * @description Target element.
        * @type getEl
        */
        /**
        * @name Popup.rawOpts
        * @description Raw Parameters of the instance from map.
        * @type object
        */
        /**
         * @name Popup.dftOpts
         * @description Default Parameters of the instance from merging.The reset method requires this variable.
         * @type object
         */
        /**
         * @name Popup.options
         * @description Parameters of the instance.
         * @type object
         */
        /**
         * @name Popup.destroyed
         * @description Destruction state.
         * @type boolean
         */
        /**
        * @name Popup.plans
        * @description Plans for listening.
        * @type object
        */
        /**
        * @name Popup.storKeys
        * @description The set of parameter fields to save.
        * @type array
        */
        /**
         * @name Popup.storTmp
         * @description Cache object.The "updateCache" method will update the variable.
         * @type object
         */
        /**
        * @name Popup.storObs
        * @description Cache listener(proxy) that listens for "storTmp" changes and automatically updates the local storage.
        * @type any
        */
        /**
        * @name Popup.timestamp
        * @description Instance creation time.
        * @type number
        */
        /**
        * @function Popup.save
        * @description Storing settings locally.
        * @param {fn_cb_options} [cb] - Callback execution after saving local storage.
        * @returns {any} Return the instance itself
        */
        /**
        * @function Popup.reset
        * @description Re-initialization of old parameters.
        * @param {fn_cb_none} [cb] - Call back after reset settings.
        * @returns {*} Return the instance itself
        */
        this.bubbleType = 'popup';
        let _this = this;
        this.triggerShow = function () {
            let isWing = _this.wings.includes(this) ? true : false, trigger = isWing ? (_this.options.wing.trigger || _this.options.trigger) : _this.options.trigger;
            if (trigger === 'click') {
                if (_this.state === 'hidden') {
                    _this.show();
                    if (!_this.options.multiple) {
                        let items = instance.data.filter((i) => i.type === _this.constructor.name.toLowerCase() && i.ins !== _this && i.ins.state === 'shown');
                        items.forEach((i) => {
                            i.ins.hide();
                        });
                    }
                    else {
                        _this.options.wing.actClass && _this.wings.includes(this) ? this.classList.add(_this.options.wing.actClass) : null;
                    }
                }
                else if (_this.state === 'shown') {
                    !_this.options.keepShow ? _this.hide() : null;
                }
            }
            else if (_this.options.trigger === 'input') {
                if (!_this.targetEl?.value) {
                    !_this.options.keepShow ? _this.hide() : null;
                }
                else {
                    _this.show();
                    _this.options.wing.actClass && _this.wings.includes(this) ? this.classList.add(_this.options.wing.actClass) : null;
                }
            }
        };
        this.toggleShow = (e) => {
            if (!contains(e.target, [this.mainEl, this.targetEl, ...this.wings]) && this.state === 'shown') {
                !this.options.keepShow ? this.hide() : null;
            }
        };
        this.triggerClose = () => {
            !this.options.keepShow ? this.hide() : null;
        };
        super.listen({ name: 'ready' });
        inital ? this.init() : null;
    }
    /**
    * @description Initialization instance.
    * @param {fn_cb_none} [cb] - Callback execution after initialization.
    * @returns {Promise<any>} - Returns a proimise whose argument is the instance itself.
    */
    async init(cb) {
        super.listen({ name: 'start' });
        this.setEmpty();
        /**
        * @name Popup.wings
        * @description Nodes associated with target node that can also open or close popup.
        * @type array
        */
        this.wings = getEls(this.options.wing.selector);
        this.renderBubble('popup');
        this.setAttrs();
        await getContent.call(this, {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.contEl,
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                this.data = data;
                this.renderContent(data);
            }
        });
        this.fixAni(this.options.placement);
        this.createPosition();
        this.handleFooter();
        this.handleTools();
        this.bindTrigger();
        this.contEl.querySelectorAll('[role="closebubble"]').forEach((k) => {
            k.removeEventListener('click', this.triggerClose);
            k.addEventListener('click', this.triggerClose, false);
        });
        super.listen({ name: 'init', cb });
        return this;
    }
    fixAni(placement) {
        this.aniIn = ['max', 'left-max', 'right-max'].includes(placement) && this.options.in === 'slideDown' ? 'fadeIn' : this.options.in;
        this.aniOut = ['max', 'left-max', 'right-max'].includes(placement) && this.options.out === 'slideUp' ? 'fadeOut' : this.options.out;
    }
    handleTools() {
        if (this.options.tools.enable) {
            this.options.tools.children.forEach((k) => {
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => {
                        console.log(300);
                        this.hide();
                    };
                }
                else if (k.name === 'enlarge') {
                    if (this.positionIns.placement === 'max') {
                        classes.replace(k.iconEl, k.swap, k.icon);
                        this.initalPlace = this.rawOpts.placement;
                    }
                    else {
                        this.initalPlace = this.positionIns.placement;
                    }
                    k.wrapEl.onclick = () => {
                        if (this.positionIns.placement === 'max') {
                            classes.replace(k.iconEl, k.icon, k.swap);
                            this.reposition(this.initalPlace);
                        }
                        else {
                            classes.replace(k.iconEl, k.swap, k.icon);
                            this.reposition('max');
                        }
                    };
                }
                else if (k.name === 'widen') {
                    if (this.positionIns.placement === 'top-max' || this.positionIns.placement === 'bottom-max') {
                        classes.replace(k.iconEl, k.swap, k.icon);
                        this.initalPlace = this.positionIns.placement.split('-')[0];
                    }
                    else {
                        this.initalPlace = this.positionIns.placement;
                    }
                    k.wrapEl.onclick = () => {
                        if (!this.positionIns.placement.includes('top') && !this.positionIns.placement.includes('bottom')) {
                            console.warn('Only valid for placement=top-* and placement=bottom-*!');
                            return;
                        }
                        if (this.positionIns.placement.includes('-max')) {
                            classes.replace(k.iconEl, k.icon, k.swap);
                            this.reposition(this.initalPlace);
                        }
                        else {
                            let position = this.positionIns.placement.split('-')[0];
                            classes.replace(k.iconEl, k.swap, k.icon);
                            this.reposition(position + '-max');
                        }
                    };
                }
                else if (k.name === 'heighten') {
                    if (this.positionIns.placement === 'left-max' || this.positionIns.placement === 'right-max') {
                        classes.replace(k.iconEl, k.swap, k.icon);
                        this.initalPlace = this.positionIns.placement.split('-')[0];
                    }
                    else {
                        this.initalPlace = this.positionIns.placement;
                    }
                    k.wrapEl.onclick = () => {
                        if (!this.positionIns.placement.includes('left') && !this.positionIns.placement.includes('right')) {
                            console.warn('Only valid for placement=left-* and placement=right-*!');
                            return;
                        }
                        if (this.positionIns.placement.includes('-max')) {
                            classes.replace(k.iconEl, k.icon, k.swap);
                            this.reposition(this.initalPlace);
                        }
                        else {
                            let position = this.positionIns.placement.split('-')[0];
                            classes.replace(k.iconEl, k.swap, k.icon);
                            this.reposition(position + '-max');
                        }
                    };
                }
                else {
                    k.action && k.action.call(this, k);
                }
            });
        }
    }
    setAttrs() {
        this.options.classes ? classes.add(this.mainEl, this.options.classes) : null;
        this.options.size ? this.mainEl.setAttribute('size', this.options.size) : null;
        this.options.contType ? this.mainEl.setAttribute('conttype', this.options.contType) : null;
        this.options.dedicated ? this.contEl.setAttribute('dedicated', '') : null;
        this.options.height ? this.contEl.style.height = this.options.height : null;
        this.options.zIndex ? this.mainEl.style.zIndex = this.options.zIndex : null;
        this.options.tools.enable ? this.mainEl.setAttribute('toolsplace', this.options.tools.placement) : null;
        this.options.theme ? this.mainEl.setAttribute('theme', this.options.theme) : null;
    }
    createPosition() {
        let tmpArr = this.options.placement.split('-'), placement = this.options.placement;
        if (tmpArr.length === 2 && this.options.adaptive && isMobi) {
            if (!tmpArr.includes('max') && !['top', 'bottom'].includes(tmpArr[0])) {
                placement = 'top-max';
            }
            else if (!tmpArr.includes('max') && ['top', 'bottom'].includes(tmpArr[0])) {
                placement = tmpArr[0] + '-max';
            }
        }
        /**
        * @name Popup.positionIns
        * @description Instance of a popup's position.
        * @type any
        */
        this.positionIns = new Position(this.targetEl, this.mainEl, extend({
            target: {
                placement,
                arrow: this.options.arrow,
                targetObserve: {
                    attrs: [],
                },
                bubbleObserve: {
                    attrs: [],
                },
                delay: 0,
            },
            source: this.options.position
        }));
    }
    bindTrigger() {
        if (this.options.trigger === 'click' || this.options.trigger === 'input') {
            if (!this.canTrigger) {
                this.targetEl.removeEventListener(this.options.trigger, this.triggerShow);
                this.targetEl.addEventListener(this.options.trigger, this.triggerShow, false);
            }
            if (this.wings.length > 0 && !this.canTrigger) {
                this.wings.forEach((item) => {
                    let trigger = this.options.wing.trigger || this.options.trigger;
                    item.removeEventListener(trigger, this.triggerShow);
                    item.addEventListener(trigger, this.triggerShow, false);
                });
            }
        }
        else if (this.options.trigger === 'hover') {
            /**
            * @name Popup.hoverIns
            * @description Instance of a popup's hover.
            * @type any
            */
            this.hoverIns = new Hover(this.targetEl, {
                onEnter: () => {
                    this.show();
                },
                onLeave: () => {
                    !this.options.keepShow ? this.hide() : null;
                },
                hold: this.mainEl,
            });
        }
        else if (this.options.trigger === 'sticky') {
            this.show();
        }
        if (this.options.pageClose && !this.options.multiple) {
            !this.canTrigger ? document.addEventListener('click', this.toggleShow, false) : null;
        }
        this.canTrigger = true;
    }
    unbindTrigger(mode = 'host') {
        this.canTrigger = false;
        if (this.options.trigger === 'click' || this.options.trigger === 'input') {
            this.targetEl.removeEventListener(this.options.trigger, this.triggerShow);
            if (this.wings.length > 0 && mode === 'all') {
                this.wings.forEach((item) => {
                    let trigger = this.options.wing.trigger || this.options.trigger;
                    item.removeEventListener(trigger, this.triggerShow);
                });
            }
        }
        else if (this.options.trigger === 'hover') {
            this.hoverIns.destroy();
        }
        if (this.options.pageClose && !this.options.multiple && mode === 'all') {
            document.removeEventListener('click', this.toggleShow, false);
        }
    }
    /**
     * @description Change popup placement.
     * @param {string} placement - The position to be replaced, with the same value as "options.placement".
     * @param {fn_cb_placement} [cb] - Callback execution after changing popup placement.
     * @returns {any} - Returns the instance itself.
     */
    reposition(placement, cb) {
        if (this.destroyed || !placement) {
            return this;
        }
        this.positionIns.change(placement, (placement) => {
            this.fixAni(placement);
            this.updateCache({ placement });
            super.listen({ name: 'reposition', cb, params: [placement] });
        });
        return this;
    }
    /**
     * @description Show or open popup.
     * @param {fn_cb_none} [cb] - Callback execution after displaying popup.
     * @returns {Promise<any>} - Returns a proimise whose argument is the instance itself.
     */
    async show(cb) {
        if (this.destroyed || this.state !== 'hidden' || this.options.asleep) {
            return this;
        }
        this.state = 'ing';
        this.options.b4Show && await this.options.b4Show.call(this);
        elState(this.mainEl).isVirtual ? document.body.appendChild(this.mainEl) : null;
        this.wrapHeight = parseInt(style(this.wrapEl).height);
        this.duration = this.options.duration || (this.wrapHeight ? Math.floor(this.wrapHeight / 3 + 250) : parseFloat(style(this.mainEl).animationDuration) * 1000);
        this.positionIns.change();
        this.mainEl.setAttribute('visibility', 'visible');
        this.targetEl ? this.targetEl.classList.add(this.options.actClass) : null;
        if (this.aniIn === 'slideDown') {
            easeHeight({ el: this.wrapEl, height: this.wrapHeight, type: 'down', duration: this.duration });
        }
        else {
            this.options.duration ? this.mainEl.style.animationDuration = `${this.options.duration}ms` : null;
            this.aniIn ? this.mainEl.style.animationName = prefix + this.aniIn : null;
        }
        this.hoverIns ? this.hoverIns.isActive = true : null;
        await delay({
            duration: this.duration,
            done: () => {
                this.state = 'shown';
                super.listen({ name: 'show', cb });
            }
        });
        return this;
    }
    /**
     * @description Hide or close popup.
     * @param {fn_cb_none} [cb] - Callback execution after closing popup.
     * @returns {Promise<any>} - Returns a proimise whose argument is the instance itself.
     */
    async hide(cb) {
        console.log(this.destroyed, this.state);
        if (this.destroyed || this.state !== 'shown') {
            return this;
        }
        this.state = 'ing';
        if (!this.options.deadShow) {
            this.options.b4Hide && await this.options.b4Hide.call(this);
            this.targetEl ? this.targetEl.classList.remove(this.options.actClass) : null;
            this.options.wing.actClass && this.wings.map((k) => { k.classList.remove(this.options.wing.actClass); });
            if (this.aniOut === 'slideUp') {
                easeHeight({ el: this.wrapEl, height: this.wrapHeight, type: 'up', duration: this.duration });
            }
            else {
                this.aniOut ? this.mainEl.style.animationName = prefix + this.aniOut : null;
            }
            this.mainEl.querySelectorAll('video,audio').forEach((k) => {
                k.pause();
            });
            this.hoverIns ? this.hoverIns.isActive = false : null;
            await delay({
                duration: this.duration,
                done: () => {
                    this.state = 'hidden';
                    this.mainEl.setAttribute('visibility', 'hidden');
                    this.mainEl.remove();
                    super.listen({ name: 'hide', cb });
                    this.aniOut === 'slideUp' ? this.wrapEl.style.height = 'auto' : null;
                }
            });
        }
        return this;
    }
}
export default Popup;
