/*---------------------------------------------------------------------------------------------
* 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 "./Frontstage.scss";
import produce from "immer";
import * as React from "react";
import { assert, Logger, ProcessDetector } from "@itwin/core-bentley";
import { StagePanelLocation, UiItemsManager, WidgetState } from "@itwin/appui-abstract";
import { Rectangle, Size, UiStateStorageStatus } from "@itwin/core-react";
import { ToolbarPopupAutoHideContext } from "@itwin/components-react";
import { addFloatingWidget, addPanelWidget, addTab, addTabToWidget, convertAllPopupWidgetContainersToFloating, createNineZoneState, floatingWidgetBringToFront, FloatingWidgets, getTabLocation, getUniqueId, getWidgetPanelSectionId, insertPanelWidget, insertTabToWidget, isFloatingTabLocation, isHorizontalPanelSide, isPanelTabLocation, isPopoutTabLocation, NineZone, NineZoneStateReducer, panelSides, removeTab, removeTabFromWidget, toolSettingsTabId, WidgetPanels, } from "@itwin/appui-layout-react";
import { useActiveFrontstageDef } from "../frontstage/Frontstage";
import { StagePanelState } from "../stagepanels/StagePanelDef";
import { UiFramework } from "../UiFramework";
import { useUiStateStorageHandler } from "../uistate/useUiStateStorage";
import { ZoneState } from "../zones/ZoneDef";
import { WidgetContent } from "./Content";
import { WidgetPanelsFrontstageContent } from "./FrontstageContent";
import { ModalFrontstageComposer, useActiveModalFrontstageInfo } from "./ModalFrontstageComposer";
import { WidgetPanelsStatusBar } from "./StatusBar";
import { WidgetPanelsTab } from "./Tab";
import { WidgetPanelsToolbars } from "./Toolbars";
import { ToolSettingsContent, WidgetPanelsToolSettings } from "./ToolSettings";
import { useEscapeSetFocusToHome } from "../hooks/useEscapeSetFocusToHome";
import { useSelector } from "react-redux";
import { useUiVisibility } from "../hooks/useUiVisibility";
import { IModelApp } from "@itwin/core-frontend";
import { FloatingWidget } from "./FloatingWidget";
import { InternalFrontstageManager } from "../frontstage/InternalFrontstageManager";
const panelZoneKeys = ["start", "end"];
const WidgetInternals = React.memo(function WidgetInternals() {
    const activeModalFrontstageInfo = useActiveModalFrontstageInfo();
    const uiIsVisible = useUiVisibility();
    return (React.createElement(React.Fragment, null,
        React.createElement(ToolbarPopupAutoHideContext.Provider, { value: !uiIsVisible },
            React.createElement(ModalFrontstageComposer, { stageInfo: activeModalFrontstageInfo }),
            React.createElement(WidgetPanels, { className: "uifw-widgetPanels", centerContent: React.createElement(WidgetPanelsToolbars, null) },
                React.createElement(WidgetPanelsFrontstageContent, null)),
            React.createElement(WidgetPanelsStatusBar, null),
            React.createElement(FloatingWidgets, null))));
});
// istanbul ignore next
const WidgetPanelsFrontstageComponent = React.memo(function WidgetPanelsFrontstageComponent() {
    return (React.createElement(React.Fragment, null,
        React.createElement(WidgetPanelsToolSettings, null),
        React.createElement(WidgetInternals, null)));
});
const widgetContent = React.createElement(WidgetContent, null);
const toolSettingsContent = React.createElement(ToolSettingsContent, null);
const widgetPanelsFrontstage = React.createElement(WidgetPanelsFrontstageComponent, null);
/** @internal */
export function useNineZoneState(frontstageDef) {
    const lastFrontstageDef = React.useRef(frontstageDef);
    const [nineZone, setNineZone] = React.useState(frontstageDef.nineZoneState);
    React.useEffect(() => {
        setNineZone(frontstageDef.nineZoneState);
        lastFrontstageDef.current = frontstageDef;
    }, [frontstageDef]);
    React.useEffect(() => {
        const listener = (args) => {
            if (args.frontstageDef !== frontstageDef || frontstageDef.isStageClosing || frontstageDef.isApplicationClosing)
                return;
            setNineZone(args.state);
        };
        return InternalFrontstageManager.onFrontstageNineZoneStateChangedEvent.addListener(listener);
    }, [frontstageDef]);
    return lastFrontstageDef.current === frontstageDef ? nineZone : frontstageDef.nineZoneState;
}
/** @returns Defined NineZoneState with fallback to last defined and default NineZoneState.
 * @internal
 */
function useCachedNineZoneState(nineZone) {
    const cached = React.useRef(nineZone || defaultNineZone);
    React.useEffect(() => {
        if (nineZone)
            cached.current = nineZone;
    }, [nineZone]);
    return nineZone || cached.current;
}
/** Update in-memory NineZoneState of newly activated frontstage with up to date size.
 * @internal
 */
