import Vue, { CreateElement } from 'vue';
import { Subject, Observable } from 'rxjs';
import { createPopper, Instance } from '@popperjs/core/lib/popper-lite.js';
import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow.js';
import flip from '@popperjs/core/lib/modifiers/flip.js';
import { Placement } from '@popperjs/core/lib/enums';
import { on } from '../dom/dom';
import store from '../../store';
import i18n from '@/locale';
import './app-popover.less';

/**
 * 悬浮窗控制器实例
 *
 * @export
 * @class AppPopover
 */
export class AppPopover {
    /**
     * 实例
     *
     * @private
     * @static
     * @memberof AppPopover
     */
    private static readonly $popover = new AppPopover();

    /**
     * vue实例
     *
     * @private
     * @type {Vue}
     * @memberof AppPopover
     */
    private vueExample!: Vue;

    /**
     * PopperJs实例
     *
     * @private
     * @type {Instance}
     * @memberof AppPopover
     */
    private popperExample?: Instance;

    /**
     * 是否显示悬浮窗
     *
     * @private
     * @type {boolean}
     * @memberof AppPopover
     */
    private showPopper: boolean = false;

    /**
     * 是否在点击空白区域时自动关闭
     *
     * @private
     * @type {boolean}
     * @memberof AppPopover
     */
    private isAutoClose: boolean = true;

    /**
     * 当前显示层级
     *
     * @private
     * @type {number}
     * @memberof AppPopover
     */
    private zIndex: number = 0;

    /**
     * Creates an instance of AppPopover.
     * @memberof AppPopover
     */
    constructor() {
        if (AppPopover.$popover) {
            return AppPopover.$popover;
        }
    }

    /**
     * 初始化vue实例
     *
     * @private
     * @returns {void}
     * @memberof AppPopover
     */
    private initVueExample(): void {
        const self = this;
        const container = document.createElement('div');
        container.className = 'app-popover-wrapper';
        on(container, 'click', () => {
            if (!this.showPopper || !this.isAutoClose) {
                return;
            }
            this.popperDestroy();
        });
        const div = document.createElement('div');
        container.appendChild(div);
        document.body.appendChild(container);
        this.vueExample = new Vue({
            el: div,
            store: store,
            i18n: i18n,
            data: { content: null, width: 300, height: 300 },
            methods: {
                click(e: MouseEvent) {
                    e.stopPropagation();
                }
            },
            render(h: CreateElement) {
                const content: any = this.content;
                container.style.zIndex = (self.zIndex - 1).toString();
                return <div v-show={self.showPopper} style={{ width: this.width + 'px', height: this.height + 'px', 'z-index': self.zIndex }} class="app-popover app-popper" on-click={this.click}>{(self.showPopper && content) ? content(h) : null}</div>;
            }
        });
    }

    /**
     * 打开悬浮窗
     *
     * @param {MouseEvent} event 事件
     * @param {*} view 视图
     * @param {*} [context={}] 应用上下文参数
     * @param {*} data 行为参数
     * @param {Placement} position 显示位置
     * @param {boolean} isAutoClose 是否可自动关闭
     * @returns {Observable<any>}
     * @memberof AppPopover
     */
    public openPop(event: MouseEvent, view: any, context: any = {}, data: any, position?: Placement, isAutoClose?: boolean): Observable<any> {
        const subject = new Subject<any>();
        this.openPopover(event, (h: CreateElement) => {
            return h(view.viewname, {
                props: {
                    viewdata: JSON.stringify(context),
                    viewDefaultUsage: false,
                    viewUsage: 4,
                    viewparam: JSON.stringify(data)
                },
                on: {
                    close: (result: any) => {
                        subject.next({ ret: 'OK', datas: result });
                        subject.complete();
                        subject.unsubscribe();
                    }
                }
            })
        }, position, isAutoClose, view.width, view.height);
        return subject.asObservable();
    }

    /**
     * 打开悬浮窗
     *
     * @param {*} event
     * @param {(h: CreateElement) => any} [content]
     * @param {Placement} [position='left-end']
     * @param {boolean} [isAutoClose=true]
     * @param {number} [width=300]
     * @param {number} [height=300]
     * @param {*} [popperOption={}]
     * @memberof AppPopover
     */
    public openPopover(event: any, content?: (h: CreateElement) => any, position: Placement = 'left-end', isAutoClose: boolean = true, width: number = 300, height: number = 300, popperOption: any = {}): void {
        // 阻止事件冒泡
        event.stopPropagation();
        const element: Element = event.toElement || event.srcElement;
        if (!this.vueExample) {
            this.initVueExample();
        }
        this.popperDestroy();
        const zIndex = this.vueExample.$store.getters.getZIndex();
        if (zIndex) {
            this.zIndex = zIndex + 100;
            this.vueExample.$store.commit('updateZIndex', this.zIndex);
        }
        // 是否可自动关闭
        this.isAutoClose = isAutoClose;
        // 更新vue实例内容
        this.showPopper = true;
        Object.assign(this.vueExample.$data, { content, width, height, zIndex: this.zIndex });
        const el: any = this.vueExample.$el;
        this.popperExample = createPopper(element, el, {
            placement: position,
            strategy: 'absolute',
            modifiers: [preventOverflow, flip],
            ...popperOption
        });
        this.vueExample.$forceUpdate();
    }

    /**
     * 销毁popper(带回填数据)
     *
     * @memberof AppPopover
     */
    public popperDestroy(): void {
        if (this.popperExample) {
            this.popperExample.destroy();
            if (this.zIndex !== 0) {
                const zIndex: any = this.zIndex;
                this.vueExample.$store.commit('updateZIndex', zIndex - 100);
                this.zIndex = 0;
            }
            this.showPopper = false;
            this.vueExample.$forceUpdate();
        }
    }

    /**
     * 获取实例
     *
     * @static
     * @memberof AppPopover
     */
    public static getInstance() {
        return AppPopover.$popover;
    }

}