/**
 * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
 */

/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */

var QfqNS = QfqNS || {};

(function (n) {
    'use strict';

    /**
     * Display a message.
     *
     * Display one message on a page. Several instances can be used per page, which results in messages being
     * stacked, with the latest message being at the bottom.
     *
     * The first instance displaying a message will append an `alert container` to the body. The last message being
     * dismissed will remove the `alert container`. A typical call sequence might look like:
     *
     *     var alert = new QfqNS.Alert("Text being displayed", "info");
     *     alert.show();
     *
     * Messages may have different background colors (severity levels), controlled by the second argument
     * `messageType` of the constructor. The possible values are
     *
     *  * `"info"`
     *  * `"warning"`
     *  * `"error"`
     *
     * The values are translated into Bootstrap `alert-*` classes internally.
     *
     * If no buttons are configured, a click anywhere on the alert will close it.
     *
     * Buttons are configured by passing an array of objects to the constructor. The attributes of the object are as
     * follows
     *
     *   {
     *     label: <button label>,
     *     focus: true | false,
     *     eventName: <eventname>
     *   }
     *
     * You can connect to the button events by using
     *
     *   var alert = new QfqNS.Alert("Text being displayed", "info", [{ label: "OK", eventName: "ok" }]);
     *   alert.on('alert.ok', function(...) { ... });
     *
     * Events are named according to `alert.<eventname>`.
     *
     * @param message {string} message to be displayed
     * @param messageType {string} type of message, can either be `"info"`, `"warning"`, or `"error"`.
     * @param buttons {list} buttons to be displayed
     * When `"none"` is provided, clicking anywhere on the message will dismiss it.
     * @constructor
     */
    n.Alert = function (message, messageType, buttons) {
        this.message = message;
        this.messageType = messageType || "info";
        this.buttons = buttons || [];

        this.$alertDiv = null;
        this.shown = false;
        // this.timeout < 1 means forever
        this.timeout = 0;
        this.fadeInDuration = 400;
        this.fadeOutDuration = 400;
        this.timerId = null;

        this.eventEmitter = new EventEmitter();

    };

    n.Alert.prototype.on = n.EventEmitter.onMixin;

    /**
     *
     * @private
     */
    n.Alert.prototype.makeAlertContainerSingleton = function () {
        var alertContainer = $("#qfqAlertContainer");
        if (alertContainer.length === 0) {
            // No container so far, create one
            alertContainer = $("<div>").attr("id", "qfqAlertContainer");
            $("body").append(alertContainer);
        }

        return alertContainer;
    };

    /**
     *
     * @returns {number|jQuery}
     * @private
     */
    n.Alert.prototype.countAlertsInAlertContainer = function () {
        return $("#qfqAlertContainer > div").length;
    };

    /**
     * @private
     */
    n.Alert.prototype.removeAlertContainer = function () {
        $("#qfqAlertContainer").remove();
    };

    /**
     * @private
     */
    n.Alert.prototype.getAlertClassBasedOnMessageType = function () {
        switch (this.messageType) {
            case "warning":
                return "alert-warning";
            case "error":
                return "alert-danger";
            /* jshint -W086 */
            default:
                n.Log.warning("Message type '" + this.messageType + "' unknown. Use default type.");
            case "info":
                return "alert-success";
            /* jshint +W086 */
        }
    };

    /**
     * @private
     */
    n.Alert.prototype.getButtons = function () {
        var $buttons = null;
        var numberOfButtons = this.buttons.length;
        var index;
        var buttonConfiguration;

        for (index = 0; index < numberOfButtons; index++) {
            buttonConfiguration = this.buttons[index];

            if (!$buttons) {
                $buttons = $("<div>").addClass("alert-buttons");
            }

            var focus = buttonConfiguration.focus ? buttonConfiguration.focus : false;

            $buttons.append($("<button>").append(buttonConfiguration.label)
                .attr('type', 'button')
                .addClass("btn btn-default" + (focus ? " wants-focus" : ""))
                .click(buttonConfiguration, this.buttonHandler.bind(this)));
        }

        return $buttons;
    };

    n.Alert.prototype.show = function () {
        if (this.shown) {
            // We only allow showing once
            return;
        }

        var $alertContainer = this.makeAlertContainerSingleton();
        this.$alertDiv = $("<div>")
            .hide()
            .addClass("alert")
            .addClass(this.getAlertClassBasedOnMessageType())
            .attr("role", "alert")
            .append(this.message);


        var buttons = this.getButtons();
        if (buttons) {
            // Buttons will take care of removing the message
            this.$alertDiv.append(buttons);
        } else {
            // Click on the message anywhere will remove the message
            this.$alertDiv.click(this.removeAlert.bind(this));
        }

        $alertContainer.append(this.$alertDiv);
        this.$alertDiv.slideDown(this.fadeInDuration, this.afterFadeIn.bind(this));
        this.$alertDiv.find(".wants-focus").focus();
        this.shown = true;


    };

    /**
     * @private
     */
    n.Alert.prototype.afterFadeIn = function () {
        if (this.timeout > 0) {
            this.timerId = window.setTimeout(this.removeAlert.bind(this), this.timeout);
        }
    };

    /**
     *
     *
     * @private
     */
    n.Alert.prototype.removeAlert = function () {
        // In case we have an armed timer (or expired timer, for that matter), disarm it.
        if (this.timerId) {
            window.clearTimeout(this.timerId);
            this.timerId = null;
        }

        var that = this;
        this.$alertDiv.slideUp(this.fadeOutDuration, function () {
            that.$alertDiv.remove();
            that.$alertDiv = null;
            that.shown = false;

            if (that.countAlertsInAlertContainer() === 0) {
                that.removeAlertContainer();
            }
        });

    };

    /**
     *
     * @param handler
     *
     * @private
     */
    n.Alert.prototype.buttonHandler = function (event) {
        this.removeAlert();
        this.eventEmitter.emitEvent('alert.' + event.data.eventName, n.EventEmitter.makePayload(this, null));
    };

    n.Alert.prototype.isShown = function () {
        return this.shown;
    };

})(QfqNS);