/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Frontstage
 */
var _a;
import { BeUiEvent, Logger } from "@itwin/core-bentley";
import { IModelApp, InteractiveTool } from "@itwin/core-frontend";
import { UiFramework } from "../UiFramework.js";
import { ToolInformation } from "../toolsettings/ToolInformation.js";
import { FrontstageDef } from "./FrontstageDef.js";
import { FrontstageProvider } from "./FrontstageProvider.js";
import { TimeTracker } from "../configurableui/TimeTracker.js";
import { UiItemsManager } from "../ui-items-provider/UiItemsManager.js";
/** Frontstage Manager class.
 * @internal
 */
export class InternalFrontstageManager {
    /** This should only be called within InternalFrontstageManager and its tests.
     *  @internal
     */
    static ensureToolInformationIsSet(toolId) {
        if (this._toolInformationMap.has(toolId))
            return;
        this._toolInformationMap.set(toolId, 
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        new ToolInformation(toolId));
    }
    /** Initializes the InternalFrontstageManager
     * @internal
     */
    static initialize() {
        if (this._initialized)
            return;
        IModelApp.toolAdmin.activeToolChanged.addListener((tool) => {
            // make sure tool settings properties are cached before creating ToolInformation
            UiFramework.toolSettings.clearToolSettingsData();
            if (tool instanceof InteractiveTool)
                UiFramework.toolSettings.initializeDataForTool(tool);
            // if the tool data is not already cached then see if there is data to cache
            this.ensureToolInformationIsSet(tool.toolId);
            UiFramework.frontstages.setActiveTool(tool);
        });
        UiFramework.toolSettings.onSyncToolSettingsProperties.addListener(this._handleSyncToolSettingsPropertiesEvent);
        UiFramework.toolSettings.onReloadToolSettingsProperties.addListener(this._handleReloadToolSettingsEvent);
        IModelApp.viewManager.onSelectedViewportChanged.addListener(this._handleSelectedViewportChanged);
        UiItemsManager.onUiProviderRegisteredEvent.addListener(() => {
            const frontstageDef = UiFramework.frontstages.activeFrontstageDef;
            if (!frontstageDef)
                return;
            frontstageDef.updateWidgetDefs();
        });
        this._initialized = true;
    }
    /** @internal */
    static get isInitialized() {
        return this._initialized;
    }
    static set isInitialized(v) {
        this._initialized = v;
    }
    /** Returns true if Frontstage is loading its controls. If false the Frontstage content and controls have been created. */
    static get isLoading() {
        return this._isLoading;
    }
    /** @internal */
    static get nineZoneSize() {
        return this._nineZoneSize;
    }
    static set nineZoneSize(size) {
        this._nineZoneSize = size;
    }
    /** @internal */
    static get frontstageDefs() {
        return this._frontstageDefs;
    }
    /** Clears the Frontstage map.
     */
    static clearFrontstageDefs() {
        this._frontstageDefs.clear();
        this._activeFrontstageDef = undefined;
    }
    /** Clears the Frontstage Providers and the defs that may have been created from them.
     */
    static clearFrontstageProviders() {
        this._frontstageProviders.clear();
        UiFramework.frontstages.clearFrontstageDefs();
    }
    /** Clears the Frontstages, Frontstage Providers and the defs that may have been created from them. */
    static clearFrontstages() {
        this._frontstages.clear();
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        UiFramework.frontstages.clearFrontstageProviders();
    }
    static getFrontstageKey(frontstageId) {
        return frontstageId;
    }
    /** @internal */
    static clearFrontstageDefsForIModelId(iModelId) {
        if (!iModelId)
            return;
        const keysToRemove = [];
        this._frontstageDefs.forEach((_, key) => {
            if (key.startsWith(`[${iModelId}]`))
                keysToRemove.push(key);
        });
        keysToRemove.forEach((keyValue) => {
            this._frontstageDefs.delete(keyValue);
        });
    }
    static addFrontstageProvider(
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    frontstageProvider) {
        const key = this.getFrontstageKey(frontstageProvider.id);
        key && this._frontstageDefs.delete(key);
        this._frontstageProviders.set(frontstageProvider.id, frontstageProvider);
    }
    static addFrontstage(frontstage) {
        const key = this.getFrontstageKey(frontstage.id);
        key && this._frontstageDefs.delete(key);
        this._frontstages.set(frontstage.id, frontstage);
    }
    /** Find a loaded Frontstage with a given id. If the id is not provided, the active Frontstage is returned.
     * If the FrontstageDef has not been cached by FrontstageDef then it will not be found. See async function
     * `getFrontstageDef` to get a FrontstageDef.
     * @param id  Id of the Frontstage to find
     * @returns  FrontstageDef with a given id if found, or undefined if not found.
     */
    static findFrontstageDef(id) {
        const key = this.getFrontstageKey(id);
        const frontstageDef = this._frontstageDefs.get(key);
        if (frontstageDef instanceof FrontstageDef)
            return frontstageDef;
        return undefined;
    }
    static findFrontstageProvider(id
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    ) {
        if (!id) {
            return undefined;
        }
        const provider = this._frontstageProviders.get(id);
        if (provider) {
            return provider;
        }
        const frontstage = this._frontstages.get(id);
        if (frontstage) {
            // eslint-disable-next-line @typescript-eslint/no-deprecated
            return new (class extends FrontstageProvider {
                get id() {
                    return frontstage?.id ?? "";
                }
                frontstageConfig() {
                    return frontstage;
                }
            })();
        }
        return undefined;
    }
    /** Find a loaded Frontstage with a given id. If the id is not provided, the active Frontstage is returned. If
     * no cached FrontstageDef is found but a FrontstageProvider is registered a FrontstageDef will be created, cached, and
     * returned.
     * @param id  Id of the Frontstage to find
     * @returns  FrontstageDef with a given id if found, or undefined if not found.
     */
    static async getFrontstageDef(id) {
        if (!id)
            return UiFramework.frontstages.activeFrontstageDef;
        let frontstageDef = this.findFrontstageDef(id);
        if (frontstageDef)
            return frontstageDef;
        const frontstageProvider = this.findFrontstageProvider(id);
        if (frontstageProvider) {
            frontstageDef = await FrontstageDef.create(frontstageProvider);
            if (frontstageDef) {
                const key = this.getFrontstageKey(frontstageDef.id);
                this._frontstageDefs.set(key, frontstageDef);
            }
            return frontstageDef;
        }
        return undefined;
    }
    /** Gets the active FrontstageDef. If a Frontstage is not active, undefined is returned.
     * @return  Active FrontstageDef, or undefined if one is not active.
     */
    static get activeFrontstageDef() {
        return this._activeFrontstageDef;
    }
    /** Gets the Id of the active FrontstageDef. If a Frontstage is not active, blank is returned.
     * @return  Id of the active FrontstageDef, or blank if one is not active.
     */
    static get activeFrontstageId() {
        const activeFrontstage = this._activeFrontstageDef;
        return activeFrontstage ? activeFrontstage.id : "";
    }
    static hasFrontstage(frontstageId) {
        if (this.findFrontstageDef(frontstageId))
            return true;
        if (this.findFrontstageProvider(frontstageId))
            return true;
        return false;
    }
    /** Sets the active FrontstageDef give the stageId.
     * @param  frontstageId  Id of the Frontstage to set active.
     * @returns A Promise that is fulfilled when the Frontstage is ready.
     */
    static async setActiveFrontstage(frontstageId) {
        const frontstageDef = await UiFramework.frontstages.getFrontstageDef(frontstageId);
        if (!frontstageDef) {
            Logger.logError(UiFramework.loggerCategory("InternalFrontstageManager"), `setActiveFrontstage: Could not load a FrontstageDef with id of '${frontstageId}'`);
            return;
        }
        return UiFramework.frontstages.setActiveFrontstageDef(frontstageDef);
    }
    /** Sets the active FrontstageDef.
     * @param  frontstageDef  FrontstageDef to set active.
     * @returns A Promise that is fulfilled when the [[FrontstageDef]] is ready.
     */
    static async setActiveFrontstageDef(frontstageDef) {
        if (this._activeFrontstageDef === frontstageDef)
            return;
        this._isLoading = true;
        const deactivatedFrontstageDef = this._activeFrontstageDef;
        if (deactivatedFrontstageDef) {
            await deactivatedFrontstageDef.onDeactivated();
            const timeTracker = deactivatedFrontstageDef.timeTracker;
            UiFramework.frontstages.onFrontstageDeactivatedEvent.emit({
                deactivatedFrontstageDef,
                activatedFrontstageDef: frontstageDef,
                totalTime: timeTracker.getTotalTimeSeconds(),
                engagementTime: timeTracker.getEngagementTimeSeconds(),
                idleTime: timeTracker.getIdleTimeSeconds(),
            });
        }
        this._activeFrontstageDef = frontstageDef;
        if (frontstageDef) {
            await frontstageDef.onActivated();
            UiFramework.frontstages.onFrontstageActivatedEvent.emit({
                activatedFrontstageDef: frontstageDef,
                deactivatedFrontstageDef,
            });
            await frontstageDef.waitUntilReady();
            this._isLoading = false;
            frontstageDef.onFrontstageReady();
            UiFramework.frontstages.onFrontstageReadyEvent.emit({ frontstageDef });
            UiFramework.visibility.handleFrontstageReady();
            await frontstageDef.setActiveContent();
        }
        this._isLoading = false;
    }
    /** Deactivates the active FrontstageDef.
     */
    static async deactivateFrontstageDef() {
        await UiFramework.frontstages.setActiveFrontstageDef(undefined);
    }
    /** Gets the Id of the active tool. If a tool is not active, blank is returned.
     * @return  Id of the active tool, or blank if one is not active.
     */
    static get activeToolId() {
        return this._activeToolId;
    }
    /** Sets the active tool id */
    static setActiveToolId(toolId) {
        this._activeToolId = toolId;
        const toolSettingsProvider = this.activeToolSettingsProvider;
        // ensure the toolSettingsProvider is initialized before emitting onToolActivatedEvent
        if (toolSettingsProvider)
            toolSettingsProvider.initialize();
        UiFramework.frontstages.onToolActivatedEvent.emit({ toolId });
    }
    /** Sets the active tool */
    static setActiveTool(tool) {
        UiFramework.frontstages.setActiveToolId(tool.toolId);
        UiFramework.frontstages.onToolIconChangedEvent.emit({
            iconSpec: tool.iconSpec,
        });
    }
    /** Gets the active tool's [[ToolInformation]] */
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    static get activeToolInformation() {
        return this._toolInformationMap.get(UiFramework.frontstages.activeToolId);
    }
    /** Gets the Tool Setting React node of the active tool.
     * @return  Tool Setting React node of the active tool, or undefined if there is no active tool or Tool Settings for the active tool.
     * @internal
     */
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    static get activeToolSettingsProvider() {
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        const activeToolInformation = UiFramework.frontstages.activeToolInformation;
        return activeToolInformation?.toolUiProvider;
    }
    /** Sets the active layout, content group and active content.
     * @param contentLayoutDef  Content layout to make active
     * @param contentGroup  Content Group to make active
     */
    static async setActiveLayout(contentLayoutDef, contentGroup) {
        const activeFrontstageDef = UiFramework.frontstages.activeFrontstageDef;
        if (activeFrontstageDef) {
            this._isLoading = false;
            activeFrontstageDef.setContentLayoutAndGroup(contentLayoutDef, contentGroup);
            UiFramework.frontstages.onContentLayoutActivatedEvent.emit({
                contentLayout: contentLayoutDef,
                contentGroup,
            });
            await activeFrontstageDef.waitUntilReady();
            this._isLoading = false;
            await activeFrontstageDef.setActiveContent();
        }
    }
    /** Sets the active layout, content group and active content.
     * @param contentGroup  Content Group to make active
     */
    static async setActiveContentGroup(contentGroup) {
        const contentLayoutDef = UiFramework.content.layouts.getForGroup(contentGroup);
        await this.setActiveLayout(contentLayoutDef, contentGroup);
    }
    /** Opens a modal Frontstage. Modal Frontstages can be stacked.
     * @param modalFrontstage  Information about the modal Frontstage
     */
    static openModalFrontstage(modalFrontstage) {
        this.pushModalFrontstage(modalFrontstage);
    }
    static pushModalFrontstage(modalFrontstage) {
        const timeTracker = new TimeTracker();
        timeTracker.startTiming();
        const frontstageItem = {
            modalFrontstage,
            timeTracker,
        };
        this._modalFrontstages.push(frontstageItem);
        this.emitModalFrontstageChangedEvent();
    }
    /** Closes the top-most modal Frontstage.
     */
    static closeModalFrontstage() {
        if (this._modalFrontstages.length > 0) {
            const topMostStageItem = this._modalFrontstages[this._modalFrontstages.length - 1];
            if (topMostStageItem.modalFrontstage.notifyCloseRequest)
                UiFramework.frontstages.onCloseModalFrontstageRequestedEvent.emit({
                    modalFrontstage: topMostStageItem.modalFrontstage,
                    stageCloseFunc: () => this.popModalFrontstage(),
                });
            else
                this.popModalFrontstage();
        }
    }
    static popModalFrontstage() {
        const frontstageItem = this._modalFrontstages.pop();
        if (frontstageItem) {
            const modalFrontstage = frontstageItem.modalFrontstage;
            const timeTracker = frontstageItem.timeTracker;
            timeTracker.stopTiming();
            UiFramework.frontstages.onModalFrontstageClosedEvent.emit({
                modalFrontstage,
                totalTime: timeTracker.getTotalTimeSeconds(),
                engagementTime: timeTracker.getEngagementTimeSeconds(),
                idleTime: timeTracker.getIdleTimeSeconds(),
            });
        }
        this.emitModalFrontstageChangedEvent();
        UiFramework.visibility.handleFrontstageReady();
    }
    static emitModalFrontstageChangedEvent() {
        UiFramework.frontstages.onModalFrontstageChangedEvent.emit({
            modalFrontstageCount: UiFramework.frontstages.modalFrontstageCount,
        });
    }
    /** Updates the top-most modal Frontstage.
     */
    static updateModalFrontstage() {
        this.emitModalFrontstageChangedEvent();
    }
    /** Gets the top-most modal Frontstage.
     * @returns Top-most modal Frontstage, or undefined if there is none.
     */
    static get activeModalFrontstage() {
        if (this._modalFrontstages.length > 0) {
            const frontstageItem = this._modalFrontstages[this._modalFrontstages.length - 1];
            const modalFrontstage = frontstageItem.modalFrontstage;
            return modalFrontstage;
        }
        else {
            return undefined;
        }
    }
    /** Gets the number of modal Frontstages.
     * @returns Modal Frontstage count
     */
    static get modalFrontstageCount() {
        return this._modalFrontstages.length;
    }
    /** Sets the active Navigation Aid via its Id.
     * @param navigationAidId  Id of the Navigation Aid to set as active
     * @param iModelConnection IModelConnection to query for view data
     */
    static setActiveNavigationAid(navigationAidId, iModelConnection) {
        UiFramework.frontstages.onNavigationAidActivatedEvent.emit({
            navigationAidId,
            iModelConnection,
        });
    }
    /** Sets the state of the widget with the given id
     * @param widgetId  Id of the Widget for which to set the state
     * @param state     New state of the widget
     * @returns true if the widget state was set successfully, or false if not.
     */
    static setWidgetState(widgetId, state) {
        const widgetDef = UiFramework.frontstages.findWidget(widgetId);
        if (widgetDef) {
            widgetDef.setWidgetState(state);
            return true;
        }
        else {
            Logger.logError(UiFramework.loggerCategory("InternalFrontstageManager"), `setWidgetState: Could not find Widget with id of '${widgetId}'`);
        }
        return false;
    }
    /** Finds a widget with the given id in the active frontstage
     * @param widgetId  Id of the Widget to find
     * @returns The WidgetDef with the given id, or undefined if not found.
     */
    static findWidget(widgetId) {
        const activeFrontstageDef = UiFramework.frontstages.activeFrontstageDef;
        if (!activeFrontstageDef)
            return undefined;
        return activeFrontstageDef.findWidgetDef(widgetId);
    }
    /** Opens a nested Frontstage. Nested Frontstages can be stacked.
     * @param nestedFrontstage  Information about the nested Frontstage
     */
    static async openNestedFrontstage(nestedFrontstage) {
        if (UiFramework.frontstages.nestedFrontstageCount === 0)
            this._activePrimaryFrontstageDef = this._activeFrontstageDef;
        this.pushNestedFrontstage(nestedFrontstage);
        await UiFramework.frontstages.setActiveFrontstageDef(nestedFrontstage);
    }
    static pushNestedFrontstage(nestedFrontstage) {
        this._nestedFrontstages.push(nestedFrontstage);
    }
    /** Closes the top-most nested Frontstage.
     */
    static async closeNestedFrontstage() {
        this.popNestedFrontstage();
        if (UiFramework.frontstages.nestedFrontstageCount > 0) {
            await UiFramework.frontstages.setActiveFrontstageDef(UiFramework.frontstages.activeNestedFrontstage);
        }
        else {
            await UiFramework.frontstages.setActiveFrontstageDef(this._activePrimaryFrontstageDef);
            this._activePrimaryFrontstageDef = undefined;
        }
    }
    static popNestedFrontstage() {
        this._nestedFrontstages.pop();
    }
    /** Gets the top-most nested Frontstage.
     * @returns Top-most nested Frontstage, or undefined if there is none.
     */
    static get activeNestedFrontstage() {
        if (this._nestedFrontstages.length > 0)
            return this._nestedFrontstages[this._nestedFrontstages.length - 1];
        return undefined;
    }
    /** Gets the number of nested Frontstages.
     * @returns Nested Frontstage count
     */
    static get nestedFrontstageCount() {
        return this._nestedFrontstages.length;
    }
}
_a = InternalFrontstageManager;
InternalFrontstageManager._initialized = false;
InternalFrontstageManager._isLoading = false;
InternalFrontstageManager._activeToolId = "";
InternalFrontstageManager._frontstageDefs = new Map();
InternalFrontstageManager._modalFrontstages = new Array();
// eslint-disable-next-line @typescript-eslint/no-deprecated
InternalFrontstageManager._frontstageProviders = new Map();
InternalFrontstageManager._frontstages = new Map();
InternalFrontstageManager._nineZoneSize = undefined;
InternalFrontstageManager._nestedFrontstages = new Array();
// eslint-disable-next-line @typescript-eslint/no-deprecated
InternalFrontstageManager._toolInformationMap = new Map();
InternalFrontstageManager._handleSyncToolSettingsPropertiesEvent = (args) => {
    const provider = _a.activeToolSettingsProvider;
    if (!provider)
        return;
    provider.syncToolSettingsProperties(args);
};
// pass on ReloadToolSettingsEvent from ToolAdmin so they are treated by UiProviders
InternalFrontstageManager._handleReloadToolSettingsEvent = () => {
    const provider = _a.activeToolSettingsProvider;
    if (!provider)
        return;
    provider.reloadPropertiesFromTool();
};
/** Handles a Viewport change & sets the active view accordingly */
InternalFrontstageManager._handleSelectedViewportChanged = (args) => {
    const frontstageDef = _a.activeFrontstageDef;
    if (!args.current || !frontstageDef || UiFramework.frontstages.isLoading)
        return;
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    frontstageDef.setActiveViewFromViewport(args.current);
};
/** Get Frontstage Deactivated event. */
InternalFrontstageManager.onFrontstageDeactivatedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Frontstage Activated event. */
InternalFrontstageManager.onFrontstageActivatedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Frontstage Activated event. */
InternalFrontstageManager.onFrontstageReadyEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Modal Frontstage Changed event. */
InternalFrontstageManager.onModalFrontstageChangedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Modal Frontstage Closed event. */
InternalFrontstageManager.onModalFrontstageClosedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Modal Frontstage Requested Closed event.
 * @alpha
 */
