"use strict";
/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Base
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.floatingWidgetBringToFront = exports.addPopoutWidget = exports.addFloatingWidget = exports.getNewFloatingWidgetBounds = exports.setWidgetActiveTabId = exports.getWidgetState = exports.assertWidgetState = exports.removePanelWidget = exports.removePopoutWidget = exports.removeFloatingWidget = exports.updateFloatingWidgetState = exports.createPopoutWidgetState = exports.createFloatingWidgetState = exports.removeWidgetState = exports.removeWidget = exports.addWidgetState = exports.updateWidgetState = exports.createWidgetState = void 0;
const immer_1 = require("immer");
const appui_abstract_1 = require("@itwin/appui-abstract");
const core_bentley_1 = require("@itwin/core-bentley");
const core_react_1 = require("@itwin/core-react");
const TabLocation_1 = require("../TabLocation");
const WidgetLocation_1 = require("../WidgetLocation");
const NineZoneStateHelpers_1 = require("./NineZoneStateHelpers");
const PanelStateHelpers_1 = require("./PanelStateHelpers");
const TabStateHelpers_1 = require("./TabStateHelpers");
/** @internal */
function createWidgetState(id, tabs, args) {
    if (tabs.length === 0)
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Widget must contain tabs");
    return {
        activeTabId: tabs[0],
        minimized: false,
        ...args,
        id,
        tabs,
    };
}
exports.createWidgetState = createWidgetState;
/** @internal */
function updateWidgetState(state, id, args) {
    return (0, immer_1.produce)(state, (draft) => {
        const widget = getWidgetState(draft, id);
        draft.widgets[id] = {
            ...widget,
            ...(0, immer_1.castDraft)(args),
        };
    });
}
exports.updateWidgetState = updateWidgetState;
/** @internal */
function addWidgetState(state, id, tabs, args) {
    // eslint-disable-next-line deprecation/deprecation
    if (id in state.widgets)
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Widget already exists");
    const widget = createWidgetState(id, tabs, args);
    for (const tabId of widget.tabs) {
        if (!(tabId in state.tabs))
            // eslint-disable-next-line deprecation/deprecation
            throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Tab does not exist", undefined, () => ({
                tabId,
            }));
        const location = (0, TabLocation_1.getTabLocation)(state, tabId);
        if (location)
            // eslint-disable-next-line deprecation/deprecation
            throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Tab is already in a widget", undefined, () => ({ tabId, widgetId: location.widgetId }));
    }
    return (0, immer_1.produce)(state, (draft) => {
        draft.widgets[id] = (0, immer_1.castDraft)(widget);
    });
}
exports.addWidgetState = addWidgetState;
/** @internal */
function removeWidget(state, id) {
    const location = (0, WidgetLocation_1.getWidgetLocation)(state, id);
    // eslint-disable-next-line deprecation/deprecation
    if (!location)
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Widget not found");
    if ((0, WidgetLocation_1.isFloatingWidgetLocation)(location))
        return removeFloatingWidget(state, id);
    if ((0, WidgetLocation_1.isPopoutWidgetLocation)(location))
        return removePopoutWidget(state, id);
    return removePanelWidget(state, id, location);
}
exports.removeWidget = removeWidget;
/** @internal */
function removeWidgetState(state, id) {
    assertWidgetState(state, id);
    return (0, immer_1.produce)(state, (draft) => {
        delete draft.widgets[id];
    });
}
exports.removeWidgetState = removeWidgetState;
/** @internal */
function createFloatingWidgetState(id, args) {
    const bounds = (0, NineZoneStateHelpers_1.toRectangleProps)(args?.bounds);
    return {
        home: {
            side: "left",
            widgetId: "",
            widgetIndex: 0,
        },
        ...args,
        bounds,
        id,
    };
}
exports.createFloatingWidgetState = createFloatingWidgetState;
/** @internal */
function createPopoutWidgetState(id, args) {
    const bounds = (0, NineZoneStateHelpers_1.toRectangleProps)(args?.bounds);
    const home = args?.home
        ? args.home
        : {
            side: "left",
            widgetId: "",
            widgetIndex: 0,
        };
    return {
        ...args,
        home,
        bounds,
        id,
    };
}
exports.createPopoutWidgetState = createPopoutWidgetState;
/** @internal */
function updateFloatingWidgetState(state, id, args) {
    if (!(id in state.floatingWidgets.byId))
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Floating widget does not exist");
    return (0, immer_1.produce)(state, (draft) => {
        const floatingWidget = draft.floatingWidgets.byId[id];
        const { bounds, ...other } = args;
        draft.floatingWidgets.byId[id] = {
            ...floatingWidget,
            ...other,
        };
        if (bounds)
            (0, NineZoneStateHelpers_1.setRectangleProps)(floatingWidget.bounds, bounds);
    });
}
exports.updateFloatingWidgetState = updateFloatingWidgetState;
/** Removes floating widget from the UI and deletes the widget state.
 * @internal
 */
