"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 Frontstage
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.useSpecificWidgetDef = exports.useActiveFrontstageDef = exports.useActiveFrontstageId = exports.FrontstageDef = void 0;
// cSpell:ignore popout
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 appui_layout_react_1 = require("@itwin/appui-layout-react");
const ContentGroup_1 = require("../content/ContentGroup");
const StagePanelDef_1 = require("../stagepanels/StagePanelDef");
const UiFramework_1 = require("../UiFramework");
const WidgetDef_1 = require("../widgets/WidgetDef");
const TimeTracker_1 = require("../configurableui/TimeTracker");
const PopoutWidget_1 = require("../childwindow/PopoutWidget");
const core_bentley_1 = require("@itwin/core-bentley");
const StageUsage_1 = require("./StageUsage");
const StagePanelLocation_1 = require("../stagepanels/StagePanelLocation");
const WidgetState_1 = require("../widgets/WidgetState");
const InternalFrontstageManager_1 = require("./InternalFrontstageManager");
const InternalContentDialogManager_1 = require("../dialog/InternalContentDialogManager");
/** FrontstageDef class provides an API for a Frontstage.
 * @public
 */
class FrontstageDef {
    constructor() {
        this._id = "";
        this._isStageClosing = false;
        this._isReady = false;
        this._isApplicationClosing = false;
        this._version = 0;
        this._timeTracker = new TimeTracker_1.TimeTracker();
    }
    get id() {
        return this._id;
    }
    get usage() {
        return this._usage !== undefined ? this._usage : StageUsage_1.StageUsage.General;
    }
    get version() {
        return this._version;
    }
    get contentGroupProvider() {
        return this._contentGroupProvider;
    }
    get floatingContentControls() {
        return this._floatingContentControls;
    }
    get toolSettings() {
        return this._toolSettings;
    }
    get statusBar() {
        return this._statusBar;
    }
    get contentManipulation() {
        return this._contentManipulation;
    }
    get viewNavigation() {
        return this._viewNavigation;
    }
    get topPanel() {
        return this._topPanel;
    }
    get leftPanel() {
        return this._leftPanel;
    }
    get rightPanel() {
        return this._rightPanel;
    }
    get bottomPanel() {
        return this._bottomPanel;
    }
    get contentLayoutDef() {
        return this._contentLayoutDef;
    }
    get contentGroup() {
        return this._contentGroup;
    }
    get frontstageProvider() {
        return this._frontstageProvider;
    }
    toStagePanelLocation(side) {
        switch (side) {
            case "bottom":
                return StagePanelLocation_1.StagePanelLocation.Bottom;
            case "left":
                return StagePanelLocation_1.StagePanelLocation.Left;
            case "right":
                return StagePanelLocation_1.StagePanelLocation.Right;
            case "top":
                return StagePanelLocation_1.StagePanelLocation.Top;
        }
    }
    populateStateMaps(nineZone, panelMap, widgetMap) {
        for (const panelSide of appui_layout_react_1.panelSides) {
            const panel = nineZone.panels[panelSide];
            const location = this.toStagePanelLocation(panelSide);
            const panelDef = this.getStagePanelDef(location);
            if (!panelDef)
                continue;
            const panelState = panel.collapsed
                ? StagePanelDef_1.StagePanelState.Minimized
                : StagePanelDef_1.StagePanelState.Open;
            panelMap.set(panelDef, panelState);
        }
        for (const widgetDef of this.widgetDefs) {
            const widgetId = widgetDef.id;
            if (widgetDef === this.toolSettings) {
                if (!nineZone.toolSettings) {
                    widgetMap.set(widgetDef, WidgetState_1.WidgetState.Hidden);
                    continue;
                }
                else if (nineZone.toolSettings.type === "docked") {
                    widgetMap.set(widgetDef, WidgetState_1.WidgetState.Open);
                    continue;
                }
            }
            const tabLocation = (0, appui_layout_react_1.getTabLocation)(nineZone, widgetId);
            if (!tabLocation) {
                widgetMap.set(widgetDef, WidgetState_1.WidgetState.Hidden);
                continue;
            }
            const widget = nineZone.widgets[tabLocation.widgetId];
            let widgetState = WidgetState_1.WidgetState.Open;
            if (widget.minimized || widgetId !== widget.activeTabId)
                widgetState = WidgetState_1.WidgetState.Closed;
            widgetMap.set(widgetDef, widgetState);
        }
    }
    triggerStateChangeEvents(oldState) {
        const newState = this.nineZoneState;
        if (!newState)
            return;
        if (this._isStageClosing || this._isApplicationClosing)
            return;
        const originalPanelStateMap = new Map();
        const originalWidgetStateMap = new Map();
        const newPanelStateMap = new Map();
        const newWidgetStateMap = new Map();
        this.populateStateMaps(oldState, originalPanelStateMap, originalWidgetStateMap);
        this.populateStateMaps(newState, newPanelStateMap, newWidgetStateMap);
        // Now walk and trigger set state events
        newWidgetStateMap.forEach((newWidgetState, widgetDef) => {
            const originalState = originalWidgetStateMap.get(widgetDef);
            if (originalState === newWidgetState)
                return;
            widgetDef.handleWidgetStateChanged(newWidgetState);
        });
        newPanelStateMap.forEach((newPanelState, panelDef) => {
            const originalState = originalPanelStateMap.get(panelDef);
            if (originalState === newPanelState)
                return;
            panelDef.handlePanelStateChanged(newPanelState);
        });
        for (const panelSide of appui_layout_react_1.panelSides) {
            const panel = newState.panels[panelSide];
            const oldPanel = oldState.panels[panelSide];
            const location = this.toStagePanelLocation(panelSide);
            const panelDef = this.getStagePanelDef(location);
            if (!panelDef)
                continue;
            if (panel.size !== oldPanel.size) {
                panelDef.handleSizeChanged(panel.size);
            }
            if (panel.pinned !== oldPanel.pinned) {
                panelDef.handlePinnedChanged(panel.pinned);
            }
        }
    }
    handlePopouts(oldState) {
        const newState = this.nineZoneState;
        const oldPopouts = oldState?.popoutWidgets.allIds || [];
        const newPopouts = newState?.popoutWidgets.allIds || [];
        const popoutsToClose = oldPopouts.filter((p) => !newPopouts.includes(p));
        const popoutsToOpen = newPopouts.filter((p) => !oldPopouts.includes(p));
        for (const popoutId of popoutsToClose) {
            UiFramework_1.UiFramework.childWindows.close(popoutId, true);
        }
        for (const popoutId of popoutsToOpen) {
            this.openPopoutWidgetContainer(popoutId);
        }
    }
    /** @internal */
    get nineZoneState() {
        return this._nineZoneState;
    }
    set nineZoneState(state) {
        if (this._nineZoneState === state)
            return;
        const oldState = this._nineZoneState;
        this._nineZoneState = state;
        this.handlePopouts(oldState);
        if (oldState) {
            this.triggerStateChangeEvents(oldState);
        }
        const isClosing = this._isStageClosing || this._isApplicationClosing;
        if (isClosing && !this.isReady)
            return;
        InternalFrontstageManager_1.InternalFrontstageManager.onFrontstageNineZoneStateChangedEvent.emit({
            frontstageDef: this,
            state,
        });
    }
    /** @internal */
    get dispatch() {
        return (this._dispatch ??= (action) => {
            if (action.type === "RESIZE") {
                InternalFrontstageManager_1.InternalFrontstageManager.nineZoneSize = core_react_1.Size.create(action.size);
            }
            const state = this.nineZoneState;
            if (!state)
                return;
            this.nineZoneState = (0, appui_layout_react_1.NineZoneStateReducer)(state, action);
        });
    }
    /** @internal */
    get timeTracker() {
        return this._timeTracker;
    }
    /** Created a [[FrontstageDef]] and initialize it */
    static async create(provider) {
        const def = new FrontstageDef();
        def._frontstageProvider = provider;
        const config = provider.frontstageConfig();
        await def.initializeFromConfig(config);
        return def;
    }
    /** Handles when the Frontstage becomes activated */
    async _onActivated() { }
    /** Handles when the Frontstage becomes activated */
    async onActivated() {
        this.updateWidgetDefs();
        const provider = this._contentGroupProvider;
        if (provider && this._initialConfig) {
            this._contentGroup = await provider.contentGroup(this._initialConfig);
        }
        // istanbul ignore next
        if (!this._contentGroup)
            throw new appui_abstract_1.UiError(UiFramework_1.UiFramework.loggerCategory(this), `onActivated: Content Group not defined`);
        this._contentLayoutDef = UiFramework_1.UiFramework.content.layouts.getForGroup(this._contentGroup);
        UiFramework_1.UiFramework.frontstages.onContentLayoutActivatedEvent.emit({
            contentLayout: this._contentLayoutDef,
            contentGroup: this._contentGroup,
        });
        this._timeTracker.startTiming();
        await this._onActivated();
    }
    /** Handles when the Frontstage becomes inactive */
    async _onDeactivated() { }
    /** Handles when the Frontstage becomes inactive */
    async onDeactivated() {
        for (const control of this._widgetControls) {
            control.onFrontstageDeactivated();
        }
        for (const control of this.contentControls) {
            control.onFrontstageDeactivated();
        }
        // istanbul ignore else
        if (this.contentGroup)
            this.contentGroup.onFrontstageDeactivated();
        // istanbul ignore next
        if (this.contentGroupProvider)
            await this.contentGroupProvider.onFrontstageDeactivated();
        this._timeTracker.stopTiming();
        this._isStageClosing = true; // this keeps widgets in child windows from automatically re-docking
        UiFramework_1.UiFramework.childWindows.closeAll();
        if (this._floatingContentControls) {
            InternalContentDialogManager_1.InternalContentDialogManager.closeAll();
            this._floatingContentControls = undefined;
        }
        if (this._toolAdminDefaultToolId) {
            core_frontend_1.IModelApp.toolAdmin.defaultToolId = this._toolAdminDefaultToolId;
            this._toolAdminDefaultToolId = undefined;
        }
        await this._onDeactivated();
        this._isStageClosing = false;
    }
    /** @internal */
    setIsApplicationClosing(value) {
        this._isApplicationClosing = value;
    }
    /** Returns once the contained widgets and content controls are ready to use */
    async waitUntilReady() {
        this._isReady = false;
        // create an array of control-ready promises
        const controlReadyPromises = new Array();
        this._widgetControls.forEach((control) => {
            controlReadyPromises.push(control.isReady);
        });
        // istanbul ignore else
        if (this.contentLayoutDef) {
            const usedContentIndexes = this.contentLayoutDef.getUsedContentIndexes();
            this.contentControls.forEach((control, index) => {
                // istanbul ignore else
                if (usedContentIndexes.includes(index))
                    controlReadyPromises.push(control.isReady);
            });
        }
        await Promise.all(controlReadyPromises);
        // Frontstage ready
        this._isReady = true;
    }
    /** Handles when the Frontstage becomes active */
    _onFrontstageReady() { }
    /** Handles when the Frontstage becomes active */
    onFrontstageReady() {
        for (const control of this._widgetControls) {
            control.onFrontstageReady();
        }
        for (const control of this.contentControls) {
            control.onFrontstageReady();
        }
        // istanbul ignore else
        if (this.contentGroup)
            this.contentGroup.onFrontstageReady();
        if (core_frontend_1.IModelApp.toolAdmin && core_frontend_1.IModelApp.viewManager && this._initialConfig) {
            const defaultTool = this._initialConfig.defaultTool;
            if (defaultTool) {
                this._toolAdminDefaultToolId = core_frontend_1.IModelApp.toolAdmin.defaultToolId;
                core_frontend_1.IModelApp.toolAdmin.defaultToolId = defaultTool;
                void core_frontend_1.IModelApp.tools.run(defaultTool);
            }
            else {
                void core_frontend_1.IModelApp.toolAdmin.startDefaultTool();
            }
        }
        this._onFrontstageReady();
    }
    /** Sets the Content Layout and Content Group */
    setContentLayoutAndGroup(contentLayoutDef, contentGroup) {
        this._contentLayoutDef = contentLayoutDef;
        this._contentGroup = contentGroup;
    }
    /** Sets the active view content control to the default or first */
    async setActiveContent() {
        let contentControl;
        let activated = false;
        // istanbul ignore else
        if (!contentControl && this.contentControls.length >= 0) {
            contentControl = this.contentControls[0];
        }
        if (contentControl) {
            UiFramework_1.UiFramework.content.setActive(contentControl.reactNode, true);
            if (contentControl.viewport) {
                const status = await core_frontend_1.IModelApp.viewManager.setSelectedView(contentControl.viewport);
                activated = status === core_bentley_1.BentleyStatus.SUCCESS;
            }
        }
        return activated;
    }
    /** Sets the active view content control */
    setActiveView(newContent, oldContent) {
        if (oldContent)
            oldContent.onDeactivated();
        newContent.onActivated();
        UiFramework_1.UiFramework.frontstages.onContentControlActivatedEvent.emit({
            activeContentControl: newContent,
            oldContentControl: oldContent,
        });
    }
    /** Sets the active view content control based on the selected viewport. */
    setActiveViewFromViewport(viewport) {
        const contentControl = this.contentControls.find((control) => control.viewport === viewport);
        // istanbul ignore else
        if (contentControl) {
            UiFramework_1.UiFramework.content.setActive(contentControl.reactNode, true);
            return true;
        }
        // istanbul ignore next
        return false;
    }
    /** Gets a [[StagePanelDef]] based on a given panel location
     * @beta
     */
    getStagePanelDef(location) {
        let panelDef;
        switch (location) {
            case StagePanelLocation_1.StagePanelLocation.Top:
                panelDef = this.topPanel;
                break;
            case StagePanelLocation_1.StagePanelLocation.Left:
                panelDef = this.leftPanel;
                break;
            case StagePanelLocation_1.StagePanelLocation.Right:
                panelDef = this.rightPanel;
                break;
            case StagePanelLocation_1.StagePanelLocation.Bottom:
                panelDef = this.bottomPanel;
                break;
        }
        // Panels can be undefined in a Frontstage
        return panelDef;
    }
    /** Gets a list of [[StagePanelDef]]s
     * @beta
     */
    get panelDefs() {
        const locations = [
            StagePanelLocation_1.StagePanelLocation.Left,
            StagePanelLocation_1.StagePanelLocation.Right,
            StagePanelLocation_1.StagePanelLocation.Top,
            StagePanelLocation_1.StagePanelLocation.Bottom,
        ];
        const panels = [];
        locations.forEach((location) => {
            const panel = this.getStagePanelDef(location);
            if (panel)
                panels.push(panel);
        });
        return panels;
    }
    /** Finds a [[WidgetDef]] based on a given id */
    findWidgetDef(id) {
        for (const widgetDef of this.widgetDefs) {
            if (widgetDef.id === id)
                return widgetDef;
        }
        return undefined;
    }
    /** Gets the list of [[WidgetControl]]s */
    get _widgetControls() {
        const widgetControls = new Array();
        this.panelDefs.forEach((panelDef) => {
            panelDef.widgetDefs.forEach((widgetDef) => {
                const widgetControl = widgetDef.widgetControl;
                // istanbul ignore if
                if (widgetControl)
                    widgetControls.push(widgetControl);
            });
        });
        return widgetControls;
    }
    addFloatingContentControl(contentControl) {
        // istanbul ignore next
        if (!contentControl)
            return;
        if (!this._floatingContentControls)
            this._floatingContentControls = new Array();
        this._floatingContentControls.push(contentControl);
        UiFramework_1.UiFramework.content.onAvailableContentChangedEvent.emit({
            contentId: contentControl.uniqueId,
        });
    }
    dropFloatingContentControl(contentControl) {
        // istanbul ignore next
        if (!contentControl || !this._floatingContentControls)
            return;
        const index = this._floatingContentControls.indexOf(contentControl);
        // istanbul ignore else
        if (index > -1) {
            this._floatingContentControls.splice(index, 1);
            UiFramework_1.UiFramework.content.onAvailableContentChangedEvent.emit({
                contentId: contentControl.uniqueId,
            });
        }
    }
    /** Gets the list of [[ContentControl]]s */
    get contentControls() {
        const contentControls = new Array();
        // istanbul ignore else
        if (this.contentGroup) {
            contentControls.push(...this.contentGroup.getContentControls());
        }
        if (this._floatingContentControls) {
            contentControls.push(...this._floatingContentControls);
        }
        return contentControls;
    }
    /** Initializes a FrontstageDef from FrontstageConfig.
     * @internal
     */
    async initializeFromConfig(config) {
        this._id = config.id;
        this._initialConfig = config;
        if (config.contentGroup instanceof ContentGroup_1.ContentGroupProvider) {
            this._contentGroupProvider = config.contentGroup;
        }
        else {
            this._contentGroup = config.contentGroup;
        }
        this._usage = config.usage;
        this._version = config.version;
        this._toolSettings = createWidgetDef(config.toolSettings, WidgetDef_1.WidgetType.ToolSettings);
        this._statusBar = createWidgetDef(config.statusBar, WidgetDef_1.WidgetType.StatusBar);
        this._contentManipulation = createWidgetDef(config.contentManipulation, WidgetDef_1.WidgetType.Tool);
        this._viewNavigation = createWidgetDef(config.viewNavigation, WidgetDef_1.WidgetType.Navigation);
        this._topPanel = createStagePanelDef(config, StagePanelLocation_1.StagePanelLocation.Top);
        this._leftPanel = createStagePanelDef(config, StagePanelLocation_1.StagePanelLocation.Left);
        this._rightPanel = createStagePanelDef(config, StagePanelLocation_1.StagePanelLocation.Right);
        this._bottomPanel = createStagePanelDef(config, StagePanelLocation_1.StagePanelLocation.Bottom);
    }
    /** @internal */
    updateWidgetDefs() {
        // Tracks provided widgets to prevent duplicates.
        const allStageWidgetDefs = [];
        // Process panels before zones so extension can explicitly target a widget for a StagePanelSection
        this.panelDefs.forEach((stagePanelDef) => {
            stagePanelDef.updateDynamicWidgetDefs(this.id, this.usage, stagePanelDef.location, undefined, allStageWidgetDefs);
        });
        InternalFrontstageManager_1.InternalFrontstageManager.onFrontstageWidgetsChangedEvent.emit({
            frontstageDef: this,
        });
    }
    /** Restores frontstage layout to initial configuration.
     * @beta
     */
    restoreLayout() {
        for (const panelDef of this.panelDefs) {
            panelDef.size = panelDef.defaultSize;
            panelDef.panelState = panelDef.defaultState;
        }
        for (const widgetDef of this.widgetDefs) {
            widgetDef.setWidgetState(widgetDef.defaultState);
        }
        InternalFrontstageManager_1.InternalFrontstageManager.onFrontstageRestoreLayoutEvent.emit({
            frontstageDef: this,
        });
    }
    /** Used only in UI 2.0 to determine WidgetState from NinezoneState
     *  @internal
     */
    getWidgetCurrentState(widgetDef) {
        const state = this.nineZoneState;
        // istanbul ignore else
        if (state) {
            const toolSettingsTabId = state.toolSettings?.tabId;
            if (toolSettingsTabId === widgetDef.id &&
                state.toolSettings?.type === "docked") {
                return WidgetState_1.WidgetState.Open;
            }
            const location = (0, appui_layout_react_1.getTabLocation)(state, widgetDef.id);
            // istanbul ignore next
            if (!location)
                return WidgetState_1.WidgetState.Hidden;
            if ((0, appui_layout_react_1.isFloatingTabLocation)(location)) {
                return WidgetState_1.WidgetState.Floating;
            }
            let collapsedPanel = false;
            // istanbul ignore else
            if ("side" in location) {
                const panel = state.panels[location.side];
                collapsedPanel =
                    panel.collapsed || undefined === panel.size || 0 === panel.size;
            }
            const widgetContainer = state.widgets[location.widgetId];
            if (widgetDef.id === widgetContainer.activeTabId && !collapsedPanel)
                return WidgetState_1.WidgetState.Open;
            else
                return WidgetState_1.WidgetState.Closed;
        }
        return widgetDef.defaultState;
    }
    isPopoutWidget(widgetId) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = (0, appui_layout_react_1.getTabLocation)(this.nineZoneState, widgetId);
            // istanbul ignore else
            if (location)
                return (0, appui_layout_react_1.isPopoutTabLocation)(location);
        }
        return false;
    }
    isFloatingWidget(widgetId) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = (0, appui_layout_react_1.getTabLocation)(this.nineZoneState, widgetId);
            // istanbul ignore else
            if (location)
                return (0, appui_layout_react_1.isFloatingTabLocation)(location);
        }
        return false;
    }
    /** Create a new floating panel that contains the widget specified by its Id. Supported only when in
     *  UI 2.0 or higher.
     * @param widgetId case sensitive Widget Id
     * @param position Position of top left corner of floating panel in pixels. If undefined {x:50, y:100} is used.
     * @param size defines the width and height of the floating panel. If undefined and widget has been floated before
     * the previous size is used, else {height:400, width:400} is used.
     * @beta
     */
    floatWidget(widgetId, position, size) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const widgetDef = this.findWidgetDef(widgetId);
        if (!widgetDef)
            return;
        this.dispatch({
            type: "WIDGET_TAB_FLOAT",
            id: widgetId,
            position,
            size,
        });
    }
    /** Check widget and panel state to determine whether the widget is currently displayed
     * @param widgetId case-sensitive Widget Id
     * @public
     */
    isWidgetDisplayed(widgetId) {
        if (!this.nineZoneState)
            return false;
        const tabLocation = (0, appui_layout_react_1.getTabLocation)(this.nineZoneState, widgetId);
        if (!tabLocation)
            return false;
        if ((0, appui_layout_react_1.isPanelTabLocation)(tabLocation)) {
            const panel = this.nineZoneState.panels[tabLocation.side];
            const widgetDef = this.findWidgetDef(widgetId);
            const isVisible = !!widgetDef && widgetDef.state === WidgetState_1.WidgetState.Open && !panel.collapsed;
            return isVisible;
        }
        return true;
    }
    /** Opens window for specified PopoutWidget container. Used to reopen popout when running in Electron.
     * @internal
     */
    openPopoutWidgetContainer(widgetContainerId) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const location = (0, appui_layout_react_1.getWidgetLocation)(state, widgetContainerId);
        if (!location)
            return;
        if (!(0, appui_layout_react_1.isPopoutWidgetLocation)(location))
            return;
        const widget = state.widgets[widgetContainerId];
        // Popout widget should only contain a single tab.
        if (widget.tabs.length !== 1)
            return;
        const tabId = widget.tabs[0];
        const widgetDef = this.findWidgetDef(tabId);
        if (!widgetDef)
            return;
        const popoutContent = (React.createElement(PopoutWidget_1.PopoutWidget, { widgetContainerId: widgetContainerId, widgetDef: widgetDef }));
        const popoutWidget = state.popoutWidgets.byId[location.popoutWidgetId];
        const bounds = core_react_1.Rectangle.create(popoutWidget.bounds);
        const position = {
            width: bounds.getWidth(),
            height: bounds.getHeight(),
            left: bounds.left,
            top: bounds.top,
        };
        UiFramework_1.UiFramework.childWindows.open(widgetContainerId, widgetDef.label, popoutContent, position, UiFramework_1.UiFramework.useDefaultPopoutUrl);
    }
    /** Create a new popout/child window that contains the widget specified by its Id. Supported only when in
     *  UI 2.0 or higher.
     * @param widgetId case sensitive Widget Id
     * @param position Position of top left corner of floating panel in pixels. If undefined {x:0, y:0} is used.
     * @param size defines the width and height of the floating panel. If undefined and widget has been floated before
     * the previous size is used, else {height:800, width:600} is used.
     * @beta
     */
    popoutWidget(widgetId, position, size) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const widgetDef = this.findWidgetDef(widgetId);
        if (!widgetDef)
            return;
        this.dispatch({
            type: "WIDGET_TAB_POPOUT",
            id: widgetId,
            position,
            size,
        });
    }
    get isStageClosing() {
        return this._isStageClosing;
    }
    get isApplicationClosing() {
        return this._isApplicationClosing;
    }
    get isReady() {
        return this._isReady;
    }
    /** @internal */
    saveChildWindowSizeAndPosition(childWindowId, childWindow) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const location = (0, appui_layout_react_1.getWidgetLocation)(state, childWindowId);
        if (!location || !(0, appui_layout_react_1.isPopoutWidgetLocation)(location))
            return;
        const widget = state.widgets[location.popoutWidgetId];
        const tabId = widget.tabs[0];
        const widgetDef = this.findWidgetDef(tabId);
        if (!widgetDef)
            return;
        const adjustmentWidth = core_bentley_1.ProcessDetector.isElectronAppFrontend ? 16 : 0;
        const adjustmentHeight = core_bentley_1.ProcessDetector.isElectronAppFrontend ? 39 : 0;
        const width = childWindow.innerWidth + adjustmentWidth;
        const height = childWindow.innerHeight + adjustmentHeight;
        const bounds = core_react_1.Rectangle.createFromSize({ width, height }).offset({
            x: childWindow.screenX,
            y: childWindow.screenY,
        });
        this.dispatch({
            type: "WIDGET_TAB_SET_POPOUT_BOUNDS",
            id: tabId,
            bounds,
        });
    }
    /** @internal */
    dockWidgetContainerByContainerId(widgetContainerId) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const location = (0, appui_layout_react_1.getWidgetLocation)(state, widgetContainerId);
        if (!location)
            return;
        if ((0, appui_layout_react_1.isFloatingWidgetLocation)(location)) {
            this.dispatch({
                type: "FLOATING_WIDGET_SEND_BACK",
                id: location.floatingWidgetId,
            });
            return;
        }
        if ((0, appui_layout_react_1.isPopoutWidgetLocation)(location)) {
            this.dispatch({
                type: "POPOUT_WIDGET_SEND_BACK",
                id: location.popoutWidgetId,
            });
            return;
        }
    }
    /** Finds the container with the specified widget and re-docks all widgets
     * back to the panel zone location that was used when the floating container
     * was generated. Supported only when in UI 2.0 or higher.
     * @param widgetId  case sensitive Widget Id.
     * @beta
     */
    dockWidgetContainer(widgetId) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const location = (0, appui_layout_react_1.getTabLocation)(state, widgetId);
        if (!location)
            return;
        this.dockWidgetContainerByContainerId(location.widgetId);
    }
    setFloatingWidgetContainerBounds(floatingWidgetId, bounds) {
        if (!this.nineZoneState ||
            !(floatingWidgetId in this.nineZoneState.floatingWidgets.byId))
            return false;
        this.dispatch({
            type: "FLOATING_WIDGET_SET_BOUNDS",
            id: floatingWidgetId,
            bounds,
        });
        this.dispatch({
            type: "FLOATING_WIDGET_SET_USER_SIZED",
            id: floatingWidgetId,
            userSized: true,
        });
        return true;
    }
    getFloatingWidgetContainerIds() {
        if (!this.nineZoneState)
            return [];
        return [...this.nineZoneState.floatingWidgets.allIds];
    }
    getFloatingWidgetContainerIdByWidgetId(widgetId) {
        if (!this.nineZoneState)
            return undefined;
        const location = (0, appui_layout_react_1.getTabLocation)(this.nineZoneState, widgetId);
        // istanbul ignore else
        if (location && (0, appui_layout_react_1.isFloatingTabLocation)(location)) {
            return location.floatingWidgetId;
        }
        // istanbul ignore next
        return undefined;
    }
    getFloatingWidgetContainerBounds(floatingWidgetId) {
        if (!floatingWidgetId)
            return undefined;
        // istanbul ignore else
        if (this.nineZoneState &&
            floatingWidgetId in this.nineZoneState.floatingWidgets.byId) {
            const foundWidget = document.querySelector(`div.nz-widget-floatingWidget[data-widget-id='${floatingWidgetId}']`);
            // istanbul ignore next
            if (foundWidget) {
                const domRect = foundWidget.getBoundingClientRect();
                return {
                    left: domRect.left,
                    right: domRect.right,
                    top: domRect.top,
                    bottom: domRect.bottom,
                };
            }
            return this.nineZoneState.floatingWidgets.byId[floatingWidgetId].bounds;
        }
        return undefined;
    }
    *_widgetDefs() {
        for (const panelDef of this.panelDefs) {
            for (const widgetDef of panelDef.widgetDefs) {
                yield widgetDef;
            }
        }
        if (this.toolSettings)
            yield this.toolSettings;
        if (this.statusBar)
            yield this.statusBar;
        return undefined;
    }
    /** Iterable of all widget definitions in a frontstage.
     * @internal
     */
    get widgetDefs() {
        const defs = this._widgetDefs();
        return {
            [Symbol.iterator]() {
                return defs;
            },
        };
    }
}
exports.FrontstageDef = FrontstageDef;
function createWidgetDef(config, type) {
    if (!config)
        return undefined;
    const widgetDef = WidgetDef_1.WidgetDef.create(config, type);
    return widgetDef;
}
function createStagePanelDef(frontstageConfig, location) {
    const config = getStagePanel(location, frontstageConfig);
    const panelDef = new StagePanelDef_1.StagePanelDef();
    panelDef.initializeFromConfig(config, location);
    return panelDef;
}
function getStagePanel(location, config) {
    switch (location) {
        case StagePanelLocation_1.StagePanelLocation.Top:
            return config.topPanel;
        case StagePanelLocation_1.StagePanelLocation.Left:
            return config.leftPanel;
        case StagePanelLocation_1.StagePanelLocation.Right:
            return config.rightPanel;
        case StagePanelLocation_1.StagePanelLocation.Bottom:
            return config.bottomPanel;
    }
}
/** Hook that returns active frontstage id.
 * @public
 */
