"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Notification
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageManager = exports.ToolAssistanceChangedEvent = exports.OpenMessageCenterEvent = exports.InputFieldMessageRemovedEvent = exports.InputFieldMessageAddedEvent = exports.ActivityMessageCancelledEvent = exports.ActivityMessageUpdatedEvent = exports.MessagesUpdatedEvent = exports.MessageAddedEvent = void 0;
const classnames_1 = require("classnames");
const _ = require("lodash");
const React = require("react");
const core_frontend_1 = require("@itwin/core-frontend");
const appui_abstract_1 = require("@itwin/appui-abstract");
const core_react_1 = require("@itwin/core-react");
const state_1 = require("../configurableui/state");
const StandardMessageBox_1 = require("../dialog/StandardMessageBox");
const ElementTooltip_1 = require("../feedback/ElementTooltip");
const UiFramework_1 = require("../UiFramework");
const MessageSpan_1 = require("./MessageSpan");
const Pointer_1 = require("./Pointer");
const StatusMessageManager_1 = require("./StatusMessageManager");
const itwinui_react_1 = require("@itwin/itwinui-react");
class MessageBoxCallbacks {
    constructor(onFulfilled, onRejected) {
        this.onFulfilled = onFulfilled;
        this.onRejected = onRejected;
        this.handleMessageBoxResult = (result) => {
            this.onFulfilled(result);
        };
    }
}
/** Message Added Event class.
 * @public
 */
class MessageAddedEvent extends appui_abstract_1.UiEvent {
}
exports.MessageAddedEvent = MessageAddedEvent;
/** Messages Updated Event class.
 * @public
 */
class MessagesUpdatedEvent extends appui_abstract_1.UiEvent {
}
exports.MessagesUpdatedEvent = MessagesUpdatedEvent;
/** Activity Message Added Event class.
 * @public
 */
class ActivityMessageUpdatedEvent extends appui_abstract_1.UiEvent {
}
exports.ActivityMessageUpdatedEvent = ActivityMessageUpdatedEvent;
/** Activity Message Cancelled Event class.
 * @public
 */
class ActivityMessageCancelledEvent extends appui_abstract_1.UiEvent {
}
exports.ActivityMessageCancelledEvent = ActivityMessageCancelledEvent;
/** Input Field Message Added Event class
 * @public
 */
class InputFieldMessageAddedEvent extends appui_abstract_1.UiEvent {
}
exports.InputFieldMessageAddedEvent = InputFieldMessageAddedEvent;
/** Input Field Message Removed Event class.
 * @public
 */
class InputFieldMessageRemovedEvent extends appui_abstract_1.UiEvent {
}
exports.InputFieldMessageRemovedEvent = InputFieldMessageRemovedEvent;
/** Open Message Center Event class.
 * @public
 */
class OpenMessageCenterEvent extends appui_abstract_1.UiEvent {
}
exports.OpenMessageCenterEvent = OpenMessageCenterEvent;
/** Tool Assistance Changed event class
 * @public
 */
class ToolAssistanceChangedEvent extends appui_abstract_1.UiEvent {
}
exports.ToolAssistanceChangedEvent = ToolAssistanceChangedEvent;
/**
 * Keeps track of the current activity message, and updates whenever
 * setupActivityMessageDetails() or setupActivityMessageValues()
 * is called.
 * Used to display tracked progress in ActivityMessage.
 */
class OngoingActivityMessage {
    constructor() {
        this.message = "";
        this.percentage = 0;
        this.details = new core_frontend_1.ActivityMessageDetails(true, true, true);
        this.isRestored = false;
    }
}
/** The MessageManager class manages messages and prompts. It is used by the [[AppNotificationManager]] class.
 * @public
 */