export function useUpdateNineZoneSize(frontstageDef) {
    React.useEffect(() => {
        const size = InternalFrontstageManager.nineZoneSize;
        let state = frontstageDef.nineZoneState;
        if (!size || !state)
            return;
        state = FrameworkStateReducer(state, {
            type: "RESIZE",
            size: {
                height: size.height,
                width: size.width,
            },
        }, frontstageDef);
        frontstageDef.nineZoneState = state;
    }, [frontstageDef]);
}
/** @internal */
export function FrameworkStateReducer(state, action, frontstageDef) {
    state = NineZoneStateReducer(state, action);
    if (action.type === "RESIZE") {
        state = produce(state, (draft) => {
            for (const panelSide of panelSides) {
                const panel = draft.panels[panelSide];
                const location = toStagePanelLocation(panelSide);
                const panelDef = frontstageDef.getStagePanelDef(location);
                if (panelDef === null || panelDef === void 0 ? void 0 : panelDef.maxSizeSpec) {
                    panel.maxSize = getPanelMaxSize(panelDef.maxSizeSpec, panelSide, action.size);
                    if (panel.size) {
                        panel.size = Math.min(Math.max(panel.size, panel.minSize), panel.maxSize);
                    }
                }
            }
        });
    }
    return state;
}
/** @internal */
export function useNineZoneDispatch(frontstageDef) {
    const dispatch = React.useCallback((action) => {
        if (action.type === "RESIZE") {
            InternalFrontstageManager.nineZoneSize = Size.create(action.size);
        }
        // istanbul ignore if
        if (action.type === "TOOL_SETTINGS_DRAG_START") {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            UiFramework.postTelemetry("Tool Settings Undocking", "28B04E07-AE73-4533-A0BA-8E2A8DC99ADF");
        }
        // istanbul ignore if
        if (action.type === "TOOL_SETTINGS_DOCK") {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            UiFramework.postTelemetry("Tool Settings Docking to Settings Bar", "BEDE684B-B3DB-4637-B3AF-DC3CBA223F94");
        }
        if (action.type === "WIDGET_TAB_POPOUT") {
            const tabId = action.id;
            frontstageDef.popoutWidget(tabId);
            return;
        }
        const nineZoneState = frontstageDef.nineZoneState;
        if (!nineZoneState)
            return;
        frontstageDef.nineZoneState = FrameworkStateReducer(nineZoneState, action, frontstageDef);
    }, [frontstageDef]);
    return dispatch;
}
/** @internal */
export const WidgetPanelsFrontstage = React.memo(function WidgetPanelsFrontstage() {
    const frontstageDef = useActiveFrontstageDef();
    React.useEffect(() => {
        const triggerWidowCloseProcessing = () => {
            frontstageDef && frontstageDef.setIsApplicationClosing(true);
        };
        frontstageDef && frontstageDef.setIsApplicationClosing(false);
        window.addEventListener("unload", triggerWidowCloseProcessing);
        return () => {
            window.removeEventListener("unload", triggerWidowCloseProcessing);
        };
    }, [frontstageDef]);
    if (!frontstageDef)
        return null;
    return (React.createElement(ActiveFrontstageDefProvider, { frontstageDef: frontstageDef }));
});
const defaultNineZone = createNineZoneState();
const tabElement = React.createElement(WidgetPanelsTab, null);
const floatingWidgetElement = React.createElement(FloatingWidget, null);
/** @internal */
export function ActiveFrontstageDefProvider({ frontstageDef }) {
    let nineZone = useNineZoneState(frontstageDef);
    nineZone = useCachedNineZoneState(nineZone);
    const dispatch = useNineZoneDispatch(frontstageDef);
    useUpdateNineZoneSize(frontstageDef);
    useSavedFrontstageState(frontstageDef);
    useSaveFrontstageSettings(frontstageDef);
    useItemsManager(frontstageDef);
    const labels = useLabels();
    const uiIsVisible = useUiVisibility();
    const showWidgetIcon = useSelector((state) => {
        const frameworkState = state[UiFramework.frameworkStateKey];
        return !!frameworkState.configurableUiState.showWidgetIcon;
    });
    const autoCollapseUnpinnedPanels = useSelector((state) => {
        const frameworkState = state[UiFramework.frameworkStateKey];
        return !!frameworkState.configurableUiState.autoCollapseUnpinnedPanels;
    });
    const animateToolSettings = useSelector((state) => {
        const frameworkState = state[UiFramework.frameworkStateKey];
        return !!frameworkState.configurableUiState.animateToolSettings;
    });
    const useToolAsToolSettingsLabel = useSelector((state) => {
        const frameworkState = state[UiFramework.frameworkStateKey];
        return !!frameworkState.configurableUiState.useToolAsToolSettingsLabel;
    });
    useFrontstageManager(frontstageDef, useToolAsToolSettingsLabel);
    const handleKeyDown = useEscapeSetFocusToHome();
    return (React.createElement("div", { className: "uifw-widgetPanels-frontstage", onKeyDown: handleKeyDown, role: "presentation" },
        React.createElement(NineZone, { dispatch: dispatch, labels: labels, state: nineZone, tab: tabElement, floatingWidget: floatingWidgetElement, showWidgetIcon: showWidgetIcon, autoCollapseUnpinnedPanels: autoCollapseUnpinnedPanels, toolSettingsContent: toolSettingsContent, widgetContent: widgetContent, animateDockedToolSettings: animateToolSettings, uiIsVisible: uiIsVisible }, widgetPanelsFrontstage)));
}
/** @internal */
export function useLabels() {
    return React.useMemo(() => ({
        dockToolSettingsTitle: UiFramework.translate("widget.tooltips.dockToolSettings"),
        moreWidgetsTitle: UiFramework.translate("widget.tooltips.moreWidgets"),
        moreToolSettingsTitle: UiFramework.translate("widget.tooltips.moreToolSettings"),
        pinPanelTitle: UiFramework.translate("widget.tooltips.pinPanel"),
        resizeGripTitle: UiFramework.translate("widget.tooltips.resizeGrip"),
        sendWidgetHomeTitle: UiFramework.translate("widget.tooltips.sendHome"),
        toolSettingsHandleTitle: UiFramework.translate("widget.tooltips.toolSettingsHandle"),
        unpinPanelTitle: UiFramework.translate("widget.tooltips.unpinPanel"),
        popoutActiveTab: UiFramework.translate("widget.tooltips.popoutActiveTab"),
    }), []);
}
/** @internal */
export function addWidgets(state, widgets, side, widgetId) {
    const visibleWidgets = widgets.filter((w) => w.isVisible && (w.defaultState !== WidgetState.Floating));
    if (visibleWidgets.length === 0)
        return state;
    const tabs = new Array();
    for (const widget of visibleWidgets) {
        const label = getWidgetLabel(widget.label);
        state = addTab(state, widget.id, {
            label,
            iconSpec: widget.iconSpec,
            preferredPanelWidgetSize: widget.preferredPanelSize,
            preferredFloatingWidgetSize: widget.defaultFloatingSize,
            canPopout: widget.canPopout,
            isFloatingStateWindowResizable: widget.isFloatingStateWindowResizable,
            hideWithUiWhenFloating: !!widget.hideWithUiWhenFloating,
            allowedPanelTargets: widget.allowedPanelTargets,
        });
        tabs.push(widget.id);
    }
    const activeWidget = visibleWidgets.find((widget) => widget.isActive);
    const minimized = false;
    // istanbul ignore else
    if ((activeWidget === null || activeWidget === void 0 ? void 0 : activeWidget.defaultState) !== WidgetState.Floating) {
        state = addPanelWidget(state, side, widgetId, tabs, {
            activeTabId: activeWidget ? activeWidget.id : tabs[0],
            minimized,
        });
    }
    return state;
}
/** @internal */
// istanbul ignore next
export function appendWidgets(state, widgetDefs, side, preferredWidgetIndex) {
    var _a;
    if (widgetDefs.length === 0)
        return state;
    // Add missing widget tabs.
    for (const widgetDef of widgetDefs) {
        const label = getWidgetLabel(widgetDef.label);
        const saveTab = state.tabs[widgetDef.id];
        const preferredPanelWidgetSize = saveTab ? saveTab.preferredPanelWidgetSize : widgetDef.preferredPanelSize;
        const preferredFloatingWidgetSize = saveTab ? saveTab.preferredFloatingWidgetSize : widgetDef.defaultFloatingSize;
        // if a defaultFloatingSize is specified then this mean the widget can't determine its intrinsic size and must be explicitly sized
        const userSized = saveTab ? (saveTab.userSized || !!widgetDef.defaultFloatingSize && !!saveTab.preferredFloatingWidgetSize)
            : !!widgetDef.defaultFloatingSize;
        state = addTab(state, widgetDef.id, {
            label,
            iconSpec: widgetDef.iconSpec,
            canPopout: widgetDef.canPopout,
            preferredPanelWidgetSize,
            preferredFloatingWidgetSize,
            userSized,
            isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
            hideWithUiWhenFloating: !!widgetDef.hideWithUiWhenFloating,
            allowedPanelTargets: widgetDef.allowedPanelTargets,
        });
        if (widgetDef.isFloatingStateSupported && widgetDef.defaultState === WidgetState.Floating) {
            const floatingContainerId = (_a = widgetDef.floatingContainerId) !== null && _a !== void 0 ? _a : getUniqueId();
            const widgetContainerId = getWidgetId(side, panelZoneKeys[preferredWidgetIndex]);
            const home = { side, widgetId: widgetContainerId, widgetIndex: 0 };
            const preferredPosition = widgetDef.defaultFloatingPosition;
            if (floatingContainerId in state.widgets) {
                state = addTabToWidget(state, widgetDef.id, floatingContainerId);
            }
            else {
                const tab = state.tabs[widgetDef.id];
                const size = { height: 200, width: 300, ...tab.preferredFloatingWidgetSize };
                const preferredPoint = preferredPosition !== null && preferredPosition !== void 0 ? preferredPosition : { x: (state.size.width - size.width) / 2, y: (state.size.height - size.height) / 2 };
                const nzBounds = Rectangle.createFromSize(state.size);
                const bounds = Rectangle.createFromSize(size).offset(preferredPoint);
                const containedBounds = bounds.containIn(nzBounds);
                state = addFloatingWidget(state, floatingContainerId, [widgetDef.id], {
                    bounds: containedBounds.toProps(),
                    home,
                    userSized,
                }, {
                    isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
                });
            }
        }
        else {
            const widgetPanelSectionId = getWidgetPanelSectionId(side, preferredWidgetIndex);
            if (widgetPanelSectionId in state.widgets) {
                state = addTabToWidget(state, widgetDef.id, widgetPanelSectionId);
            }
            else {
                const panel = state.panels[side];
                if (panel.widgets.length < panel.maxWidgetCount) {
                    state = addPanelWidget(state, side, widgetPanelSectionId, [widgetDef.id]);
                }
                else {
                    const widgetIndex = Math.min(preferredWidgetIndex, panel.widgets.length - 1);
                    const widgetId = panel.widgets[widgetIndex];
                    state = addTabToWidget(state, widgetDef.id, widgetId);
                }
            }
        }
    }
    return state;
}
function processPopoutWidgets(initialState, frontstageDef) {
    // istanbul ignore next
    if (!initialState.popoutWidgets)
        return initialState;
    // istanbul ignore next - not unit testing electron case that reopens popout windows
    if (initialState.popoutWidgets && ProcessDetector.isElectronAppFrontend) {
        if (initialState.popoutWidgets.allIds.length && ProcessDetector.isElectronAppFrontend) {
            for (const widgetContainerId of initialState.popoutWidgets.allIds) {
                frontstageDef.openPopoutWidgetContainer(initialState, widgetContainerId);
            }
        }
        return initialState;
    }
    return convertAllPopupWidgetContainersToFloating(initialState);
}
/** Adds frontstageDef widgets that are missing in NineZoneState.
 * @internal
 */
