/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/* eslint-disable deprecation/deprecation */
/** @packageDocumentation
 * @module Frontstage
 */
// cSpell:ignore popout
import * as React from "react";
import { IModelApp } from "@itwin/core-frontend";
import { StagePanelLocation, StageUsage, UiError, WidgetState } from "@itwin/appui-abstract";
import { Rectangle } from "@itwin/core-react";
import { dockWidgetContainer, floatWidget, getTabLocation, getWidgetLocation, isFloatingTabLocation, isPanelTabLocation, isPopoutTabLocation, isPopoutWidgetLocation, panelSides, popoutWidgetToChildWindow, } from "@itwin/appui-layout-react";
import { ContentGroupProvider } from "../content/ContentGroup";
import { StagePanelState, toPanelSide } from "../stagepanels/StagePanelDef";
import { UiFramework } from "../UiFramework";
import { Zone, ZoneLocation } from "../zones/Zone";
import { Frontstage } from "./Frontstage";
import { TimeTracker } from "../configurableui/TimeTracker";
import { PopoutWidget } from "../childwindow/PopoutWidget";
import { FrameworkStateReducer } from "../widget-panels/Frontstage";
import { assert, BentleyStatus, ProcessDetector } from "@itwin/core-bentley";
import { Widget } from "../widgets/Widget";
import { StagePanel } from "../stagepanels/StagePanel";
import { InternalFrontstageManager } from "./InternalFrontstageManager";
import { InternalContentDialogManager } from "../dialog/InternalContentDialogManager";
/** FrontstageDef class provides an API for a Frontstage.
 * @public
 */
