/*****************************************************************************
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 * as represented by the Administrator of the National Aeronautics and Space
 * Administration. All rights reserved.
 *
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * Open MCT Web includes source code licensed under additional open source
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 * this source code distribution or the Licensing information page available
 * at runtime from the About dialog for additional information.
 *****************************************************************************/
/*global define*/

/**
 * This bundle implements the dialog service, which can be used to
 * launch dialogs for user input & notifications.
 * @namespace platform/commonUI/dialog
 */
define(
    [],
    function () {
        "use strict";
        /**
         * The dialog service is responsible for handling window-modal
         * communication with the user, such as displaying forms for user
         * input.
         * @memberof platform/commonUI/dialog
         * @constructor
         */
        function DialogService(overlayService, $q, $log) {
            this.overlayService = overlayService;
            this.$q = $q;
            this.$log = $log;
            this.overlay = undefined;
            this.dialogVisible = false;
        }

        // Stop showing whatever overlay is currently active
        // (e.g. because the user hit cancel)
        DialogService.prototype.dismiss = function () {
            var overlay = this.overlay;
            if (overlay) {
                overlay.dismiss();
            }
            this.dialogVisible = false;
        };

        DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
            // We will return this result as a promise, because user
            // input is asynchronous.
            var deferred = this.$q.defer(),
                self = this;

            // Confirm function; this will be passed in to the
            // overlay-dialog template and associated with a
            // OK button click
            function confirm(value) {
                // Pass along the result
                deferred.resolve(resultGetter ? resultGetter() : value);

                // Stop showing the dialog
                self.dismiss();
            }

            // Cancel function; this will be passed in to the
            // overlay-dialog template and associated with a
            // Cancel or X button click
            function cancel() {
                deferred.reject();
                self.dismiss();
            }

            // Add confirm/cancel callbacks
            model.confirm = confirm;
            model.cancel = cancel;

            if (this.canShowDialog(model)) {
                // Add the overlay using the OverlayService, which
                // will handle actual insertion into the DOM
                this.overlay = this.overlayService.createOverlay(
                    key,
                    model,
                    typeClass || "t-dialog"
                );

                // Track that a dialog is already visible, to
                // avoid spawning multiple dialogs at once.
                this.dialogVisible = true;
            } else {
                deferred.reject();
            }

            return deferred.promise;
        };

        /**
         * Request user input via a window-modal dialog.
         *
         * @param {FormModel} formModel a description of the form
         *        to be shown (see platform/forms)
         * @param {object} value the initial state of the form
         * @returns {Promise} a promise for the form value that the
         *          user has supplied; this may be rejected if
         *          user input cannot be obtained (for instance,
         *          because the user cancelled the dialog)
         */
        DialogService.prototype.getUserInput = function (formModel, value) {
            var overlayModel = {
                title: formModel.name,
                message: formModel.message,
                structure: formModel,
                value: value
            };

            // Provide result from the model
            function resultGetter() {
                return overlayModel.value;
            }

            // Show the overlay-dialog
            return this.getDialogResponse(
                "overlay-dialog",
                overlayModel,
                resultGetter
            );
        };

        /**
         * Request that the user chooses from a set of options,
         * which will be shown as buttons.
         *
         * @param dialogModel a description of the dialog to show
         * @return {Promise} a promise for the user's choice
         */
        DialogService.prototype.getUserChoice = function (dialogModel) {
            // Show the overlay-options dialog
            return this.getDialogResponse(
                "overlay-options",
                { dialog: dialogModel }
            );
        };

        /**
         * Tests if a dialog can be displayed. A modal dialog may only be
         * displayed if one is not already visible.
         * Will log a warning message if it can't display a dialog.
         * @returns {boolean} true if dialog is currently visible, false
         * otherwise
         */
        DialogService.prototype.canShowDialog = function(dialogModel){
            if (this.dialogVisible){
                // Only one dialog should be shown at a time.
                // The application design should be such that
                // we never even try to do this.
                this.$log.warn([
                    "Dialog already showing; ",
                    "unable to show ",
                    dialogModel.title
                ].join(""));

                return false;
            } else {
                return true;
            }
        };

        /**
         * A user action that can be performed from a blocking dialog. These
         * actions will be rendered as buttons within a blocking dialog.
         *
         * @typedef DialogOption
         * @property {string} label a label to be displayed as the button
         * text for this action
         * @property {function} callback a function to be called when the
         * button is clicked
         */

        /**
         * A description of the model options that may be passed to the
         * showBlockingMessage method. Note that the DialogModel desribed
         * here is shared with the Notifications framework.
         * @see NotificationService
         *
         * @typedef DialogModel
         * @property {string} title the title to use for the dialog
         * @property {string} severity the severity level of this message.
         * These are defined in a bundle constant with key 'dialogSeverity'
         * @property {string} hint the 'hint' message to show below the title
         * @property {string} actionText text that indicates a current action,
         * shown above a progress bar to indicate what's happening.
         * @property {number} progress a percentage value (1-100)
         * indicating the completion of the blocking task
         * @property {string} progressText the message to show below a
         * progress bar to indicate progress. For example, this might be
         * used to indicate time remaining, or items still to process.
         * @property {boolean} unknownProgress some tasks may be
         * impossible to provide an estimate for. Providing a true value for
         * this attribute will indicate to the user that the progress and
         * duration cannot be estimated.
         * @property {DialogOption} primaryOption an action that will
         * be added to the dialog as a button. The primary action can be
         * used as the suggested course of action for the user. Making it
         * distinct from other actions allows it to be styled differently,
         * and treated preferentially in banner mode.
         * @property {DialogOption[]} options a list of actions that will
         * be added to the dialog as buttons.
         */

        /**
         * Displays a blocking (modal) dialog. This dialog can be used for
         * displaying messages that require the user's
         * immediate attention. The message may include an indication of
         * progress, as well as a series of actions that
         * the user can take if necessary
         * @param {DialogModel} dialogModel defines options for the dialog
         * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
         * @returns {boolean}
         */
        DialogService.prototype.showBlockingMessage = function(dialogModel) {
            if (this.canShowDialog(dialogModel)) {
                // Add the overlay using the OverlayService, which
                // will handle actual insertion into the DOM
                this.overlay = this.overlayService.createOverlay(
                    "overlay-blocking-message",
                    dialogModel,
                    "t-dialog-sm"
                );
                // Track that a dialog is already visible, to
                // avoid spawning multiple dialogs at once.
                this.dialogVisible = true;
                return true;
            } else {
                return false;
            }
        };

        return DialogService;
    }
);
