import ngModule from '../../module';
import Popup from '../popup';
import isMobile from '../../lib/is-mobile';
import './style.scss';

/**
 * A simple popover.
 *
 * @property {HTMLElement} parent The parent element to show drop down relative to
 * @property {HTMLElement} content Element holding the popover content
 *
 * @event open Thrown when popover is displayed
 * @event close Thrown when popover is hidden
 */
export default class Popover extends Popup {
    constructor(...args) {
        super(...args);
        this.displayMode = isMobile ? 'centered' : 'relative';
    }

    /**
     * Shows the popover emitting the open signal. If a parent is specified
     * it is shown in a visible relative position to it.
     *
     * @param {HTMLElement|Event} parent Overrides the parent property
     * @param {String} direction - Direction [left]
     */
    show(parent, direction) {
        if (parent instanceof Event)
            parent = event.target;

        if (parent) this.parent = parent;
        if (direction) this.direction = direction;

        super.show();
        this.content = this.popup.querySelector('.content');
        this.$timeout(() => this.relocate(), 10);
    }

    hide() {
        this.content = null;
        super.hide();
    }

    get parent() {
        return this.__parent;
    }

    // Bug #2147 Popover loses parent location
    set parent(value) {
        this.__parent = value;
        if (!value) return;

        const parentRect = value.getBoundingClientRect();
        this.parentRect = {};
        for (let prop in parentRect)
            this.parentRect[prop] = parentRect[prop];
    }

    /**
     * Repositions the popover to a correct location relative to the parent.
     */
    relocate() {
        if (!(this.parent && this._shown && this.displayMode == 'relative'))
            return;

        let margin = 10;
        let arrow = this.popup.querySelector('.arrow');

        let style = this.windowEl.style;
        style.width = '';
        style.height = '';

        let arrowStyle = arrow.style;
        arrowStyle.top = '';
        arrowStyle.bottom = '';

        let parentRect = this.parentRect;
        let popoverRect = this.windowEl.getBoundingClientRect();
        let arrowRect = arrow.getBoundingClientRect();
        let clamp = (value, min, max) => Math.min(Math.max(value, min), max);

        let arrowHeight = Math.floor(arrowRect.height / 2);
        let arrowOffset = arrowHeight + margin / 2;

        let docEl = this.document.documentElement;
        let maxRight = Math.min(window.innerWidth, docEl.clientWidth) - margin;
        let maxBottom = Math.min(window.innerHeight, docEl.clientHeight) - margin;
        let maxWith = maxRight - margin;
        let maxHeight = maxBottom - margin - arrowHeight;

        let width = clamp(popoverRect.width, parentRect.width, maxWith);
        let height = popoverRect.height;

        let left;
        if (this.direction == 'left') {
            left = parentRect.left + parentRect.width;
            left = clamp(left, margin, maxRight - width);
        } else {
            left = parentRect.left + parentRect.width / 2 - width / 2;
            left = clamp(left, margin, maxRight - width);
        }

        let top;
        if (this.direction == 'left')
            top = parentRect.top;
        else
            top = parentRect.top + parentRect.height + arrowOffset;
        let showTop = top + height > maxBottom;
        if (showTop) top = parentRect.top - height - arrowOffset;
        top = Math.max(top, margin);

        if (this.direction == 'left')
            arrowStyle.left = `0`;
        else if (showTop)
            arrowStyle.bottom = `0`;
        else
            arrowStyle.top = `0`;

        let arrowLeft;
        if (this.direction == 'left') {
            arrowLeft = 0;
            let arrowTop = arrowOffset;
            arrowStyle.top = `${arrowTop}px`;
        } else {
            arrowLeft = (parentRect.left - left) + parentRect.width / 2;
            arrowLeft = clamp(arrowLeft, arrowHeight, width - arrowHeight);
        }
        arrowStyle.left = `${arrowLeft}px`;

        style.top = `${top}px`;
        style.left = `${left}px`;
        style.width = `${width}px`;
        if (height > maxHeight) style.height = `${maxHeight}px`;
    }
}

ngModule.vnComponent('vnPopover', {
    controller: Popover
});

ngModule.run(['$compile', function($compile) {
    Popover.prototype.contentLinkFn = $compile(require('./index.html'));
}]);