export class FrontstageDef {
    constructor() {
        this._id = "";
        this._defaultContentId = "";
        this._isInFooterMode = true;
        this._isStageClosing = false;
        this._isReady = false;
        this._isApplicationClosing = false;
        this._version = 0;
        this._timeTracker = new TimeTracker();
    }
    get id() { return this._id; }
    /** @deprecated in 3.6. UI 1.0 will be removed in AppUi 4.0.*/
    get defaultTool() { return this._defaultTool; }
    /** @deprecated in 3.6. UI 1.0 will be removed in AppUi 4.0.*/
    get defaultContentId() { return this._defaultContentId; }
    get isInFooterMode() { return this._isInFooterMode; }
    /** @deprecated in 3.6. UI 1.0 will be removed in AppUi 4.0.*/
    get applicationData() { return this._applicationData; }
    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; }
    /** @deprecated in 3.6. Use [[FrontstageDef.contentManipulation]] instead. */
    get topLeft() { return this._topLeft; }
    /** @deprecated in 3.6. Use [[FrontstageDef.toolSettings]] instead. */
    get topCenter() { return this._topCenter; }
    /** @deprecated in 3.6. Use [[FrontstageDef.viewNavigation]] instead. */
    get topRight() { return this._topRight; }
    /** @deprecated in 3.6. Use [[FrontstageDef.leftPanel]] instead. */
    get centerLeft() { return this._centerLeft; }
    /** @deprecated in 3.6. Use [[FrontstageDef.rightPanel]] instead. */
    get centerRight() { return this._centerRight; }
    /** @deprecated in 3.6. Use [[FrontstageDef.leftPanel]] instead. */
    get bottomLeft() { return this._bottomLeft; }
    /** @deprecated in 3.6. Use [[FrontstageDef.statusBar]] instead. */
    get bottomCenter() { return this._bottomCenter; }
    /** @deprecated in 3.6. Use [[FrontstageDef.rightPanel]] instead. */
    get bottomRight() { return this._bottomRight; }
    /** @beta */
    get toolSettings() { var _a; return (_a = this.topCenter) === null || _a === void 0 ? void 0 : _a.getSingleWidgetDef(); }
    /** @beta */
    get statusBar() { var _a; return (_a = this.bottomCenter) === null || _a === void 0 ? void 0 : _a.getSingleWidgetDef(); }
    /** @beta */
    get contentManipulation() { var _a; return (_a = this.topLeft) === null || _a === void 0 ? void 0 : _a.getSingleWidgetDef(); }
    /** @beta */
    get viewNavigation() { var _a; return (_a = this.topRight) === null || _a === void 0 ? void 0 : _a.getSingleWidgetDef(); }
    /** @beta */
    get topPanel() { return this._topPanel; }
    /** @beta
     * @deprecated in 3.0. Only topPanel is supported in UI 2.0 */
    get topMostPanel() { return this._topMostPanel; }
    /** @beta */
    get leftPanel() { return this._leftPanel; }
    /** @beta */
    get rightPanel() { return this._rightPanel; }
    /** @beta */
    get bottomPanel() { return this._bottomPanel; }
    /** @beta
     * @deprecated in 3.0. Only bottomPanel is supported in UI 2.0  */
    get bottomMostPanel() { return this._bottomMostPanel; }
    get contentLayoutDef() { return this._contentLayoutDef; }
    get contentGroup() { return this._contentGroup; }
    get frontstageProvider() { return this._frontstageProvider; }
    /** @internal */
    get nineZone() { return this._nineZone; } // istanbul ignore next
    set nineZone(props) { this._nineZone = props; }
    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, panelMap, widgetMap) {
        for (const panelSide of panelSides) {
            const panel = nineZone.panels[panelSide];
            const location = this.toStagePanelLocation(panelSide);
            const panelDef = this.getStagePanelDef(location);
            if (panelDef) {
                const panelState = panel.collapsed ? StagePanelState.Minimized : StagePanelState.Open;
                panelMap.set(panelDef, panelState);
            }
            for (const widgetId of panel.widgets) {
                const widget = nineZone.widgets[widgetId];
                // istanbul ignore else
                if (widget) {
                    for (const tabId of widget.tabs) {
                        const widgetDef = this.findWidgetDef(tabId);
                        if (widgetDef) {
                            let widgetState = WidgetState.Open;
                            if (widget.minimized || tabId !== widget.activeTabId)
                                widgetState = WidgetState.Closed;
                            widgetMap.set(widgetDef, widgetState);
                        }
                    }
                }
            }
        }
        // istanbul ignore next
        for (const widgetId of nineZone.floatingWidgets.allIds) {
            const widget = nineZone.widgets[widgetId];
            if (widget) {
                for (const tabId of widget.tabs) {
                    const widgetDef = this.findWidgetDef(tabId);
                    if (widgetDef) {
                        let widgetState = WidgetState.Open;
                        if (widget.minimized || tabId !== widget.activeTabId)
                            widgetState = WidgetState.Closed;
                        widgetMap.set(widgetDef, widgetState);
                    }
                }
            }
        }
    }
    triggerStateChangeEventForWidgetsAndPanels(state) {
        // istanbul ignore else
        if (!(this._isStageClosing || this._isApplicationClosing)) {
            if (state) {
                const originalPanelStateMap = new Map();
                const originalWidgetStateMap = new Map();
                const newPanelStateMap = new Map();
                const newWidgetStateMap = new Map();
                this._nineZoneState && this.populateStateMaps(this._nineZoneState, originalPanelStateMap, originalWidgetStateMap);
                this.populateStateMaps(state, newPanelStateMap, newWidgetStateMap);
                // set internal state value before triggering events
                this._nineZoneState = state;
                // now walk and trigger set state events
                newWidgetStateMap.forEach((stateValue, widgetDef) => {
                    const originalState = originalWidgetStateMap.get(widgetDef);
                    if (originalState !== stateValue) {
                        UiFramework.frontstages.onWidgetStateChangedEvent.emit({ widgetDef, widgetState: stateValue });
                        widgetDef.onWidgetStateChanged();
                    }
                });
                newPanelStateMap.forEach((stateValue, panelDef) => {
                    const originalState = originalPanelStateMap.get(panelDef);
                    if (originalState !== stateValue) {
                        UiFramework.frontstages.onPanelStateChangedEvent.emit({
                            panelDef,
                            panelState: stateValue,
                        });
                    }
                });
            }
            else {
                this._nineZoneState = state;
            }
        }
    }
    /** @internal */
    get nineZoneState() { return this._nineZoneState; }
    set nineZoneState(state) {
        if (this._nineZoneState === state)
            return;
        if ("1" === UiFramework.uiVersion || !this._nineZoneState) {
            this._nineZoneState = state;
        }
        else {
            this.triggerStateChangeEventForWidgetsAndPanels(state);
        }
        // istanbul ignore next - don't trigger any side effects until stage "isReady"
        if (!(this._isStageClosing || this._isApplicationClosing) || this.isReady) {
            InternalFrontstageManager.onFrontstageNineZoneStateChangedEvent.emit({
                frontstageDef: this,
                state,
            });
        }
    }
    /** @internal */
    get savedWidgetDefs() { return this._savedWidgetDefs; }
    set savedWidgetDefs(widgets) {
        this._savedWidgetDefs = widgets;
    }
    /** @internal */
    get timeTracker() { return this._timeTracker; }
    /** Created a [[FrontstageDef]] and initialize it */
    static async create(provider) {
        const def = new FrontstageDef();
        def._frontstageProvider = provider;
        if (provider.frontstageConfig) {
            const config = provider.frontstageConfig();
            const props = toFrontstageProps(config);
            await def.initializeFromProps(props);
        }
        else {
            await def.initializeFromProps(provider.frontstage.props);
        }
        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._initialProps) {
            if (provider.contentGroup) {
                const config = toFrontstageConfig(this._initialProps);
                this._contentGroup = await provider.contentGroup(config);
            }
            else {
                this._contentGroup = await provider.provideContentGroup(this._initialProps);
            }
        }
        // istanbul ignore next
        if (!this._contentGroup)
            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();
        }
        // 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.childWindows.closeAll();
        if (this._floatingContentControls) {
            InternalContentDialogManager.closeAll();
            this._floatingContentControls = 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();
        this._onFrontstageReady();
    }
    /** Starts the default tool for the Frontstage.
     * @deprecated in 3.6. UI 1.0 will be removed in AppUi 4.0.
     */
    startDefaultTool() {
        // Start the default tool
        // istanbul ignore next
        if (IModelApp.toolAdmin && IModelApp.viewManager) {
            if (this.defaultTool) {
                IModelApp.toolAdmin.defaultToolId = this.defaultTool.toolId;
                this.defaultTool.execute();
            }
            else {
                IModelApp.toolAdmin.startDefaultTool(); // eslint-disable-line @typescript-eslint/no-floating-promises
            }
        }
    }
    /** 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 (this.contentGroup && this.defaultContentId) {
            contentControl = this.contentGroup.getContentControlById(this.defaultContentId);
        }
        // istanbul ignore else
        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);
        // istanbul ignore else
        if (contentControl) {
            UiFramework.content.setActive(contentControl.reactNode, true);
            return true;
        }
        // istanbul ignore next
        return false;
    }
    /** Gets a [[ZoneDef]] based on a given zone id.
     * @deprecated in 3.6. UI1.0 is deprecated.
     */
    getZoneDef(zoneId) {
        let zoneDef;
        switch (zoneId) {
            case 1:
                zoneDef = this.topLeft;
                break;
            case 2:
                zoneDef = this.topCenter;
                break;
            case 3:
                zoneDef = this.topRight;
                break;
            case 4:
                zoneDef = this.centerLeft;
                break;
            case 6:
                zoneDef = this.centerRight;
                break;
            case 7:
                zoneDef = this.bottomLeft;
                break;
            case 8:
                zoneDef = this.bottomCenter;
                break;
            case 9:
                zoneDef = this.bottomRight;
                break;
            // istanbul ignore next
            default:
                throw new RangeError();
        }
        // Zones can be undefined in a Frontstage
        return zoneDef;
    }
    /** Gets a list of [[ZoneDef]]s.
     * @deprecated in 3.6. UI1.0 is deprecated.
     */
    get zoneDefs() {
        const zones = [1, 2, 3, 4, 6, 7, 8, 9];
        const zoneDefs = [];
        zones.forEach((zoneId) => {
            const zoneDef = this.getZoneDef(zoneId);
            if (zoneDef)
                zoneDefs.push(zoneDef);
        });
        return zoneDefs;
    }
    /** 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.TopMost:
                panelDef = this.topMostPanel; // eslint-disable-line deprecation/deprecation
                break;
            case StagePanelLocation.Left:
                panelDef = this.leftPanel;
                break;
            case StagePanelLocation.Right:
                panelDef = this.rightPanel;
                break;
            case StagePanelLocation.Bottom:
                panelDef = this.bottomPanel;
                break;
            case StagePanelLocation.BottomMost:
                panelDef = this.bottomMostPanel; // eslint-disable-line deprecation/deprecation
                break;
            // istanbul ignore next
            default:
                throw new RangeError();
        }
        // Panels can be undefined in a Frontstage
        return panelDef;
    }
    /** Gets a list of [[StagePanelDef]]s
     * @beta
     */
    get panelDefs() {
        const panels = [
            StagePanelLocation.Left, StagePanelLocation.Right,
            StagePanelLocation.Top, StagePanelLocation.TopMost,
            StagePanelLocation.Bottom, StagePanelLocation.BottomMost,
        ];
        const panelDefs = [];
        panels.forEach((location) => {
            const panelDef = this.getStagePanelDef(location);
            if (panelDef)
                panelDefs.push(panelDef);
        });
        return panelDefs;
    }
    /** Finds a [[WidgetDef]] based on a given id */
    findWidgetDef(id) {
        for (const zoneDef of this.zoneDefs) {
            const widgetDef = zoneDef.findWidgetDef(id);
            if (widgetDef)
                return widgetDef;
        }
        for (const panelDef of this.panelDefs) {
            const widgetDef = panelDef.findWidgetDef(id);
            if (widgetDef)
                return widgetDef;
        }
        // istanbul ignore next
        return undefined;
    }
    /** Gets the list of [[WidgetControl]]s */
    get _widgetControls() {
        const widgetControls = new Array();
        this.zoneDefs.forEach((zoneDef) => {
            zoneDef.widgetDefs.forEach((widgetDef) => {
                const widgetControl = widgetDef.widgetControl;
                if (widgetControl)
                    widgetControls.push(widgetControl);
            });
        });
        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.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.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 FrontstageProps
     * @internal
     */
    async initializeFromProps(props) {
        this._id = props.id;
        this._initialProps = props;
        this._defaultTool = props.defaultTool; // Might be `undefined`, see `toFrontstageProps`.
        if (props.defaultContentId !== undefined)
            this._defaultContentId = props.defaultContentId;
        if (props.contentGroup instanceof ContentGroupProvider) {
            this._contentGroupProvider = props.contentGroup;
        }
        else {
            this._contentGroup = props.contentGroup;
        }
        if (props.isInFooterMode !== undefined)
            this._isInFooterMode = props.isInFooterMode;
        this._applicationData = props.applicationData;
        this._usage = props.usage;
        this._version = props.version || 0;
        // eslint-disable-next-line deprecation/deprecation
        this._topLeft = Frontstage.createZoneDef(props.contentManipulationTools ? props.contentManipulationTools : props.topLeft, ZoneLocation.TopLeft, props);
        // eslint-disable-next-line deprecation/deprecation
        this._topCenter = Frontstage.createZoneDef(props.toolSettings ? props.toolSettings : props.topCenter, ZoneLocation.TopCenter, props);
        // eslint-disable-next-line deprecation/deprecation
        this._topRight = Frontstage.createZoneDef(props.viewNavigationTools ? /* istanbul ignore next */ props.viewNavigationTools : props.topRight, ZoneLocation.TopRight, props);
        // eslint-disable-next-line deprecation/deprecation
        this._centerLeft = Frontstage.createZoneDef(props.centerLeft, ZoneLocation.CenterLeft, props);
        // eslint-disable-next-line deprecation/deprecation
        this._centerRight = Frontstage.createZoneDef(props.centerRight, ZoneLocation.CenterRight, props);
        // eslint-disable-next-line deprecation/deprecation
        this._bottomLeft = Frontstage.createZoneDef(props.bottomLeft, ZoneLocation.BottomLeft, props);
        // eslint-disable-next-line deprecation/deprecation
        this._bottomCenter = Frontstage.createZoneDef(props.statusBar ? props.statusBar : props.bottomCenter, ZoneLocation.BottomCenter, props);
        // eslint-disable-next-line deprecation/deprecation
        this._bottomRight = Frontstage.createZoneDef(props.bottomRight, ZoneLocation.BottomRight, props);
        // eslint-disable-next-line deprecation/deprecation
        this._topPanel = Frontstage.createStagePanelDef(StagePanelLocation.Top, props);
        this._topMostPanel = Frontstage.createStagePanelDef(StagePanelLocation.TopMost, props);
        this._leftPanel = Frontstage.createStagePanelDef(StagePanelLocation.Left, props);
        this._rightPanel = Frontstage.createStagePanelDef(StagePanelLocation.Right, props);
        this._bottomPanel = Frontstage.createStagePanelDef(StagePanelLocation.Bottom, props);
        this._bottomMostPanel = Frontstage.createStagePanelDef(StagePanelLocation.BottomMost, props);
    }
    /** @internal */
    updateWidgetDefs() {
        // Tracks provided widgets to prevent duplicates.
        const allStageWidgetDefs = [];
        // Process panels before zones so in uiVersion="2" extension can explicitly target a widget for a StagePanelSection
        this.panelDefs.forEach((stagePanelDef) => {
            stagePanelDef.updateDynamicWidgetDefs(this.id, this.usage, stagePanelDef.location, undefined, allStageWidgetDefs, this.applicationData);
        });
        this.zoneDefs.forEach((zoneDef) => {
            zoneDef.updateDynamicWidgetDefs(this.id, this.usage, zoneDef.zoneLocation, undefined, allStageWidgetDefs, this.applicationData);
        });
    }
    /** @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.onFrontstageRestoreLayoutEvent.emit({ frontstageDef: this });
    }
    /** Used only in UI 2.0 to determine WidgetState from NinezoneState
     *  @internal
     */
    getWidgetCurrentState(widgetDef) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetDef.id);
            // istanbul ignore next
            if (!location)
                return WidgetState.Hidden;
            if (isFloatingTabLocation(location)) {
                const floatingWidget = this.nineZoneState.floatingWidgets.byId[location.floatingWidgetId];
                if (floatingWidget && floatingWidget.hidden)
                    return WidgetState.Hidden;
                else
                    return WidgetState.Floating;
            }
            let collapsedPanel = false;
            // istanbul ignore else
            if ("side" in location) {
                const panel = this.nineZoneState.panels[location.side];
                collapsedPanel = panel.collapsed || undefined === panel.size || 0 === panel.size;
            }
            const widgetContainer = this.nineZoneState.widgets[location.widgetId];
            if (widgetDef.id === widgetContainer.activeTabId && !collapsedPanel)
                return WidgetState.Open;
            else
                return WidgetState.Closed;
        }
        return widgetDef.defaultState;
    }
    /** Used only in UI 2.0 to determine StagePanelState and size from NinezoneState
     *  @internal
     */
    getPanelCurrentState(panelDef) {
        var _a, _b;
        // istanbul ignore next
        if (this.nineZoneState) {
            const side = toPanelSide(panelDef.location);
            const panel = this.nineZoneState.panels[side];
            if (panel)
                return [panel.collapsed ? StagePanelState.Minimized : StagePanelState.Open, (_a = panel.size) !== null && _a !== void 0 ? _a : 0];
            return [StagePanelState.Off, 0];
        }
        return [panelDef.defaultState, (_b = panelDef.defaultSize) !== null && _b !== void 0 ? _b : 0];
    }
    isPopoutWidget(widgetId) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetId);
            // istanbul ignore else
            if (location)
                return isPopoutTabLocation(location);
        }
        return false;
    }
    isFloatingWidget(widgetId) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetId);
            // istanbul ignore else
            if (location)
                return 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 point 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, point, size) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetId);
            if (location) {
                let popoutWidgetContainerId;
                if (isPopoutTabLocation(location)) {
                    popoutWidgetContainerId = location.popoutWidgetId;
                }
                const state = floatWidget(this.nineZoneState, widgetId, point, size);
                // istanbul ignore else
                if (state) {
                    this.nineZoneState = state;
                    setTimeout(() => {
                        popoutWidgetContainerId && UiFramework.childWindows.close(popoutWidgetContainerId, true);
                    }, 600);
                }
            }
        }
    }
    /** Check widget and panel state to determine whether the widget is currently displayed
     * @param widgetId case-sensitive Widget Id
     * @public
     */
    isWidgetDisplayed(widgetId) {
        let widgetIsVisible = false;
        // istanbul ignore else
        if (this.nineZoneState) {
            const tabLocation = getTabLocation(this.nineZoneState, widgetId);
            // istanbul ignore else
            if (tabLocation) {
                if (isFloatingTabLocation(tabLocation)) {
                    const floatingWidget = this.nineZoneState.floatingWidgets.byId[tabLocation.floatingWidgetId];
                    // istanbul ignore else
                    if (!!!floatingWidget.hidden)
                        widgetIsVisible = true;
                }
                else if (isPopoutTabLocation(tabLocation)) {
                    widgetIsVisible = true;
                }
                else {
                    // istanbul ignore else
                    if (isPanelTabLocation(tabLocation)) {
                        const panel = this.nineZoneState.panels[tabLocation.side];
                        const widgetDef = this.findWidgetDef(widgetId);
                        if (widgetDef && widgetDef.state === WidgetState.Open && !panel.collapsed)
                            widgetIsVisible = true;
                    }
                }
            }
        }
        return widgetIsVisible;
    }
    /** Opens window for specified PopoutWidget container. Used to reopen popout when running in Electron.
     * @internal */
    openPopoutWidgetContainer(state, widgetContainerId) {
        const location = getWidgetLocation(state, widgetContainerId);
        // istanbul ignore next
        if (!location)
            return;
        // istanbul ignore next
        if (!isPopoutWidgetLocation(location))
            return;
        const widget = state.widgets[widgetContainerId];
        const popoutWidget = state.popoutWidgets.byId[location.popoutWidgetId];
        // Popout widget should only contain a single tab.
        // istanbul ignore next
        if (widget.tabs.length !== 1)
            return;
        const tabId = widget.tabs[0];
        const widgetDef = this.findWidgetDef(tabId);
        // istanbul ignore next
        if (!widgetDef)
            return;
        const popoutContent = (React.createElement(PopoutWidget, { widgetContainerId: widgetContainerId, widgetDef: widgetDef }));
        const bounds = Rectangle.create(popoutWidget.bounds);
        const position = {
            width: bounds.getWidth(),
            height: bounds.getHeight(),
            left: bounds.left,
            top: bounds.top,
        };
        UiFramework.childWindows.open(widgetContainerId, widgetDef.label, popoutContent, position, 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 point 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, point, size) {
        // istanbul ignore next
        if (!this.nineZoneState)
            return;
        let location = getTabLocation(this.nineZoneState, widgetId);
        if (!location || isPopoutTabLocation(location))
            return;
        const widgetDef = this.findWidgetDef(widgetId);
        // istanbul ignore next
        if (!widgetDef)
            return;
        // get the state to apply that will pop-out the specified WidgetTab to child window.
        let preferredBounds = Rectangle.createFromSize({ height: 800, width: 600 });
        // istanbul ignore next
        if (widgetDef.popoutBounds)
            preferredBounds = widgetDef.popoutBounds;
        if (size)
            preferredBounds = preferredBounds.setSize(size);
        if (point)
            preferredBounds = preferredBounds.setPosition(point);
        const state = popoutWidgetToChildWindow(this.nineZoneState, widgetId, preferredBounds);
        this.nineZoneState = state;
        // now that the state is updated get the id of the container that houses the widgetTab/widgetId
        location = getTabLocation(state, widgetId);
        assert(!!location && isPopoutTabLocation(location));
        const widgetContainerId = location.widgetId;
        const popoutWidget = state.popoutWidgets.byId[widgetContainerId];
        const bounds = Rectangle.create(popoutWidget.bounds);
        setTimeout(() => {
            const popoutContent = (React.createElement(PopoutWidget, { widgetContainerId: widgetContainerId, widgetDef: widgetDef }));
            const position = {
                width: bounds.getWidth(),
                height: bounds.getHeight(),
                left: bounds.left,
                top: bounds.top,
            };
            UiFramework.childWindows.open(widgetContainerId, widgetDef.label, popoutContent, position, UiFramework.useDefaultPopoutUrl);
        });
    }
    get isStageClosing() {
        return this._isStageClosing;
    }
    get isApplicationClosing() {
        return this._isApplicationClosing;
    }
    get isReady() {
        return this._isReady;
    }
    /** @internal */
    saveChildWindowSizeAndPosition(childWindowId, childWindow) {
        if (!this.nineZoneState)
            return;
        const location = getWidgetLocation(this.nineZoneState, childWindowId);
        if (!location || !isPopoutWidgetLocation(location))
            return;
        const widget = this.nineZoneState.widgets[location.popoutWidgetId];
        const tabId = widget.tabs[0];
        const widgetDef = this.findWidgetDef(tabId);
        if (!widgetDef)
            return;
        const adjustmentWidth = ProcessDetector.isElectronAppFrontend ? 16 : 0;
        const adjustmentHeight = ProcessDetector.isElectronAppFrontend ? 39 : 0;
        const width = childWindow.innerWidth + adjustmentWidth;
        const height = childWindow.innerHeight + adjustmentHeight;
        const bounds = Rectangle.createFromSize({ width, height }).offset({ x: childWindow.screenX, y: childWindow.screenY });
        widgetDef.popoutBounds = bounds;
    }
    /** Method used to possibly change a Popout Widget back to a docked widget if the user was the one closing the popout's child
     * window.
     *  @internal
     */
    dockPopoutWidgetContainer(widgetContainerId) {
        if (!this.nineZoneState)
            return;
        // Make sure the widgetContainerId is still in popout state. We don't want to set it to docked if the window is being closed because
        // an API call has moved the widget from a popout state to a floating state.
        // istanbul ignore else
        const location = getWidgetLocation(this.nineZoneState, widgetContainerId);
        if (!location || !isPopoutWidgetLocation(location))
            return;
        const state = dockWidgetContainer(this.nineZoneState, widgetContainerId, true);
        this.nineZoneState = state;
    }
    /** 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) {
        // istanbul ignore else
        if (this.nineZoneState) {
            const location = getTabLocation(this.nineZoneState, widgetId);
            if (location) {
                const widgetContainerId = location.widgetId;
                const state = dockWidgetContainer(this.nineZoneState, widgetContainerId, true);
                state && (this.nineZoneState = state);
                if (isPopoutTabLocation(location)) {
                    UiFramework.childWindows.close(location.widgetId, true);
                }
            }
        }
    }
    setFloatingWidgetContainerBounds(floatingWidgetId, bounds) {
        if (!this.nineZoneState || !(floatingWidgetId in this.nineZoneState.floatingWidgets.byId))
            return false;
        let state = FrameworkStateReducer(this.nineZoneState, {
            type: "FLOATING_WIDGET_SET_BOUNDS",
            id: floatingWidgetId,
            bounds,
        }, this);
        state = FrameworkStateReducer(state, {
            type: "FLOATING_WIDGET_SET_USER_SIZED",
            id: floatingWidgetId,
            userSized: true,
        }, this);
        this.nineZoneState = state;
        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);
        // istanbul ignore else
        if (location && 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 zoneDef of this.zoneDefs) {
            for (const widgetDef of zoneDef.widgetDefs) {
                yield widgetDef;
            }
        }
        for (const panelDef of this.panelDefs) {
            for (const widgetDef of panelDef.widgetDefs) {
                yield widgetDef;
            }
        }
        return undefined;
    }
    /** Iterable of all widget definitions in a frontstage.
     * @internal
     */
    get widgetDefs() {
        const defs = this._widgetDefs();
        return {
            [Symbol.iterator]() {
                return defs;
            },
        };
    }
}
function toFrontstageProps(config) {
    const { contentManipulation, viewNavigation, toolSettings, statusBar, topPanel, leftPanel, bottomPanel, rightPanel, ...other } = config;
    const props = {
        ...other,
        defaultTool: undefined,
        toolSettings: toolSettings ? toZoneElement(toolSettings) : undefined,
        statusBar: statusBar ? toZoneElement(statusBar) : undefined,
        contentManipulationTools: contentManipulation ? toZoneElement(contentManipulation) : undefined,
        viewNavigationTools: viewNavigation ? toZoneElement(viewNavigation) : undefined,
        topPanel: topPanel ? toStagePanelElement(topPanel) : undefined,
        leftPanel: leftPanel ? toStagePanelElement(leftPanel) : undefined,
        bottomPanel: bottomPanel ? toStagePanelElement(bottomPanel) : undefined,
        rightPanel: rightPanel ? toStagePanelElement(rightPanel) : undefined,
    };
    return props;
}
function toWidgetElement(config) {
    return (React.createElement(Widget, { key: config.id, ...config }));
}
function toZoneElement(config) {
    return (React.createElement(Zone, { widgets: [
            toWidgetElement(config),
        ] }));
}
function toStagePanelElement(config) {
    var _a, _b;
    const { sections, ...other } = config;
    const startWidgets = (_a = sections === null || sections === void 0 ? void 0 : sections.start) === null || _a === void 0 ? void 0 : _a.map((widget) => toWidgetElement(widget));
    const endWidgets = (_b = sections === null || sections === void 0 ? void 0 : sections.end) === null || _b === void 0 ? void 0 : _b.map((widget) => toWidgetElement(widget));
    const panelZones = {};
    if (startWidgets)
        panelZones.start = {
            widgets: startWidgets,
        };
    if (endWidgets)
        panelZones.end = {
            widgets: endWidgets,
        };
    return (React.createElement(StagePanel, { panelZones: panelZones, ...other }));
}
let widgetConfigId = 0;
function toWidgetConfig(widget) {
    const props = widget.props;
    return {
        ...props,
        id: props.id ? props.id : `widget-config-${++widgetConfigId}`,
    };
}
function toWidgetConfigFromZone(zone) {
    const widgets = zone.props.widgets;
    if (!widgets || widgets.length === 0)
        return undefined;
    const widget = widgets[0];
    return toWidgetConfig(widget);
}
function toPanelConfig(panel) {
    var _a, _b, _c, _d;
    const props = panel.props;
    const { panelZones, ...other } = props;
    const start = (_b = (_a = panelZones === null || panelZones === void 0 ? void 0 : panelZones.start) === null || _a === void 0 ? void 0 : _a.widgets) === null || _b === void 0 ? void 0 : _b.map((w) => toWidgetConfig(w));
    const end = (_d = (_c = panelZones === null || panelZones === void 0 ? void 0 : panelZones.end) === null || _c === void 0 ? void 0 : _c.widgets) === null || _d === void 0 ? void 0 : _d.map((w) => toWidgetConfig(w));
    const config = {
        ...other,
        sections: {
            start,
            end,
        },
    };
    return config;
}
function toFrontstageConfig(props) {
    const { contentManipulationTools, viewNavigationTools, toolSettings, statusBar, topPanel, leftPanel, bottomPanel, rightPanel, ...other } = props;
    const config = {
        ...other,
        version: props.version || 0,
        contentManipulation: contentManipulationTools ? toWidgetConfigFromZone(contentManipulationTools) : undefined,
        viewNavigation: viewNavigationTools ? toWidgetConfigFromZone(viewNavigationTools) : undefined,
        toolSettings: toolSettings ? toWidgetConfigFromZone(toolSettings) : undefined,
        statusBar: statusBar ? toWidgetConfigFromZone(statusBar) : undefined,
        topPanel: topPanel ? toPanelConfig(topPanel) : undefined,
        leftPanel: leftPanel ? toPanelConfig(leftPanel) : undefined,
        bottomPanel: bottomPanel ? toPanelConfig(bottomPanel) : undefined,
        rightPanel: rightPanel ? toPanelConfig(rightPanel) : undefined,
    };
    return config;
}
//# sourceMappingURL=FrontstageDef.js.map