import ngModule from '../../module';
import Popup from '../popup';
import './style.scss';

/**
 * Dialog component that allows to register function handlers for responses. If
 * any of the handlers returns false or a promise that resolves to false,
 * the dialog closing is cancelled. Also, if promise is returned, the dialog
 * will wait until it is resolved by locking itself and displaying a loading
 * animation.
 *
 * @property {Function} onResponse Handler for dialog response
 * @property {Function} onAccept Shortcut handler for accept response
 * @slot body The dialog HTML body
 * @slot buttons The dialog HTML buttons
 */
export default class Dialog extends Popup {
    /**
     * Shows the dialog and optionally registers a handler for the response.
     *
     * @param {*} data Optional user data to pass to response handler
     * @param {Function} responseHandler An optional response handler
     * @return {Promise} A promise that will be resolved with response when dialog is closed
     */
    show(data, responseHandler) {
        if (this.shown)
            return this.$q.reject(new Error('Dialog already shown'));
        super.show();

        if (typeof data == 'function') {
            responseHandler = data;
            data = null;
        }

        this.data = data;
        this.showHandler = responseHandler;
        return this.$q(resolve => {
            this.resolve = resolve;
        });
    }

    /**
     * Hides the dialog resolving the promise returned by show().
     *
     * @param {String} response The response
     */
    hide(response) {
        if (!this.shown) return;
        super.hide();

        this.showHandler = null;
        if (this.resolve)
            this.resolve(response);
    }

    /**
     * Calls the response handlers.
     *
     * @param {String} response The response code
     * @return {Boolean} The response handler return
     */
    respond(response) {
        if (!this.shown)
            return this.$q.resolve();
        return this.responseHandler(response);
    }

    /**
     * The default response handler, it can be overriden by child classes to
     * add custom logic.
     *
     * @param {String} response The response code
     * @return {Boolean} The response handler return
     */
    responseHandler(response) {
        let handlerArgs = {
            $response: response,
            $data: this.data
        };
        let cancellers = [];

        if (this.onResponse)
            cancellers.push(this.onResponse(handlerArgs));
        if (response == 'accept' && this.onAccept)
            cancellers.push(this.onAccept(handlerArgs));
        if (this.showHandler)
            cancellers.push(this.showHandler(response, this.data));

        let promises = [];
        let resolvedCancellers = [];

        for (let canceller of cancellers) {
            if (canceller instanceof Object && canceller.then)
                promises.push(canceller);
            else
                resolvedCancellers.push(canceller);
        }

        let close = () => {
            if (resolvedCancellers.indexOf(false) == -1)
                this.hide(response);
            else
                return false;
        };

        if (promises.length) {
            this.loading = true;

            return this.$q.all(promises)
                .then(res => {
                    resolvedCancellers = resolvedCancellers.concat(res);
                    return close();
                })
                .finally(() => {
                    this.loading = false;
                });
        } else
            return this.$q.resolve(close());
    }

    onButtonClick(event) {
        let buttons = this.popup.querySelector('.buttons');
        let tplButtons = buttons.querySelector('tpl-buttons');
        let node = event.target;
        while (node.parentNode != tplButtons) {
            if (node == buttons) return;
            node = node.parentNode;
        }

        this.respond(node.getAttribute('response'));
    }
}

ngModule.vnComponent('vnDialog', {
    slotTemplate: require('./index.html'),
    controller: Dialog,
    transclude: {
        title: '?tplTitle',
        body: '?tplBody',
        buttons: '?tplButtons'
    },
    bindings: {
        message: '@?',
        onResponse: '&?',
        onAccept: '&?'
    }
});