export function addMissingWidgets(frontstageDef, initialState) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
    let state = initialState;
    state = appendWidgets(state, determineNewWidgets((_a = frontstageDef.centerLeft) === null || _a === void 0 ? void 0 : _a.widgetDefs, state), "left", 0);
    state = appendWidgets(state, determineNewWidgets((_b = frontstageDef.leftPanel) === null || _b === void 0 ? void 0 : _b.panelZones.start.widgetDefs, state), "left", 0);
    state = appendWidgets(state, determineNewWidgets((_c = frontstageDef.bottomLeft) === null || _c === void 0 ? void 0 : _c.widgetDefs, state), "left", 1);
    state = appendWidgets(state, determineNewWidgets((_d = frontstageDef.leftPanel) === null || _d === void 0 ? void 0 : _d.panelWidgetDefs, state), "left", 1);
    state = appendWidgets(state, determineNewWidgets((_e = frontstageDef.leftPanel) === null || _e === void 0 ? void 0 : _e.panelZones.end.widgetDefs, state), "left", 1);
    state = appendWidgets(state, determineNewWidgets((_f = frontstageDef.centerRight) === null || _f === void 0 ? void 0 : _f.widgetDefs, state), "right", 0);
    state = appendWidgets(state, determineNewWidgets((_g = frontstageDef.rightPanel) === null || _g === void 0 ? void 0 : _g.panelZones.start.widgetDefs, state), "right", 0);
    state = appendWidgets(state, determineNewWidgets((_h = frontstageDef.bottomRight) === null || _h === void 0 ? void 0 : _h.widgetDefs, state), "right", 1);
    state = appendWidgets(state, determineNewWidgets((_j = frontstageDef.rightPanel) === null || _j === void 0 ? void 0 : _j.panelWidgetDefs, state), "right", 1);
    state = appendWidgets(state, determineNewWidgets((_k = frontstageDef.rightPanel) === null || _k === void 0 ? void 0 : _k.panelZones.end.widgetDefs, state), "right", 1);
    state = appendWidgets(state, determineNewWidgets((_l = frontstageDef.topPanel) === null || _l === void 0 ? void 0 : _l.panelWidgetDefs, state), "top", 0);
    state = appendWidgets(state, determineNewWidgets((_m = frontstageDef.topPanel) === null || _m === void 0 ? void 0 : _m.panelZones.start.widgetDefs, state), "top", 0);
    state = appendWidgets(state, determineNewWidgets((_o = frontstageDef.topMostPanel) === null || _o === void 0 ? void 0 : _o.panelWidgetDefs, state), "top", 1); // eslint-disable-line deprecation/deprecation
    state = appendWidgets(state, determineNewWidgets((_p = frontstageDef.topPanel) === null || _p === void 0 ? void 0 : _p.panelZones.end.widgetDefs, state), "top", 1);
    state = appendWidgets(state, determineNewWidgets((_q = frontstageDef.bottomPanel) === null || _q === void 0 ? void 0 : _q.panelWidgetDefs, state), "bottom", 0);
    state = appendWidgets(state, determineNewWidgets((_r = frontstageDef.bottomPanel) === null || _r === void 0 ? void 0 : _r.panelZones.start.widgetDefs, state), "bottom", 0);
    state = appendWidgets(state, determineNewWidgets((_s = frontstageDef.bottomMostPanel) === null || _s === void 0 ? void 0 : _s.panelWidgetDefs, state), "bottom", 1); // eslint-disable-line deprecation/deprecation
    state = appendWidgets(state, determineNewWidgets((_t = frontstageDef.bottomPanel) === null || _t === void 0 ? void 0 : _t.panelZones.end.widgetDefs, state), "bottom", 1);
    return state;
}
/** Hide widgets with Hidden defaultState. */
function hideWidgets(state, frontstageDef) {
    const tabs = Object.values(state.tabs);
    for (const tab of tabs) {
        const widgetDef = frontstageDef.findWidgetDef(tab.id);
        if (!widgetDef)
            continue;
        if (widgetDef.defaultState === WidgetState.Hidden) {
            state = hideWidget(state, widgetDef);
        }
    }
    return state;
}
/** Removes NineZoneState widgets that are missing in frontstageDef.
 * @internal
 */