InternalFrontstageManager.onCloseModalFrontstageRequestedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Tool Activated event. */
InternalFrontstageManager.onToolActivatedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get ToolSetting Reload event. */
// eslint-disable-next-line @typescript-eslint/no-deprecated
InternalFrontstageManager.onToolSettingsReloadEvent = new BeUiEvent();
/** Get Tool Panel Opened event.
 * @internal
 */
// eslint-disable-next-line @typescript-eslint/no-deprecated
InternalFrontstageManager.onToolPanelOpenedEvent = new BeUiEvent();
/** Get Tool Icon Changed event. */
InternalFrontstageManager.onToolIconChangedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Content Layout Activated event. */
InternalFrontstageManager.onContentLayoutActivatedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Content Control Activated event. */
InternalFrontstageManager.onContentControlActivatedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Navigation Aid Activated event. */
InternalFrontstageManager.onNavigationAidActivatedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get Widget State Changed event. */
InternalFrontstageManager.onWidgetStateChangedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** @internal */
// eslint-disable-next-line @typescript-eslint/no-deprecated
InternalFrontstageManager.onWidgetDefsUpdatedEvent = new BeUiEvent();
/** @internal */
InternalFrontstageManager.onFrontstageNineZoneStateChangedEvent = 
// eslint-disable-next-line @typescript-eslint/no-deprecated
new BeUiEvent();
/** @internal */
InternalFrontstageManager.onFrontstageRestoreLayoutEvent = 
// eslint-disable-next-line @typescript-eslint/no-deprecated
new BeUiEvent();
/** @internal */
InternalFrontstageManager.onFrontstageWidgetsChangedEvent = 
// eslint-disable-next-line @typescript-eslint/no-deprecated
new BeUiEvent();
/** Get panel state changed event.
 * @alpha
 */
InternalFrontstageManager.onPanelStateChangedEvent = new BeUiEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
/** Get panel pinned changed event.
 * @alpha
 */
InternalFrontstageManager.onPanelPinnedChangedEvent = 
// eslint-disable-next-line @typescript-eslint/no-deprecated
new BeUiEvent();
/** @internal */
InternalFrontstageManager.onPanelSizeChangedEvent = new BeUiEvent();
//# sourceMappingURL=InternalFrontstageManager.js.map