/*---------------------------------------------------------------------------------------------
 * 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
 */
// cSpell:ignore popout
import { UiError } from "@itwin/appui-abstract";
import { BentleyStatus } from "@itwin/core-bentley";
import { IModelApp, NotifyMessageDetails, OutputMessagePriority, OutputMessageType, } from "@itwin/core-frontend";
import { Rectangle } from "@itwin/core-react";
import * as React from "react";
import { UiFramework } from "../UiFramework";
import { ChildWindowWidget } from "../childwindow/ChildWindowWidget";
import { TimeTracker } from "../configurableui/TimeTracker";
import { ContentGroupProvider } from "../content/ContentGroup";
import { InternalContentDialogManager } from "../dialog/InternalContentDialogManager";
import { NineZoneStateReducer } from "../layout/state/NineZoneStateReducer";
import { getTabLocation, isFloatingTabLocation, isPanelTabLocation, isPopoutTabLocation, } from "../layout/state/TabLocation";
import { getWidgetLocation, isFloatingWidgetLocation, isPopoutWidgetLocation, } from "../layout/state/WidgetLocation";
import { panelSides } from "../layout/widget-panels/Panel";
import { StagePanelDef } from "../stagepanels/StagePanelDef";
import { StagePanelLocation } from "../stagepanels/StagePanelLocation";
import { StagePanelState } from "../stagepanels/StagePanelState";
import { getWidgetState, WidgetDef, WidgetType } from "../widgets/WidgetDef";
import { WidgetState } from "../widgets/WidgetState";
import { InternalFrontstageManager } from "./InternalFrontstageManager";
import { StageUsage } from "./StageUsage";
/** FrontstageDef class provides an API for a Frontstage.
 * @public
 */