function removeFloatingWidget(state, id) {
    if (!(id in state.floatingWidgets.byId))
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Floating widget does not exist");
    state = (0, immer_1.produce)(state, (draft) => {
        delete draft.floatingWidgets.byId[id];
        const idIndex = draft.floatingWidgets.allIds.indexOf(id);
        draft.floatingWidgets.allIds.splice(idIndex, 1);
    });
    return removeWidgetState(state, id);
}
exports.removeFloatingWidget = removeFloatingWidget;
/** Removes popout widget from the UI and deletes the widget state.
 * @internal
 */
function removePopoutWidget(state, id) {
    if (!(id in state.popoutWidgets.byId))
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Popout widget does not exist");
    state = (0, immer_1.produce)(state, (draft) => {
        delete draft.popoutWidgets.byId[id];
        const index = state.popoutWidgets.allIds.indexOf(id);
        draft.popoutWidgets.allIds.splice(index, 1);
    });
    return removeWidgetState(state, id);
}
exports.removePopoutWidget = removePopoutWidget;
/** @internal */
function removePanelWidget(state, id, location) {
    location = location || findPanelWidget(state, id);
    // eslint-disable-next-line deprecation/deprecation
    if (!location)
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Panel widget not found");
    state = (0, PanelStateHelpers_1.updatePanelState)(state, location.side, (draft) => {
        (0, core_bentley_1.assert)(!!location);
        draft.widgets.splice(location.index, 1);
    });
    const widgets = state.panels[location.side].widgets;
    const expandedWidget = widgets.find((widgetId) => {
        const widget = getWidgetState(state, widgetId);
        return !widget.minimized;
    });
    if (!expandedWidget && widgets.length > 0) {
        const firstWidgetId = widgets[0];
        state = updateWidgetState(state, firstWidgetId, {
            minimized: false,
        });
    }
    return removeWidgetState(state, id);
}
exports.removePanelWidget = removePanelWidget;
function findPanelWidget(state, id) {
    const location = (0, WidgetLocation_1.getWidgetLocation)(state, id);
    if (location && (0, WidgetLocation_1.isPanelWidgetLocation)(location))
        return location;
    return undefined;
}
/** @internal */
function assertWidgetState(state, id) {
    if (!(id in state.widgets))
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Widget does not exist", undefined, () => ({
            id,
        }));
}
exports.assertWidgetState = assertWidgetState;
/** @internal */
function getWidgetState(state, id) {
    assertWidgetState(state, id);
    return state.widgets[id];
}
exports.getWidgetState = getWidgetState;
/** @internal */
function setWidgetActiveTabId(state, widgetId, tabId) {
    const widget = getWidgetState(state, widgetId);
    if (!widget.tabs.includes(tabId))
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Tab is not in a widget");
    state = updateWidgetState(state, widgetId, {
        activeTabId: tabId,
    });
    const floatingWidget = state.floatingWidgets.byId[widgetId];
    if (floatingWidget) {
        const activeTab = state.tabs[tabId];
        const preferredFloatingWidgetSize = core_react_1.Rectangle.create(floatingWidget.bounds).getSize();
        state = (0, TabStateHelpers_1.updateTabState)(state, activeTab.id, (draft) => {
            (0, NineZoneStateHelpers_1.initSizeProps)(draft, "preferredFloatingWidgetSize", preferredFloatingWidgetSize);
        });
    }
    return state;
}
exports.setWidgetActiveTabId = setWidgetActiveTabId;
/** @internal */
function getNewFloatingWidgetBounds(state) {
    // Matches min size (to handle auto-sized floating widgets correctly).
    const size = { height: 120, width: 200 };
    const initialPosition = new core_react_1.Point(360, 340);
    const nzBounds = core_react_1.Rectangle.createFromSize(state.size);
    const widgetsBounds = nzBounds.inset(20, 20, 20, 20);
    const offset = new core_react_1.Point(40, 40);
    let bounds = core_react_1.Rectangle.createFromSize(size);
    if (state.floatingWidgets.allIds.length === 0) {
        // Initial floating widget position.
        bounds = bounds.offset(initialPosition);
    }
    else {
        // Position is relative to last floating widget if available.
        const widgetId = state.floatingWidgets.allIds[state.floatingWidgets.allIds.length - 1];
        const widget = state.floatingWidgets.byId[widgetId];
        const widgetBounds = core_react_1.Rectangle.create(widget.bounds);
        // Bounds relative to top left of a last floating widget.
        const topLeft = widgetBounds.topLeft().offset(offset);
        bounds = bounds.offset(topLeft);
        // Bottom right of new bounds should also be outside of a floating widget.
        const widgetBottomRight = new core_react_1.Point(widgetBounds.right, widgetBounds.bottom);
        const minBottomRight = widgetBottomRight.offset(offset);
        const x = Math.max(0, minBottomRight.x - bounds.right);
        const y = Math.max(0, minBottomRight.y - bounds.bottom);
        bounds = bounds.offset({ x, y });
    }
    // TODO: might still end up with a bunch of overlapping widgets.
    if (bounds.bottom >= widgetsBounds.bottom) {
        bounds = bounds.setPosition({ x: bounds.left, y: widgetsBounds.top });
    }
    if (bounds.right >= widgetsBounds.right) {
        bounds = bounds.setPosition({
            x: widgetsBounds.left,
            y: widgetsBounds.top,
        });
    }
    bounds = bounds.containIn(widgetsBounds);
    return bounds.toProps();
}
exports.getNewFloatingWidgetBounds = getNewFloatingWidgetBounds;
/** @internal */
function addFloatingWidget(state, id, tabs, floatingWidgetArgs, widgetArgs) {
    if (id in state.floatingWidgets.byId)
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Floating widget already exists");
    state = addWidgetState(state, id, tabs, widgetArgs);
    let bounds = floatingWidgetArgs?.bounds;
    if (!bounds) {
        bounds = getNewFloatingWidgetBounds(state);
    }
    const tabId = tabs[0];
    const tab = state.tabs[tabId];
    const resizable = tab.isFloatingWidgetResizable;
    const floatingWidget = createFloatingWidgetState(id, {
        bounds,
        resizable,
        ...floatingWidgetArgs,
    });
    return (0, immer_1.produce)(state, (stateDraft) => {
        stateDraft.floatingWidgets.byId[id] = floatingWidget;
        stateDraft.floatingWidgets.allIds.push(id);
    });
}
exports.addFloatingWidget = addFloatingWidget;
/** @internal */
function addPopoutWidget(state, id, tabs, popoutWidgetArgs, widgetArgs) {
    if (tabs.length !== 1)
        // eslint-disable-next-line deprecation/deprecation
        throw new appui_abstract_1.UiError(NineZoneStateHelpers_1.category, "Popout widget should contain one tab only", undefined, () => ({ tabs }));
    const popoutWidget = createPopoutWidgetState(id, popoutWidgetArgs);
    state = addWidgetState(state, id, tabs, widgetArgs);
    return (0, immer_1.produce)(state, (stateDraft) => {
        stateDraft.popoutWidgets.byId[id] = popoutWidget;
        stateDraft.popoutWidgets.allIds.push(id);
    });
}
exports.addPopoutWidget = addPopoutWidget;
/** @internal */
function floatingWidgetBringToFront(state, floatingWidgetId) {
    return (0, immer_1.produce)(state, (draft) => {
        const idIndex = draft.floatingWidgets.allIds.indexOf(floatingWidgetId);
        const spliced = draft.floatingWidgets.allIds.splice(idIndex, 1);
        draft.floatingWidgets.allIds.push(spliced[0]);
    });
}
exports.floatingWidgetBringToFront = floatingWidgetBringToFront;
//# sourceMappingURL=WidgetStateHelpers.js.map