class MessageManager {
    /** List of messages as NotifyMessageDetailsType. */
    static get messages() { return this._messages; }
    /** Manager of active messages. */
    static get activeMessageManager() { return this._activeMessageManager; }
    /** Clear the message list. */
    static clearMessages() {
        this._messages.splice(0);
        this._activeMessageManager.initialize();
        this.onMessagesUpdatedEvent.emit({});
        this._lastMessage = undefined;
    }
    /** Update the message list. */
    static updateMessages() {
        this.onMessagesUpdatedEvent.emit({});
    }
    /** Set the maximum number of cached message. */
    static setMaxCachedMessages(max) {
        this._maxCachedMessages = max;
        this.checkMaxCachedMessages();
    }
    /** The maximum number of displayed Sticky messages. */
    static get maxDisplayedStickyMessages() { return this._maxDisplayedStickyMessages; }
    static set maxDisplayedStickyMessages(max) { this._maxDisplayedStickyMessages = max; }
    /** Output a message and/or alert to the user.
     * @param  message  Details about the message to output.
     */
    static outputMessage(message) {
        if (message.msgType === core_frontend_1.OutputMessageType.Pointer) {
            Pointer_1.PointerMessage.showMessage(message);
        }
        else if (message.msgType === core_frontend_1.OutputMessageType.InputField) {
            if (message.inputField)
                MessageManager.displayInputFieldMessage(message.inputField, message.briefMessage, message.detailedMessage, message.priority);
            else
                message.msgType = core_frontend_1.OutputMessageType.Sticky; // Note: Changing the message.msgType here for InputField without inputField
        }
        else if (message.msgType === core_frontend_1.OutputMessageType.Alert) {
            if (message.openAlert === core_frontend_1.OutputMessageAlert.Balloon)
                message.msgType = core_frontend_1.OutputMessageType.Sticky; // Note: Changing the message.msgType here for Balloon
            else
                MessageManager.showAlertMessageBox(message);
        }
        MessageManager.addMessage(message);
    }
    /** Set the element where messages should be animated out to on exit.
     * @param  element `HTMLElement` to animate out to.
     */
    static registerAnimateOutToElement(element) {
        this._animateOutToElement = element;
    }
    /** Display a message.
     * Works only with `Sticky` and `Toast` message types.
     * @param  message  Details about the message to display.
     * @param options Optionally override individual toast parameters.
     * @param settings Optionally override all toasts settings (i.e. placement or order).
     * @returns Object with reference to the message (i.e. to close it programmatically) if it was displayed.
     */
    static displayMessage(message, options, settings) {
        if (message.msgType !== core_frontend_1.OutputMessageType.Sticky && message.msgType !== core_frontend_1.OutputMessageType.Toast) {
            return;
        }
        const toastOptions = {
            hasCloseButton: true,
            duration: message.displayTime.milliseconds,
            type: message.msgType === core_frontend_1.OutputMessageType.Sticky
                ? "persisting"
                : "temporary",
            animateOutTo: this._animateOutToElement,
            ...options,
        };
        itwinui_react_1.toaster.setSettings({ placement: "bottom", order: "ascending", ...settings });
        const content = React.createElement(React.Fragment, null,
            message.briefMessage.reactNode || message.briefMessage,
            message.detailedMessage &&
                React.createElement(itwinui_react_1.Small, { style: { display: "block" } }, message.detailedMessage.reactNode || message.detailedMessage));
        switch (message.priority) {
            case core_frontend_1.OutputMessagePriority.Warning:
                return itwinui_react_1.toaster.warning(content, toastOptions);
            case core_frontend_1.OutputMessagePriority.Info:
                return itwinui_react_1.toaster.informational(content, toastOptions);
            case core_frontend_1.OutputMessagePriority.Error:
            case core_frontend_1.OutputMessagePriority.Fatal:
                return itwinui_react_1.toaster.negative(content, toastOptions);
            default:
                return itwinui_react_1.toaster.positive(content, toastOptions);
        }
    }
    /** Output a message and/or alert to the user.
     * @param  message  Details about the message to output.
     */
    static addMessage(message) {
        if (!_.isEqual(message, this._lastMessage)) {
            this.addToMessageCenter(message);
            this._lastMessage = message;
        }
        this._activeMessageManager.add(message);
        this.onMessageAddedEvent.emit({ message });
    }
    /** Add a message to the Message Center.
     * @param  message  Details about the message to output.
     */
    static addToMessageCenter(message) {
        this._messages.push(message);
        this.onMessagesUpdatedEvent.emit({});
        this.checkMaxCachedMessages();
    }
    /** Checks number of messages against the maximum. */
    static checkMaxCachedMessages() {
        if (this._messages.length > this._maxCachedMessages) {
            const numToErase = this._maxCachedMessages / 4;
            this._messages.splice(0, numToErase);
            this.onMessagesUpdatedEvent.emit({});
        }
    }
    /**
     * Sets details for setting up an Activity message.
     * @param details    Details for setup of ActivityMessage
     * @returns true if details is valid and can be used to display ActivityMessage
     */
    static setupActivityMessageDetails(details) {
        this._ongoingActivityMessage.details = details;
        this._ongoingActivityMessage.isRestored = details.showDialogInitially;
        return true;
    }
    /**
     * Output an activity message to the user.
     * @param message         The message text.
     * @param percentComplete The percentage of completion.
     * @return true if the message was displayed, false if the message could not be displayed.
     */
    static outputActivityMessage(message, percentComplete) {
        return MessageManager.setupActivityMessageValues(message, percentComplete);
    }
    /**
     * Sets values on _OngoingActivityMessage to be referenced when displaying
     * an ActivityMessage.
     * @param message     Message of the process that ActivityMessage is tracking
     * @param percentage  Progress made by activity in percentage
     * @param restored    True if original ActivityMessage has been closed and
     *                    is now being restored from the status bar.
     * @returns true if details is valid and can be used to display ActivityMessage
     */
    static setupActivityMessageValues(message, percentage, restored) {
        this._ongoingActivityMessage.message = message;
        this._ongoingActivityMessage.percentage = percentage;
        this.onActivityMessageUpdatedEvent.emit({
            message,
            percentage,
            details: this._ongoingActivityMessage.details,
            restored: (restored !== undefined) ? restored : this._ongoingActivityMessage.isRestored,
        });
        this._ongoingActivityMessage.isRestored = false;
        return true;
    }
    /**
     * Dismisses current ActivityMessage and ends activity if canceled.
     * @param isCompleted   True if the activity was completed, false if it was canceled
     * @returns True if both ActivityMessage and activity process are ended.
     */
    static endActivityMessage(isCompleted) {
        this.endActivityProcessing(isCompleted);
        this.onActivityMessageCancelledEvent.emit({});
        return true;
    }
    /**
     * Ends processing for activity according to message definition.
     * @param isCompleted   True if the activity was completed, false if it was canceled
     */
    static endActivityProcessing(isCompleted) {
        if (isCompleted)
            this._ongoingActivityMessage.details.onActivityCompleted();
        else
            this._ongoingActivityMessage.details.onActivityCancelled();
    }
    /**
     * Displays an input field message near target element.
     * @param target  The currently focused or recently focused element to place the
     *                input field message near.
     * @param messageText  Text to display in the message.
     * @param detailedMessage   Optional detailed message text to display.
     * @param priority   Optional message priority which controls icon to display.
     */
    static displayInputFieldMessage(target, messageText, detailedMessage = "", priority = core_frontend_1.OutputMessagePriority.Error) {
        this.onInputFieldMessageAddedEvent.emit({
            target,
            messageText,
            detailedMessage,
            priority,
        });
    }
    /**
     * Hides the currently displayed input field message.
     */
    static hideInputFieldMessage() {
        this.onInputFieldMessageRemovedEvent.emit({});
    }
    /**
     * Opens message center.
     */
    static openMessageCenter() {
        this.onOpenMessageCenterEvent.emit({});
    }
    /** Output a prompt to the user. A 'prompt' indicates an action the user should take to proceed. */
    static outputPrompt(prompt) {
        UiFramework_1.UiFramework.dispatchActionToStore(state_1.ConfigurableUiActionId.SetToolPrompt, prompt, true);
    }
    /** Gets an icon CSS class name based on a given NotifyMessageDetailsType. */
    static getIconClassName(details) {
        const severity = MessageManager.getSeverity(details);
        const className = core_react_1.MessageContainer.getIconClassName(severity, false);
        const iconClassName = (0, classnames_1.default)("icon", "notifymessage-icon", className);
        return iconClassName;
    }
    /** Gets a MessageSeverity based on a given NotifyMessageDetailsType. */
    static getSeverity(details) {
        let severity = appui_abstract_1.MessageSeverity.None;
        switch (details.priority) {
            case core_frontend_1.OutputMessagePriority.None:
                severity = appui_abstract_1.MessageSeverity.Success;
                break;
            case core_frontend_1.OutputMessagePriority.Success:
                severity = appui_abstract_1.MessageSeverity.Success;
                break;
            case core_frontend_1.OutputMessagePriority.Info:
                severity = appui_abstract_1.MessageSeverity.Information;
                break;
            case core_frontend_1.OutputMessagePriority.Warning:
                severity = appui_abstract_1.MessageSeverity.Warning;
                break;
            case core_frontend_1.OutputMessagePriority.Error:
                severity = appui_abstract_1.MessageSeverity.Error;
                break;
            case core_frontend_1.OutputMessagePriority.Fatal:
                severity = appui_abstract_1.MessageSeverity.Fatal;
                break;
        }
        return severity;
    }
    /** Gets a MessageBoxIconType based on a given NotifyMessageDetailsType. */
    static getIconType(details) {
        let iconType = core_frontend_1.MessageBoxIconType.NoSymbol;
        switch (details.priority) {
            case core_frontend_1.OutputMessagePriority.None:
                iconType = core_frontend_1.MessageBoxIconType.NoSymbol;
                break;
            case core_frontend_1.OutputMessagePriority.Success:
                iconType = core_frontend_1.MessageBoxIconType.Success;
                break;
            case core_frontend_1.OutputMessagePriority.Info:
                iconType = core_frontend_1.MessageBoxIconType.Information;
                break;
            case core_frontend_1.OutputMessagePriority.Warning:
                iconType = core_frontend_1.MessageBoxIconType.Warning;
                break;
            case core_frontend_1.OutputMessagePriority.Error:
                iconType = core_frontend_1.MessageBoxIconType.Critical;
                break;
            case core_frontend_1.OutputMessagePriority.Fatal:
                iconType = core_frontend_1.MessageBoxIconType.Critical;
                break;
        }
        return iconType;
    }
    /** Output a MessageBox and wait for response from the user.
     * @param mbType       The MessageBox type.
     * @param message      The message to display.
     * @param icon         The MessageBox icon type.
     * @return the response from the user.
     */
    static async openMessageBox(mbType, message, icon) {
        const title = UiFramework_1.UiFramework.translate("general.alert");
        return new Promise((onFulfilled, onRejected) => {
            const messageBoxCallbacks = new MessageBoxCallbacks(onFulfilled, onRejected);
            const messageElement = React.createElement(MessageSpan_1.MessageSpan, { message: message });
            UiFramework_1.UiFramework.dialogs.modal.open(this.standardMessageBox(mbType, icon, title, messageElement, messageBoxCallbacks));
        });
    }
    /** @internal */
    static showAlertMessageBox(messageDetails) {
        const title = UiFramework_1.UiFramework.translate("general.alert");
        const iconType = this.getIconType(messageDetails);
        const content = (React.createElement(React.Fragment, null,
            React.createElement(MessageSpan_1.MessageSpan, { message: messageDetails.briefMessage }),
            messageDetails.detailedMessage && (React.createElement("p", null,
                React.createElement(MessageSpan_1.MessageSpan, { message: messageDetails.detailedMessage })))));
        UiFramework_1.UiFramework.dialogs.modal.open(this.standardMessageBox(core_frontend_1.MessageBoxType.Ok, iconType, title, content));
    }
    static standardMessageBox(mbType, iconType, title, messageElement, callbacks) {
        const onResult = (callbacks !== undefined) ? callbacks.handleMessageBoxResult : undefined;
        return (React.createElement(StandardMessageBox_1.StandardMessageBox, { opened: true, messageBoxType: mbType, iconType: iconType, title: title, onResult: onResult }, messageElement));
    }
    /** Setup tool assistance instructions for a tool. The instructions include the main instruction, which includes the current prompt.
     * @param instructions The tool assistance instructions.
     * @public
     */
    static setToolAssistance(instructions) {
        MessageManager.onToolAssistanceChangedEvent.emit({ instructions });
    }
    /** Show a tooltip window. Saves tooltip location for AccuSnap to test if cursor has moved far enough away to close tooltip.
     * @param htmlElement The HTMLElement that anchors the tooltip.
     * @param message     What to display inside the tooltip.
     * @param location    An optional location, relative to the origin of htmlElement, for the tooltip. If undefined, center of `htmlElement`.
     * @param options     Options that supply additional information about how the tooltip should function.
     */
    static openToolTip(htmlElement, message, location, options) {
        core_frontend_1.IModelApp.notifications.toolTipLocation.setFrom(location);
        ElementTooltip_1.ElementTooltip.showTooltip(htmlElement, message, location, options);
    }
    /** @internal */
    static closeAllMessages() {
        ElementTooltip_1.ElementTooltip.hideTooltip();
        Pointer_1.PointerMessage.hideMessage();
        MessageManager.clearMessages();
        MessageManager.hideInputFieldMessage();
        MessageManager.endActivityMessage(false);
    }
}
exports.MessageManager = MessageManager;
MessageManager._maxCachedMessages = 500;
MessageManager._maxDisplayedStickyMessages = 3;
MessageManager._messages = [];
MessageManager._ongoingActivityMessage = new OngoingActivityMessage();
MessageManager._activeMessageManager = new StatusMessageManager_1.StatusMessageManager();
/** The MessageAddedEvent is fired when a message is added via outputMessage(). */
MessageManager.onMessageAddedEvent = new MessageAddedEvent();
/** The MessagesUpdatedEvent is fired when a message is added or the messages are cleared. */
MessageManager.onMessagesUpdatedEvent = new MessagesUpdatedEvent();
/** The ActivityMessageUpdatedEvent is fired when an Activity message updates via outputActivityMessage(). */
MessageManager.onActivityMessageUpdatedEvent = new ActivityMessageUpdatedEvent();
/** The ActivityMessageCancelledEvent is fired when an Activity message is cancelled via
 * endActivityMessage(ActivityMessageEndReason.Cancelled) or
 * by the user clicking the 'Cancel' link.
 */
MessageManager.onActivityMessageCancelledEvent = new ActivityMessageCancelledEvent();
MessageManager.onInputFieldMessageAddedEvent = new InputFieldMessageAddedEvent();
MessageManager.onInputFieldMessageRemovedEvent = new InputFieldMessageRemovedEvent();
MessageManager.onOpenMessageCenterEvent = new OpenMessageCenterEvent();
/** The ToolAssistanceChangedEvent is fired when a tool calls IModelApp.notifications.setToolAssistance().
 * @public
 */
MessageManager.onToolAssistanceChangedEvent = new ToolAssistanceChangedEvent();
//# sourceMappingURL=MessageManager.js.map