import Vue from 'vue';
import { Component, Prop, Watch } from "vue-property-decorator";
import Popper, { Placement } from "popper.js";
import vueRemovedHookMixin from "vue-removed-hook-mixin";

@Component({
    template: require("../../components/popper.html"),
    mixins: [ vueRemovedHookMixin ]
})
export class PopperComponent extends Vue {
    @Prop([ String, HTMLElement ])
    public reference: string | HTMLElement;

    @Prop(Object)
    public refs: string;

    @Prop({ type: String, default: "auto" })
    public placement: Placement;

    @Prop(Boolean)
    public arrow: boolean;

    @Prop({ type: Boolean, default: true })
    public responsive: boolean;

    private _event;
    private _popper;

    private getReferenceElement() {
        const isInPortal = this.$parent.$options.name == 'portalTarget';

        if (typeof this.reference === 'string') {
            if (this.refs) {
                return this.refs[this.reference];
            }

            if (isInPortal) {
                return this.$parent.$parent.$refs[this.reference];
            }

            return this.$parent.$refs[this.reference];
        }

        if (this.reference instanceof HTMLElement) {
            return this.reference;
        }

        return isInPortal ? this.$parent.$el : this.$el.parentElement;
    }

    focusOut(event: MouseEvent) {
        if (this.$el.contains(event.target as Node)) {
            return;
        }

        this.$emit('leave', event);
    }

    mounted() {
        const reference = this.getReferenceElement();

        this._popper = new Popper(reference, this.$el, {
            placement: this.placement,
            modifiers: {
                arrow: { enabled: this.arrow, element: this.$refs['arrow'] as Element },
                responsive: {
                    enabled: this.responsive,
                    order: 890,
                    fn(data) {
                        if (window.innerWidth < 560) {
                            data.instance.options.placement = 'top';
                            data.styles.transform = `translate3d(0, ${data.offsets.popper.top}px, 0)`;
                            data.styles.right = '0';
                            data.styles.left = '0';
                            data.styles.width = 'auto';
                            data.arrowStyles.left = `${data.offsets.popper.left + data.offsets.arrow.left}px`;
                        }

                        return data;
                    }
                }
            }
        });

        this.$nextTick(() => {
            this._popper.update();
            document.addEventListener('click', this._event = this.focusOut.bind(this), { capture: true });
        });
    }

    updated() {
        this._popper.update();
    }

    @Watch('visible')
    private onVisibilityUpdate() {
        this._popper.update();
        window.dispatchEvent(new Event('resize'));
    }

    beforeDestroy() {
        this._event && document.removeEventListener('click', this._event, { capture: true });
    }

    removed() {
        this._popper.destroy()
    }
}

@Component({ template: require('../../components/fold.html') })
export class FoldComponent extends Vue {
    private observer: MutationObserver;

    @Prop(Boolean)
    public visible: boolean;

    @Prop(Boolean)
    public lazy: boolean;

    mounted() {
        this.resize();

        this.observer = new MutationObserver(() => this.resize());
        this.observer.observe(this.$refs['inner'] as Node, {
            characterData: true,
            subtree: true,
            childList: true
        });
    }

    beforeDestroy() {
        this.observer.disconnect();
    }

    @Watch('visible')
    private resize() {
        const inner = this.$refs['inner'] as HTMLDivElement;
        (this.$el as HTMLElement).style.height = `${(this.visible && inner) ? inner.clientHeight : 0}px`;
    }
}

@Component({ template: require("../../components/lazy.html") })
export class LazyComponent extends Vue {
    @Prop(Boolean)
    public    activate: boolean;
    protected visible:  boolean = false;

    @Watch('activate')
    private onVisibilityChange(value, old) {
        this.visible = value || old;
    }
}

Vue.component('Popper', PopperComponent);
Vue.component('Fold', FoldComponent);
Vue.component('Lazy', LazyComponent);

// https://github.com/vuejs/vue/issues/7829
Vue.component('Empty', {
    functional: true,
    render: (h, { data }) => h('template', data, '')
});