export function removeMissingWidgets(frontstageDef, initialState) {
    let state = initialState;
    for (const [, tab] of Object.entries(state.tabs)) {
        if (tab.id === toolSettingsTabId)
            continue;
        const widgetDef = frontstageDef.findWidgetDef(tab.id);
        if (widgetDef)
            continue;
        state = removeTab(state, tab.id);
    }
    return state;
}
function getWidgetLabel(label) {
    return label === "" ? "Widget" : label;
}
function toStagePanelLocation(side) {
    switch (side) {
        case "bottom":
            return StagePanelLocation.Bottom;
        case "left":
            return StagePanelLocation.Left;
        case "right":
            return StagePanelLocation.Right;
        case "top":
            return StagePanelLocation.Top;
    }
}
/** @internal */
export function getWidgetId(side, key) {
    switch (side) {
        case "left": {
            if (key === "start") {
                return "leftStart";
            }
            return "leftEnd";
        }
        case "right": {
            if (key === "start") {
                return "rightStart";
            }
            return "rightEnd";
        }
        case "top": {
            if (key === "start") {
                return "topStart";
            }
            return "topEnd";
        }
        case "bottom": {
            if (key === "start")
                return "bottomStart";
            return "bottomEnd";
        }
    }
}
/** @internal */
export function getPanelZoneWidgets(frontstageDef, panelZone) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
    switch (panelZone) {
        case "leftStart": {
            const outArray = [...((_a = frontstageDef.centerLeft) === null || _a === void 0 ? void 0 : _a.widgetDefs) || []];
            (_b = frontstageDef.leftPanel) === null || _b === void 0 ? void 0 : _b.panelZones.start.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "leftEnd": {
            const outArray = [...((_c = frontstageDef.bottomLeft) === null || _c === void 0 ? void 0 : _c.widgetDefs) || []];
            (_d = frontstageDef.leftPanel) === null || _d === void 0 ? void 0 : _d.panelZones.end.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            (_e = frontstageDef.leftPanel) === null || _e === void 0 ? void 0 : _e.panelWidgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "rightStart": {
            const outArray = [...((_f = frontstageDef.centerRight) === null || _f === void 0 ? void 0 : _f.widgetDefs) || []];
            (_g = frontstageDef.rightPanel) === null || _g === void 0 ? void 0 : _g.panelZones.start.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "rightEnd": {
            const outArray = [...((_h = frontstageDef.bottomRight) === null || _h === void 0 ? void 0 : _h.widgetDefs) || []];
            (_j = frontstageDef.rightPanel) === null || _j === void 0 ? void 0 : _j.panelZones.end.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            (_k = frontstageDef.rightPanel) === null || _k === void 0 ? void 0 : _k.panelWidgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "topStart": {
            const outArray = [...((_l = frontstageDef.topPanel) === null || _l === void 0 ? void 0 : _l.panelWidgetDefs) || []];
            (_m = frontstageDef.topPanel) === null || _m === void 0 ? void 0 : _m.panelZones.start.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "topEnd": {
            const outArray = [...((_o = frontstageDef.topMostPanel) === null || _o === void 0 ? void 0 : _o.panelWidgetDefs) || []]; // eslint-disable-line deprecation/deprecation
            (_p = frontstageDef.topPanel) === null || _p === void 0 ? void 0 : _p.panelZones.end.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "bottomStart": {
            const outArray = [...((_q = frontstageDef.bottomPanel) === null || _q === void 0 ? void 0 : _q.panelWidgetDefs) || []];
            (_r = frontstageDef.bottomPanel) === null || _r === void 0 ? void 0 : _r.panelZones.start.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
        case "bottomEnd": {
            const outArray = [...((_s = frontstageDef.bottomMostPanel) === null || _s === void 0 ? void 0 : _s.panelWidgetDefs) || []]; // eslint-disable-line deprecation/deprecation
            (_t = frontstageDef.bottomPanel) === null || _t === void 0 ? void 0 : _t.panelZones.end.widgetDefs.forEach((widgetDef) => {
                // istanbul ignore else
                if (undefined === outArray.find((widget) => widget.id === widgetDef.id))
                    outArray.push(widgetDef);
            });
            return outArray;
        }
    }
}
/** @internal */
export function addPanelWidgets(state, frontstageDef, side) {
    const start = getWidgetId(side, "start");
    const startWidgets = getPanelZoneWidgets(frontstageDef, start);
    state = addWidgets(state, determineNewWidgets(startWidgets, state), side, start);
    const end = getWidgetId(side, "end");
    const endWidgets = getPanelZoneWidgets(frontstageDef, end);
    state = addWidgets(state, determineNewWidgets(endWidgets, state), side, end);
    return state;
}
/** @internal */
export function isFrontstageStateSettingResult(settingsResult) {
    if (settingsResult.status === UiStateStorageStatus.Success)
        return true;
    return false;
}
/** @internal */
export function initializePanel(state, frontstageDef, panelSide) {
    state = addPanelWidgets(state, frontstageDef, panelSide);
    const location = toStagePanelLocation(panelSide);
    const panelDef = frontstageDef.getStagePanelDef(location);
    state = produce(state, (draft) => {
        var _a, _b, _c;
        const panel = draft.panels[panelSide];
        panel.minSize = (_a = panelDef === null || panelDef === void 0 ? void 0 : panelDef.minSize) !== null && _a !== void 0 ? _a : panel.minSize;
        panel.pinned = (_b = panelDef === null || panelDef === void 0 ? void 0 : panelDef.pinned) !== null && _b !== void 0 ? _b : panel.pinned;
        panel.resizable = (_c = panelDef === null || panelDef === void 0 ? void 0 : panelDef.resizable) !== null && _c !== void 0 ? _c : panel.resizable;
        if (panelDef === null || panelDef === void 0 ? void 0 : panelDef.maxSizeSpec) {
            panel.maxSize = getPanelMaxSize(panelDef.maxSizeSpec, panelSide, state.size);
        }
        const size = panelDef === null || panelDef === void 0 ? void 0 : panelDef.size;
        panel.size = size === undefined ? size : Math.min(Math.max(size, panel.minSize), panel.maxSize);
    });
    return state;
}
function getPanelMaxSize(maxSizeSpec, panel, nineZoneSize) {
    if (typeof maxSizeSpec === "number") {
        return maxSizeSpec;
    }
    const size = isHorizontalPanelSide(panel) ? nineZoneSize.height : nineZoneSize.width;
    return maxSizeSpec.percentage / 100 * size;
}
const stateVersion = 13; // this needs to be bumped when NineZoneState is changed (to recreate the layout).
/** @internal */
export function initializeNineZoneState(frontstageDef) {
    let nineZone = defaultNineZone;
    nineZone = produce(nineZone, (stateDraft) => {
        // istanbul ignore next
        if (!InternalFrontstageManager.nineZoneSize)
            return;
        stateDraft.size = {
            height: InternalFrontstageManager.nineZoneSize.height,
            width: InternalFrontstageManager.nineZoneSize.width,
        };
    });
    nineZone = initializePanel(nineZone, frontstageDef, "left");
    nineZone = initializePanel(nineZone, frontstageDef, "right");
    nineZone = initializePanel(nineZone, frontstageDef, "top");
    nineZone = initializePanel(nineZone, frontstageDef, "bottom");
    nineZone = produce(nineZone, (stateDraft) => {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        for (const [, panel] of Object.entries(stateDraft.panels)) {
            const expanded = panel.widgets.find((widgetId) => stateDraft.widgets[widgetId].minimized === false);
            const firstWidget = panel.widgets.length > 0 ? stateDraft.widgets[panel.widgets[0]] : undefined;
            // istanbul ignore next
            if (!expanded && firstWidget) {
                firstWidget.minimized = false;
            }
        }
        stateDraft.panels.left.collapsed = isPanelCollapsed([
            (_a = frontstageDef.centerLeft) === null || _a === void 0 ? void 0 : _a.zoneState,
            (_b = frontstageDef.bottomLeft) === null || _b === void 0 ? void 0 : _b.zoneState,
        ], [(_c = frontstageDef.leftPanel) === null || _c === void 0 ? void 0 : _c.panelState]);
        stateDraft.panels.right.collapsed = isPanelCollapsed([
            (_d = frontstageDef.centerRight) === null || _d === void 0 ? void 0 : _d.zoneState,
            (_e = frontstageDef.bottomRight) === null || _e === void 0 ? void 0 : _e.zoneState,
        ], [(_f = frontstageDef.rightPanel) === null || _f === void 0 ? void 0 : _f.panelState]);
        stateDraft.panels.top.collapsed = isPanelCollapsed([], [
            // istanbul ignore next - ignore topMostPanel if topPanel is defined
            frontstageDef.topPanel ? (_g = frontstageDef.topPanel) === null || _g === void 0 ? void 0 : _g.panelState : (_h = frontstageDef.topMostPanel) === null || _h === void 0 ? void 0 : _h.panelState, // eslint-disable-line deprecation/deprecation
        ]);
        stateDraft.panels.bottom.collapsed = isPanelCollapsed([], [
            // istanbul ignore next - ignore bottomMostPanel if bottomPanel is defined
            frontstageDef.bottomPanel ? (_j = frontstageDef.bottomPanel) === null || _j === void 0 ? void 0 : _j.panelState : (_k = frontstageDef.bottomMostPanel) === null || _k === void 0 ? void 0 : _k.panelState, // eslint-disable-line deprecation/deprecation
        ]);
        const topCenterDef = frontstageDef.topCenter;
        const toolSettingsWidgetDef = topCenterDef === null || topCenterDef === void 0 ? void 0 : topCenterDef.getSingleWidgetDef();
        if (toolSettingsWidgetDef) {
            const toolSettingsTab = stateDraft.tabs[toolSettingsTabId];
            toolSettingsTab.preferredPanelWidgetSize = toolSettingsWidgetDef.preferredPanelSize;
        }
    });
    nineZone = addMissingWidgets(frontstageDef, nineZone);
    nineZone = hideWidgets(nineZone, frontstageDef);
    nineZone = processPopoutWidgets(nineZone, frontstageDef);
    return nineZone;
}
/** Converts from saved NineZoneState to NineZoneState.
 * @note Restores toolSettings tab.
 * @note Restores tab labels.
 * @internal
 */
export function restoreNineZoneState(frontstageDef, saved) {
    let restored = {
        ...saved,
        tabs: defaultNineZone.tabs,
    };
    for (const [, tab] of Object.entries(saved.tabs)) {
        // Re-add the placeholder tab in order to correctly remove it from the state if WidgetDef is not found.
        restored = addTab(restored, tab.id);
        const widgetDef = frontstageDef.findWidgetDef(tab.id);
        if (!widgetDef) {
            Logger.logInfo(UiFramework.loggerCategory(restoreNineZoneState), "WidgetDef is not found for saved tab.", () => ({
                frontstageId: frontstageDef.id,
                tabId: tab.id,
            }));
            restored = removeTab(restored, tab.id);
            continue;
        }
        restored = produce(restored, (draft) => {
            draft.tabs[tab.id] = {
                ...tab,
                label: getWidgetLabel(widgetDef.label),
                hideWithUiWhenFloating: widgetDef.hideWithUiWhenFloating,
                iconSpec: widgetDef.iconSpec,
                canPopout: widgetDef.canPopout,
                isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
                allowedPanelTargets: widgetDef.allowedPanelTargets,
            };
        });
    }
    restored = produce(restored, (draft) => {
        // remove center panel section if one is found in left or right panels
        const oldLeftMiddleIndex = saved.panels.left.widgets.findIndex((value) => value === "leftMiddle");
        // istanbul ignore next
        if (-1 !== oldLeftMiddleIndex) {
            draft.panels.left.widgets = saved.panels.left.widgets.filter((value) => value !== "leftMiddle");
            if ("leftEnd" in draft.widgets) {
                draft.widgets.leftMiddle.tabs.forEach((tab) => draft.widgets.leftEnd.tabs.push(tab));
            }
            else {
                draft.widgets.leftEnd = { ...draft.widgets.leftMiddle };
                delete draft.widgets.leftMiddle;
            }
        }
        const oldRightMiddleIndex = saved.panels.right.widgets.findIndex((value) => value === "rightMiddle");
        // istanbul ignore next
        if (-1 !== oldRightMiddleIndex) {
            draft.panels.right.widgets = saved.panels.right.widgets.filter((value) => value !== "rightMiddle");
            if ("rightEnd" in draft.widgets) {
                draft.widgets.rightMiddle.tabs.forEach((tab) => draft.widgets.rightEnd.tabs.push(tab));
            }
            else {
                draft.widgets.rightEnd = { ...draft.widgets.rightMiddle };
                delete draft.widgets.rightMiddle;
            }
        }
        return;
    });
    if (InternalFrontstageManager.nineZoneSize && (0 !== InternalFrontstageManager.nineZoneSize.height || 0 !== InternalFrontstageManager.nineZoneSize.width)) {
        restored = FrameworkStateReducer(restored, {
            type: "RESIZE",
            size: {
                height: InternalFrontstageManager.nineZoneSize.height,
                width: InternalFrontstageManager.nineZoneSize.width,
            },
        }, frontstageDef);
    }
    return restored;
}
/** Prepares NineZoneState to be saved.
 * @note Removes toolSettings tab.
 * @note Removes tab labels and iconSpec.
 * @internal
 */
export function packNineZoneState(state) {
    let packed = {
        ...state,
        tabs: {},
    };
    packed = produce(packed, (draft) => {
        for (const [, tab] of Object.entries(state.tabs)) {
            if (tab.id === toolSettingsTabId)
                continue;
            draft.tabs[tab.id] = {
                id: tab.id,
                preferredFloatingWidgetSize: tab.preferredFloatingWidgetSize,
                allowedPanelTargets: tab.allowedPanelTargets,
                userSized: tab.userSized,
            };
        }
    });
    return packed;
}
// istanbul ignore next
function packSavedWidgets(frontstageDef) {
    let savedWidgets = [];
    for (const widgetDef of frontstageDef.widgetDefs) {
        const id = widgetDef.id;
        const initialWidget = {
            id,
        };
        const widget = produce(initialWidget, (draft) => {
            if (widgetDef.tabLocation) {
                draft.tabLocation = { ...widgetDef.tabLocation };
            }
            if (widgetDef.popoutBounds) {
                draft.popoutBounds = widgetDef.popoutBounds.toProps();
            }
        });
        if (widget === initialWidget)
            continue;
        savedWidgets.push(widget);
    }
    // Add previously saved widgets.
    const prevSavedWidgets = frontstageDef.savedWidgetDefs || [];
    for (const prevWidget of prevSavedWidgets) {
        if (savedWidgets.find((w) => w.id === prevWidget.id))
            continue;
        savedWidgets.push(prevWidget);
    }
    // Limit number of saved widgets.
    const maxWidgets = 100;
    if (savedWidgets.length > maxWidgets)
        savedWidgets = savedWidgets.slice(0, maxWidgets);
    return savedWidgets;
}
// istanbul ignore next
function restoreSavedWidgets(frontstage) {
    const savedWidgets = frontstage.savedWidgetDefs || [];
    for (const savedWidget of savedWidgets) {
        const widgetId = savedWidget.id;
        const widgetDef = frontstage.findWidgetDef(widgetId);
        if (!widgetDef)
            continue;
        if (savedWidget.tabLocation) {
            widgetDef.tabLocation = { ...savedWidget.tabLocation };
        }
        if (savedWidget.popoutBounds) {
            widgetDef.popoutBounds = Rectangle.create(savedWidget.popoutBounds);
        }
    }
}
/** @internal */
export function isPanelCollapsed(zoneStates, panelStates) {
    const openZone = zoneStates.find((zoneState) => zoneState === ZoneState.Open);
    const openPanel = panelStates.find((panelState) => panelState === StagePanelState.Open);
    return !openZone && !openPanel;
}
function addHiddenWidget(state, widgetDef) {
    const tabLocation = widgetDef.tabLocation || widgetDef.defaultTabLocation;
    const tabId = widgetDef.id;
    const { widgetId, tabIndex } = tabLocation;
    if (widgetId in state.widgets) {
        // Add to an existing widget (by widget id).
        return insertTabToWidget(state, tabId, widgetId, tabIndex);
    }
    else if (tabLocation.floatingWidget) {
        const floatingWidget = tabLocation.floatingWidget;
        assert(widgetId === floatingWidget.id);
        // Add to a new floating widget.
        const nzBounds = Rectangle.createFromSize(state.size);
        const bounds = Rectangle.create(floatingWidget.bounds).containIn(nzBounds);
        return addFloatingWidget(state, floatingWidget.id, [tabId], {
            ...floatingWidget,
            bounds: bounds.toProps(),
        });
    }
    // Add to a panel section.
    const panel = state.panels[tabLocation.side];
    // Add to existing panel section.
    if (panel.widgets.length >= panel.maxWidgetCount) {
        const sectionIndex = Math.min(panel.maxWidgetCount - 1, tabLocation.widgetIndex);
        const sectionId = panel.widgets[sectionIndex];
        return insertTabToWidget(state, tabId, sectionId, tabLocation.tabIndex);
    }
    // Create a new panel section.
    const newSectionId = getUniqueId();
    return insertPanelWidget(state, panel.side, newSectionId, [tabId], tabLocation.widgetIndex);
}
/** @internal */
export function setWidgetState(state, widgetDef, widgetState) {
    var _a, _b;
    if (widgetState === WidgetState.Hidden) {
        return hideWidget(state, widgetDef);
    }
    else if (widgetState === WidgetState.Open) {
        const id = widgetDef.id;
        let location = getTabLocation(state, id);
        if (!location) {
            state = addHiddenWidget(state, widgetDef);
            location = getTabLocation(state, id);
        }
        return produce(state, (draft) => {
            var _a;
            assert(!!location);
            const widget = draft.widgets[location.widgetId];
            widget.minimized = false;
            widget.activeTabId = id;
            if (isFloatingTabLocation(location)) {
                const floatingWidget = draft.floatingWidgets.byId[location.floatingWidgetId];
                floatingWidget.hidden = false;
                // istanbul ignore else
            }
            else if (isPanelTabLocation(location)) {
                const panel = draft.panels[location.side];
                panel.collapsed = false;
                if (undefined === panel.size || 0 === panel.size) {
                    // istanbul ignore next
                    panel.size = (_a = panel.minSize) !== null && _a !== void 0 ? _a : 200;
                }
            }
        });
    }
    else if (widgetState === WidgetState.Closed) {
        const id = widgetDef.id;
        let location = getTabLocation(state, id);
        if (!location) {
            state = addHiddenWidget(state, widgetDef);
            location = getTabLocation(state, id);
        }
        // TODO: should change activeTabId of a widget with multiple tabs.
        return produce(state, (draft) => {
            assert(!!location);
            const widget = draft.widgets[location.widgetId];
            if (isFloatingTabLocation(location)) {
                if (id === widget.activeTabId) {
                    widget.minimized = true;
                }
            }
        });
    }
    else if (widgetState === WidgetState.Floating) {
        const id = widgetDef.id;
        let location = getTabLocation(state, id);
        if (!location) {
            state = addHiddenWidget(state, widgetDef);
            location = getTabLocation(state, id);
        }
        assert(!!location);
        if (isFloatingTabLocation(location))
            return state;
        // Widget is not floating.
        state = removeTabFromWidget(state, id);
        const bounds = (_b = (_a = widgetDef.tabLocation) === null || _a === void 0 ? void 0 : _a.floatingWidget) === null || _b === void 0 ? void 0 : _b.bounds;
        state = addFloatingWidget(state, getUniqueId(), [id], { bounds });
    }
    return state;
}
/** Stores widget location and hides it in the UI. */
function hideWidget(state, widgetDef) {
    const location = getTabLocation(state, widgetDef.id);
    if (!location)
        return state;
    // istanbul ignore else
    if (isFloatingTabLocation(location)) {
        const floatingWidget = state.floatingWidgets.byId[location.widgetId];
        widgetDef.setFloatingContainerId(location.floatingWidgetId);
        widgetDef.tabLocation = {
            side: floatingWidget.home.side,
            tabIndex: floatingWidget.home.widgetIndex,
            widgetId: floatingWidget.id,
            widgetIndex: floatingWidget.home.widgetIndex,
            floatingWidget,
        };
    }
    else if (!isPopoutTabLocation(location)) {
        const widgetId = location.widgetId;
        const side = isPanelTabLocation(location) ? location.side : "left";
        const widgetIndex = isPanelTabLocation(location) ? state.panels[side].widgets.indexOf(widgetId) : 0;
        const tabIndex = state.widgets[location.widgetId].tabs.indexOf(widgetDef.id);
        widgetDef.tabLocation = {
            side,
            tabIndex,
            widgetId,
            widgetIndex,
        };
    }
    return removeTabFromWidget(state, widgetDef.id);
}
/** @internal */
export function showWidget(state, id) {
    const location = getTabLocation(state, id);
    if (!location)
        return state;
    state = produce(state, (draft) => {
        const widget = draft.widgets[location.widgetId];
        widget.activeTabId = id;
        widget.minimized = false;
        if (isPanelTabLocation(location)) {
            const panel = draft.panels[location.side];
            panel.collapsed = false;
        }
    });
    if (isFloatingTabLocation(location)) {
        state = floatingWidgetBringToFront(state, location.floatingWidgetId);
    }
    return state;
}
/** @internal */
export function expandWidget(state, id) {
    const location = getTabLocation(state, id);
    if (!location)
        return state;
    return produce(state, (draft) => {
        const widget = draft.widgets[location.widgetId];
        if (isPanelTabLocation(location)) {
            const panel = draft.panels[location.side];
            panel.widgets.forEach((wId) => {
                const w = draft.widgets[wId];
                w.minimized = true;
            });
        }
        widget.minimized = false;
    });
}
/** @internal */
export function setWidgetLabel(state, id, label) {
    if (!(id in state.tabs))
        return state;
    return produce(state, (draft) => {
        const tab = draft.tabs[id];
        tab.label = label;
    });
}
/** @internal */
export function useSavedFrontstageState(frontstageDef) {
    const uiStateStorage = useUiStateStorageHandler();
    const uiStateStorageRef = React.useRef(uiStateStorage);
    const isMountedRef = React.useRef(false);
    React.useEffect(() => {
        isMountedRef.current = true;
        return () => { isMountedRef.current = false; };
    }, []);
    React.useEffect(() => {
        uiStateStorageRef.current = uiStateStorage;
    }, [uiStateStorage]);
    React.useEffect(() => {
        async function fetchFrontstageState() {
            if (frontstageDef.nineZoneState) {
                frontstageDef.nineZoneState = processPopoutWidgets(frontstageDef.nineZoneState, frontstageDef);
                return;
            }
            const id = frontstageDef.id;
            const version = frontstageDef.version;
            const settingResult = await uiStateStorageRef.current.getSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id));
            if (!isMountedRef.current)
                return;
            if (isFrontstageStateSettingResult(settingResult)) {
                const setting = settingResult.setting;
                if (setting.version >= version && setting.stateVersion >= stateVersion) {
                    const restored = restoreNineZoneState(frontstageDef, setting.nineZone);
                    frontstageDef.savedWidgetDefs = setting.widgets;
                    restoreSavedWidgets(frontstageDef);
                    let state = addMissingWidgets(frontstageDef, restored);
                    state = hideWidgets(state, frontstageDef);
                    state = processPopoutWidgets(state, frontstageDef);
                    frontstageDef.nineZoneState = state;
                    return;
                }
            }
            frontstageDef.nineZoneState = initializeNineZoneState(frontstageDef);
        }
        fetchFrontstageState(); // eslint-disable-line @typescript-eslint/no-floating-promises
    }, [frontstageDef]);
}
/** @internal */
export function useSaveFrontstageSettings(frontstageDef) {
    const nineZone = useNineZoneState(frontstageDef);
    const uiSettingsStorage = useUiStateStorageHandler();
    const saveSetting = React.useMemo(() => {
        return debounce(async (frontstage, state) => {
            const id = frontstage.id;
            const widgets = packSavedWidgets(frontstage);
            frontstage.savedWidgetDefs = widgets;
            const setting = {
                id,
                version: frontstage.version,
                stateVersion,
                nineZone: packNineZoneState(state),
                widgets,
            };
            await uiSettingsStorage.saveSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id), setting);
        }, 1000);
    }, [uiSettingsStorage]);
    React.useEffect(() => {
        return () => {
            saveSetting.cancel();
        };
    }, [saveSetting]);
    React.useEffect(() => {
        if (!nineZone || nineZone.draggedTab)
            return;
        saveSetting(frontstageDef, nineZone);
    }, [frontstageDef, nineZone, saveSetting]);
}
/** @internal */
export const FRONTSTAGE_SETTINGS_NAMESPACE = "uifw-frontstageSettings";
/** @internal */
export function getFrontstageStateSettingName(frontstageId) {
    return `frontstageState[${frontstageId}]`;
}
// istanbul ignore next
function debounce(func, duration) {
    let timeout;
    const debounced = (...args) => {
        const handler = () => {
            timeout = undefined;
            return func(...args);
        };
        window.clearTimeout(timeout);
        timeout = window.setTimeout(handler, duration);
    };
    debounced.cancel = () => {
        window.clearTimeout(timeout);
    };
    return debounced;
}
const createListener = (frontstageDef, listener) => {
    return (...args) => {
        if (!frontstageDef.nineZoneState)
            return;
        listener(...args);
    };
};
/** @internal */
export function useFrontstageManager(frontstageDef, useToolAsToolSettingsLabel) {
    React.useEffect(() => {
        const listener = createListener(frontstageDef, ({ widgetDef, widgetState }) => {
            assert(!!frontstageDef.nineZoneState);
            frontstageDef.nineZoneState = setWidgetState(frontstageDef.nineZoneState, widgetDef, widgetState);
        });
        UiFramework.frontstages.onWidgetStateChangedEvent.addListener(listener);
        return () => {
            UiFramework.frontstages.onWidgetStateChangedEvent.removeListener(listener);
        };
    }, [frontstageDef]);
    React.useEffect(() => {
        const listener = createListener(frontstageDef, ({ widgetDef }) => {
            assert(!!frontstageDef.nineZoneState);
            frontstageDef.nineZoneState = showWidget(frontstageDef.nineZoneState, widgetDef.id);
        });
        InternalFrontstageManager.onWidgetShowEvent.addListener(listener);
        return () => {
            InternalFrontstageManager.onWidgetShowEvent.removeListener(listener);
        };
    }, [frontstageDef]);
    React.useEffect(() => {
        const listener = createListener(frontstageDef, ({ widgetDef }) => {
            assert(!!frontstageDef.nineZoneState);
            let nineZoneState = showWidget(frontstageDef.nineZoneState, widgetDef.id);
            nineZoneState = expandWidget(nineZoneState, widgetDef.id);
            frontstageDef.nineZoneState = nineZoneState;
        });
        InternalFrontstageManager.onWidgetExpandEvent.addListener(listener);
        return () => {
            InternalFrontstageManager.onWidgetExpandEvent.removeListener(listener);
        };
    }, [frontstageDef]);
    const uiSettingsStorage = useUiStateStorageHandler();
    React.useEffect(() => {
        const listener = (args) => {
            // TODO: track restoring frontstages to support workflows:  i.e. prevent loading frontstage OR saving layout when delete is pending
            uiSettingsStorage.deleteSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(args.frontstageDef.id)); // eslint-disable-line @typescript-eslint/no-floating-promises
            if (frontstageDef.id === args.frontstageDef.id) {
                args.frontstageDef.nineZoneState = initializeNineZoneState(frontstageDef);
            }
            else {
                args.frontstageDef.nineZoneState = undefined;
            }
        };
        InternalFrontstageManager.onFrontstageRestoreLayoutEvent.addListener(listener);
        return () => {
            InternalFrontstageManager.onFrontstageRestoreLayoutEvent.removeListener(listener);
        };
    }, [uiSettingsStorage, frontstageDef]);
    React.useEffect(() => {
        const listener = createListener(frontstageDef, ({ widgetDef }) => {
            assert(!!frontstageDef.nineZoneState);
            const label = widgetDef.label;
            frontstageDef.nineZoneState = setWidgetLabel(frontstageDef.nineZoneState, widgetDef.id, label);
        });
        InternalFrontstageManager.onWidgetLabelChangedEvent.addListener(listener);
        return () => {
            InternalFrontstageManager.onWidgetLabelChangedEvent.removeListener(listener);
        };
    }, [frontstageDef]);
    const defaultLabel = React.useMemo(() => UiFramework.translate("widget.labels.toolSettings"), []);
    React.useEffect(() => {
        const updateLabel = createListener(frontstageDef, () => {
            var _a;
            const toolId = UiFramework.frontstages.activeToolId;
            assert(!!frontstageDef.nineZoneState);
            const label = useToolAsToolSettingsLabel ?
                ((_a = IModelApp.tools.find(toolId)) === null || _a === void 0 ? void 0 : _a.flyover) || defaultLabel : defaultLabel;
            frontstageDef.nineZoneState = setWidgetLabel(frontstageDef.nineZoneState, toolSettingsTabId, label);
        });
        // Whenever the frontstageDef or the useTool... changes, keep the label up to date.
        updateLabel();
        // If useTool... is true, listen for events to keep up the label up to date.
        if (useToolAsToolSettingsLabel) {
            UiFramework.frontstages.onFrontstageReadyEvent.addListener(updateLabel);
            InternalFrontstageManager.onFrontstageRestoreLayoutEvent.addListener(updateLabel);
            UiFramework.frontstages.onToolActivatedEvent.addListener(updateLabel);
            UiFramework.frontstages.onToolSettingsReloadEvent.addListener(updateLabel);
        }
        return () => {
            if (useToolAsToolSettingsLabel) {
                UiFramework.frontstages.onFrontstageReadyEvent.removeListener(updateLabel);
                InternalFrontstageManager.onFrontstageRestoreLayoutEvent.removeListener(updateLabel);
                UiFramework.frontstages.onToolActivatedEvent.removeListener(updateLabel);
                UiFramework.frontstages.onToolSettingsReloadEvent.removeListener(updateLabel);
            }
        };
    }, [frontstageDef, useToolAsToolSettingsLabel, defaultLabel]);
}
/** @internal */
// istanbul ignore next
export function useItemsManager(frontstageDef) {
    const refreshNineZoneState = (def) => {
        // Fired for both registered/unregistered. Update definitions and remove/add missing widgets.
        def.updateWidgetDefs();
        restoreSavedWidgets(def);
        let state = def.nineZoneState;
        if (!state)
            return;
        state = addMissingWidgets(def, state);
        state = removeMissingWidgets(def, state);
        state = hideWidgets(state, def); // TODO: should only apply to widgets provided by registered provider in onUiProviderRegisteredEvent.
        def.nineZoneState = state;
    };
    React.useEffect(() => {
        const remove = UiItemsManager.onUiProviderRegisteredEvent.addListener(() => {
            refreshNineZoneState(frontstageDef);
        });
        // Need to refresh anytime frontstageDef changes because uiItemsProvider may have added something to
        // another stage before it was possibly unloaded in this stage.
        refreshNineZoneState(frontstageDef);
        return remove;
    }, [frontstageDef]);
}
// istanbul ignore next
function determineNewWidgets(defs, state) {
    return (defs || []).filter((def) => !(def.id in state.tabs));
}
//# sourceMappingURL=Frontstage.js.map