export class FrontstageDef {
    constructor() {
        this._id = "";
        this._isStageClosing = false;
        this._isReady = false;
        this._isApplicationClosing = false;
        this._version = 0;
        this._timeTracker = new TimeTracker();
        this._batching = false;
    }
    get id() {
        return this._id;
    }
    get usage() {
        return this._usage !== undefined ? this._usage : 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;
    }
    /** @deprecated in 4.15.0. Use {@link FrontstageDef.id} to look up a frontstage. */
    // eslint-disable-next-line deprecation/deprecation
    get frontstageProvider() {
        return this._frontstageProvider;
    }
    toStagePanelLocation(side) {
        switch (side) {
            case "bottom":
                return StagePanelLocation.Bottom;
            case "left":
                return StagePanelLocation.Left;
            case "right":
                return StagePanelLocation.Right;
            case "top":
                return StagePanelLocation.Top;
        }
    }
    populateStateMaps(nineZone) {
        const panelMap = new Map();
        const widgetMap = new Map();
        for (const panelSide of panelSides) {
            const panel = nineZone.panels[panelSide];
            const location = this.toStagePanelLocation(panelSide);
            const panelDef = this.getStagePanelDef(location);
            if (!panelDef)
                continue;
            const panelState = panel.collapsed
                ? StagePanelState.Minimized
                : StagePanelState.Open;
            panelMap.set(panelDef, panelState);
        }
        for (const widgetDef of this.widgetDefs) {
            const widgetState = getWidgetState(widgetDef, nineZone);
            widgetMap.set(widgetDef, widgetState);
        }
        return { panelMap, widgetMap };
    }
    triggerStateChangeEvents(oldState) {
        const newState = this.nineZoneState;
        if (!newState)
            return;
        if (this._isStageClosing || this._isApplicationClosing)
            return;
        const { panelMap, widgetMap } = this.populateStateMaps(oldState);
        const { panelMap: newPanelMap, widgetMap: newWidgetMap } = this.populateStateMaps(newState);
        // Now walk and trigger set state events
        newWidgetMap.forEach((newWidgetState, widgetDef) => {
            const originalState = widgetMap.get(widgetDef);
            if (originalState === newWidgetState)
                return;
            widgetDef.handleWidgetStateChanged(newWidgetState);
        });
        newPanelMap.forEach((newPanelState, panelDef) => {
            const originalState = panelMap.get(panelDef);
            if (originalState === newPanelState)
                return;
            panelDef.handlePanelStateChanged(newPanelState);
        });
        for (const panelSide of 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.childWindows.close(popoutId, true);
        }
        for (const popoutId of popoutsToOpen) {
            const result = this.openPopoutWidgetContainer(popoutId, oldState);
            if (!result)
                return result;
        }
        return true;
    }
    /** @internal */
    get nineZoneState() {
        return this._nineZoneState;
    }
    set nineZoneState(state) {
        if (this._nineZoneState === state)
            return;
        const oldState = this._nineZoneState;
        this._nineZoneState = state;
        if (this._batching)
            return;
        const popoutResult = this.handlePopouts(oldState);
        if (oldState) {
            this.triggerStateChangeEvents(oldState);
        }
        const isClosing = this._isStageClosing || this._isApplicationClosing;
        if (isClosing && !this.isReady)
            return;
        if (popoutResult === false) {
            IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, UiFramework.translate("widget.errorMessage.widgetPopoutFail"), undefined, OutputMessageType.Toast));
            return;
        }
        InternalFrontstageManager.onFrontstageNineZoneStateChangedEvent.emit({
            frontstageDef: this,
            state,
        });
    }
    /** @internal */
    get dispatch() {
        return (this._dispatch ??= (action) => {
            const state = this.nineZoneState;
            if (!state)
                return;
            this.nineZoneState = NineZoneStateReducer(state, action);
        });
    }
    set dispatch(dispatch) {
        this._dispatch = dispatch;
    }
    /** Dispatch multiple actions inside `fn`, but trigger events once.
     * @internal
     */
    batch(fn) {
        const initialState = this._nineZoneState;
        this._batching = true;
        fn();
        this._batching = false;
        const updatedState = this._nineZoneState;
        this._nineZoneState = initialState;
        this.nineZoneState = updatedState;
    }
    /** @internal */
    get timeTracker() {
        return this._timeTracker;
    }
    /** Creates a [[FrontstageDef]] and initializes it. */
    static async create(
    // eslint-disable-next-line deprecation/deprecation
    providerOrFrontstage) {
        const def = new FrontstageDef();
        let config;
        if ("frontstageConfig" in providerOrFrontstage) {
            def._frontstageProvider = providerOrFrontstage;
            config = providerOrFrontstage.frontstageConfig();
        }
        else {
            config = providerOrFrontstage;
        }
        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);
        }
        if (!this._contentGroup)
            // eslint-disable-next-line deprecation/deprecation
            throw new UiError(UiFramework.loggerCategory(this), `onActivated: Content Group not defined`);
        this._contentLayoutDef = UiFramework.content.layouts.getForGroup(this._contentGroup);
        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();
        }
        if (this.contentGroup)
            this.contentGroup.onFrontstageDeactivated();
        if (this.contentGroupProvider)
            await this.contentGroupProvider.onFrontstageDeactivated();
        this._timeTracker.stopTiming();
        this._isStageClosing = true; // this keeps widgets in child windows from automatically re-docking
        UiFramework.childWindows.closeAll();
        if (this._floatingContentControls) {
            InternalContentDialogManager.closeAll();
            this._floatingContentControls = undefined;
        }
        if (this._toolAdminDefaultToolId) {
            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);
        });
        if (this.contentLayoutDef) {
            const usedContentIndexes = this.contentLayoutDef.getUsedContentIndexes();
            this.contentControls.forEach((control, index) => {
                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();
        }
        if (this.contentGroup)
            this.contentGroup.onFrontstageReady();
        if (IModelApp.toolAdmin && IModelApp.viewManager && this._initialConfig) {
            const defaultTool = this._initialConfig.defaultTool;
            if (defaultTool) {
                this._toolAdminDefaultToolId = IModelApp.toolAdmin.defaultToolId;
                IModelApp.toolAdmin.defaultToolId = defaultTool;
                void IModelApp.tools.run(defaultTool);
            }
            else {
                void 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;
        if (!contentControl && this.contentControls.length >= 0) {
            contentControl = this.contentControls[0];
        }
        if (contentControl) {
            UiFramework.content.setActive(contentControl.reactNode, true);
            if (contentControl.viewport) {
                const status = await IModelApp.viewManager.setSelectedView(contentControl.viewport);
                activated = status === BentleyStatus.SUCCESS;
            }
        }
        return activated;
    }
    /** Sets the active view content control */
    setActiveView(newContent, oldContent) {
        if (oldContent)
            oldContent.onDeactivated();
        newContent.onActivated();
        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);
        if (contentControl) {
            UiFramework.content.setActive(contentControl.reactNode, true);
            return true;
        }
        return false;
    }
    /** Gets a [[StagePanelDef]] based on a given panel location
     * @beta
     */
    getStagePanelDef(location) {
        let panelDef;
        switch (location) {
            case StagePanelLocation.Top:
                panelDef = this.topPanel;
                break;
            case StagePanelLocation.Left:
                panelDef = this.leftPanel;
                break;
            case StagePanelLocation.Right:
                panelDef = this.rightPanel;
                break;
            case 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.Left,
            StagePanelLocation.Right,
            StagePanelLocation.Top,
            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;
                if (widgetControl)
                    widgetControls.push(widgetControl);
            });
        });
        return widgetControls;
    }
    addFloatingContentControl(contentControl) {
        if (!contentControl)
            return;
        if (!this._floatingContentControls)
            this._floatingContentControls = new Array();
        this._floatingContentControls.push(contentControl);
        UiFramework.content.onAvailableContentChangedEvent.emit({
            contentId: contentControl.uniqueId,
        });
    }
    dropFloatingContentControl(contentControl) {
        if (!contentControl || !this._floatingContentControls)
            return;
        const index = this._floatingContentControls.indexOf(contentControl);
        if (index > -1) {
            this._floatingContentControls.splice(index, 1);
            UiFramework.content.onAvailableContentChangedEvent.emit({
                contentId: contentControl.uniqueId,
            });
        }
    }
    /** Gets the list of [[ContentControl]]s */
    get contentControls() {
        const contentControls = new Array();
        if (this.contentGroup) {
            contentControls.push(...this.contentGroup.getContentControls());
        }
        if (this._floatingContentControls) {
            contentControls.push(...this._floatingContentControls);
        }
        return contentControls;
    }
    /** Initializes a FrontstageDef from frontstage.
     * @internal
     */
    async initializeFromConfig(config) {
        this._id = config.id;
        this._initialConfig = config;
        if (config.contentGroup instanceof ContentGroupProvider) {
            this._contentGroupProvider = config.contentGroup;
        }
        else {
            this._contentGroup = config.contentGroup;
        }
        this._usage = config.usage;
        this._version = config.version;
        this._toolSettings = createWidgetDef(config.toolSettings, WidgetType.ToolSettings);
        this._statusBar = createWidgetDef(config.statusBar, WidgetType.StatusBar);
        this._contentManipulation = createWidgetDef(config.contentManipulation, WidgetType.Tool);
        this._viewNavigation = createWidgetDef(config.viewNavigation, WidgetType.Navigation);
        this._topPanel = createStagePanelDef(config, StagePanelLocation.Top);
        this._leftPanel = createStagePanelDef(config, StagePanelLocation.Left);
        this._rightPanel = createStagePanelDef(config, StagePanelLocation.Right);
        this._bottomPanel = createStagePanelDef(config, 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.onFrontstageWidgetsChangedEvent.emit({
            frontstageDef: this,
        });
    }
    /** Restores frontstage layout to initial configuration.
     * @beta
     */
    restoreLayout() {
        for (const panelDef of this.panelDefs) {
            // eslint-disable-next-line deprecation/deprecation
            panelDef.size = panelDef.initialConfig?.size;
            panelDef.sizeSpec = panelDef.initialConfig?.sizeSpec;
            panelDef.panelState = panelDef.defaultState;
            panelDef.pinned = panelDef.initialConfig?.pinned ?? true;
        }
        for (const widgetDef of this.widgetDefs) {
            widgetDef.setWidgetState(widgetDef.defaultState);
        }
        InternalFrontstageManager.onFrontstageRestoreLayoutEvent.emit({
            frontstageDef: this,
        });
    }
    isPopoutWidget(widgetId) {
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetId);
            if (location)
                return isPopoutTabLocation(location);
        }
        return false;
    }
    isFloatingWidget(widgetId) {
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetId);
            if (location)
                return isFloatingTabLocation(location);
        }
        return false;
    }
    /** Create a new floating panel that contains the widget specified by its Id.
     * @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 = getTabLocation(this.nineZoneState, widgetId);
        if (!tabLocation)
            return false;
        if (isPanelTabLocation(tabLocation)) {
            const panel = this.nineZoneState.panels[tabLocation.side];
            const widgetDef = this.findWidgetDef(widgetId);
            const isVisible = !!widgetDef && widgetDef.state === 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, oldState) {
        const state = this.nineZoneState;
        if (!state)
            return false;
        const location = getWidgetLocation(state, widgetContainerId);
        if (!location)
            return false;
        if (!isPopoutWidgetLocation(location))
            return false;
        const widget = state.widgets[widgetContainerId];
        // Popout widget should only contain a single tab.
        if (widget.tabs.length !== 1)
            return false;
        const tabId = widget.tabs[0];
        const widgetDef = this.findWidgetDef(tabId);
        if (!widgetDef)
            return false;
        const popoutContent = (React.createElement(ChildWindowWidget, { widgetContainerId: widgetContainerId, widgetDef: widgetDef }));
        const popoutWidget = state.popoutWidgets.byId[location.popoutWidgetId];
        const bounds = Rectangle.create(popoutWidget.bounds);
        const position = {
            width: bounds.getWidth(),
            height: bounds.getHeight(),
            left: bounds.left,
            top: bounds.top,
        };
        const result = UiFramework.childWindows.open(widgetContainerId, widgetDef.label, popoutContent, position, UiFramework.useDefaultPopoutUrl);
        if (!result && oldState) {
            this.nineZoneState = oldState;
            return false;
        }
        return true;
    }
    /** Create a new popout/child window that contains the widget specified by its Id.
     * @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 = getWidgetLocation(state, childWindowId);
        if (!location || !isPopoutWidgetLocation(location))
            return;
        const widget = state.widgets[location.popoutWidgetId];
        const tabId = widget.tabs[0];
        const widgetDef = this.findWidgetDef(tabId);
        if (!widgetDef)
            return;
        let height = childWindow.innerHeight;
        if (childWindow.deltaHeight) {
            height += childWindow.deltaHeight;
            if (height < 1)
                height = 100;
        }
        let width = childWindow.innerWidth;
        if (childWindow.deltaWidth) {
            width += childWindow.deltaWidth;
            if (width < 1)
                width = 100;
        }
        let left = childWindow.screenLeft;
        if (childWindow.deltaLeft)
            left += childWindow.deltaLeft;
        let top = childWindow.screenTop;
        if (childWindow.deltaTop)
            top += childWindow.deltaTop;
        const bounds = Rectangle.createFromSize({ width, height }).offset({
            x: left,
            y: top,
        });
        this.dispatch({
            type: "WIDGET_TAB_SET_POPOUT_BOUNDS",
            id: tabId,
            bounds,
        });
    }
    /** @internal */
    dockWidgetContainerByContainerId(widgetContainerId) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const location = getWidgetLocation(state, widgetContainerId);
        if (!location)
            return;
        if (isFloatingWidgetLocation(location)) {
            this.dispatch({
                type: "FLOATING_WIDGET_SEND_BACK",
                id: location.floatingWidgetId,
            });
            return;
        }
        if (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.
     * @param widgetId  case sensitive Widget Id.
     * @beta
     */
    dockWidgetContainer(widgetId) {
        const state = this.nineZoneState;
        if (!state)
            return;
        const location = 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 = getTabLocation(this.nineZoneState, widgetId);
        if (location && isFloatingTabLocation(location)) {
            return location.floatingWidgetId;
        }
        return undefined;
    }
    getFloatingWidgetContainerBounds(floatingWidgetId) {
        if (!floatingWidgetId)
            return undefined;
        if (this.nineZoneState &&
            floatingWidgetId in this.nineZoneState.floatingWidgets.byId) {
            const foundWidget = document.querySelector(`div.nz-widget-floatingWidget[data-widget-id='${floatingWidgetId}']`);
            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;
            },
        };
    }
}
function createWidgetDef(config, type) {
    if (!config)
        return undefined;
    const widgetDef = WidgetDef.create(config, type);
    return widgetDef;
}
function createStagePanelDef(frontstage, location) {
    const config = getStagePanel(location, frontstage);
    const panelDef = new StagePanelDef();
    panelDef.initializeFromConfig(config, location);
    return panelDef;
}
function getStagePanel(location, config) {
    switch (location) {
        case StagePanelLocation.Top:
            return config.topPanel;
        case StagePanelLocation.Left:
            return config.leftPanel;
        case StagePanelLocation.Right:
            return config.rightPanel;
        case StagePanelLocation.Bottom:
            return config.bottomPanel;
    }
}
/** Hook that returns active frontstage id.
 * @public
 */