const useActiveFrontstageId = () => {
    const def = useActiveFrontstageDef();
    const id = React.useMemo(() => (def ? /* istanbul ignore next */ def.id : ""), [def]);
    return id;
};
exports.useActiveFrontstageId = useActiveFrontstageId;
/** Hook to return the active Frontstage def.
 *  @public
 */
function useActiveFrontstageDef() {
    const [def, setDef] = React.useState(UiFramework_1.UiFramework.frontstages.activeFrontstageDef);
    React.useEffect(() => {
        return UiFramework_1.UiFramework.frontstages.onFrontstageActivatedEvent.addListener((args) => {
            setDef(args.activatedFrontstageDef);
        });
    }, []);
    return def;
}
exports.useActiveFrontstageDef = useActiveFrontstageDef;
/** Hook that returns the widgetDef for a specific widgetId within the active frontstage.
 * @public
 */
function useSpecificWidgetDef(widgetId) {
    const frontstageDef = useActiveFrontstageDef();
    const [widgetDef, setWidgetDef] = React.useState(() => frontstageDef?.findWidgetDef(widgetId));
    React.useEffect(() => {
        setWidgetDef(frontstageDef?.findWidgetDef(widgetId));
        return InternalFrontstageManager_1.InternalFrontstageManager.onFrontstageWidgetsChangedEvent.addListener((args) => {
            if (args.frontstageDef !== frontstageDef)
                return;
            setWidgetDef(frontstageDef.findWidgetDef(widgetId));
        });
    }, [frontstageDef, widgetId]);
    return widgetDef;
}
exports.useSpecificWidgetDef = useSpecificWidgetDef;
//# sourceMappingURL=FrontstageDef.js.map