export const useActiveFrontstageId = () => {
    const def = useActiveFrontstageDef();
    const id = React.useMemo(() => (def ? def.id : ""), [def]);
    return id;
};
/** Hook to return the active Frontstage def.
 * @public
 */
export function useActiveFrontstageDef() {
    const [def, setDef] = React.useState(UiFramework.frontstages.activeFrontstageDef);
    React.useEffect(() => {
        return UiFramework.frontstages.onFrontstageActivatedEvent.addListener((args) => {
            setDef(args.activatedFrontstageDef);
        });
    }, []);
    return def;
}
/** Hook that returns the widgetDef for a specific widgetId within the active frontstage.
 * @public
 */
export function useSpecificWidgetDef(widgetId) {
    const frontstageDef = useActiveFrontstageDef();
    const [widgetDef, setWidgetDef] = React.useState(() => frontstageDef?.findWidgetDef(widgetId));
    React.useEffect(() => {
        setWidgetDef(frontstageDef?.findWidgetDef(widgetId));
        return InternalFrontstageManager.onFrontstageWidgetsChangedEvent.addListener((args) => {
            if (args.frontstageDef !== frontstageDef)
                return;
            setWidgetDef(frontstageDef.findWidgetDef(widgetId));
        });
    }, [frontstageDef, widgetId]);
    return widgetDef;
}
//# sourceMappingURL=FrontstageDef.js.map