/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Toolbar
 */
import * as React from "react";
import { Logger } from "@itwin/core-bentley";
import { ConditionalBooleanValue, ToolbarItemsManager, ToolbarItemUtilities, ToolbarOrientation, ToolbarUsage, } from "@itwin/appui-abstract";
import { Orientation } from "@itwin/core-react";
import { ToolbarOpacitySetting, ToolbarWithOverflow } from "@itwin/components-react";
import { Direction, Toolbar, ToolbarPanelAlignment } from "@itwin/appui-layout-react";
import { useFrameworkVersion } from "../hooks/useFrameworkVersion";
import { SyncUiEventDispatcher } from "../syncui/SyncUiEventDispatcher";
import { UiFramework } from "../UiFramework";
import { ToolbarDragInteractionContext } from "./DragInteraction";
import { ToolbarHelper } from "./ToolbarHelper";
import { useDefaultToolbarItems } from "./useDefaultToolbarItems";
import { useUiItemsProviderToolbarItems } from "./useUiItemsProviderToolbarItems";
/** Private function to set up sync event monitoring of toolbar items */
function useToolbarItemSyncEffect(uiDataProvider, syncIdsOfInterest) {
    React.useEffect(() => {
        const handleSyncUiEvent = (args) => {
            if (0 === syncIdsOfInterest.length)
                return;
            // istanbul ignore else
            if (syncIdsOfInterest.some((value) => args.eventIds.has(value.toLowerCase()))) {
                // process each item that has interest
                uiDataProvider.refreshAffectedItems(args.eventIds);
            }
        };
        SyncUiEventDispatcher.onSyncUiEvent.addListener(handleSyncUiEvent);
        return () => {
            SyncUiEventDispatcher.onSyncUiEvent.removeListener(handleSyncUiEvent);
        };
    }, [uiDataProvider, syncIdsOfInterest, uiDataProvider.items]);
    React.useEffect(() => {
        const handleToolActivatedEvent = ({ toolId }) => {
            uiDataProvider.setActiveToolId(toolId);
        };
        UiFramework.frontstages.onToolActivatedEvent.addListener(handleToolActivatedEvent);
        return () => {
            UiFramework.frontstages.onToolActivatedEvent.removeListener(handleToolActivatedEvent);
        };
    }, [uiDataProvider, uiDataProvider.items]);
}
function nestedAddItemToSpecifiedParentGroup(items, groupChildren) {
    const outItems = [];
    for (const toolbarItem of items) {
        if (!ToolbarItemUtilities.isGroupButton(toolbarItem)) {
            outItems.push(toolbarItem);
            continue;
        }
        const newChildren = nestedAddItemToSpecifiedParentGroup(toolbarItem.items, groupChildren);
        const foundIndices = [];
        groupChildren.forEach((entry, index) => {
            if (entry.parentToolGroupId === toolbarItem.id) {
                foundIndices.push(index);
            }
        });
        // istanbul ignore else
        if (foundIndices.length) {
            // process in reverse order so groupChildren can be reduced as we find matches
            foundIndices.sort(
            // istanbul ignore next
            (a, b) => a - b).reverse().forEach((foundIndex) => {
                newChildren.push(groupChildren[foundIndex]);
                groupChildren.splice(foundIndex);
            });
        }
        outItems.push({ ...toolbarItem, items: newChildren });
    }
    return outItems;
}
function addItemToSpecifiedParentGroup(items, groupChildren) {
    const outItems = [];
    for (const toolbarItem of items) {
        if (!ToolbarItemUtilities.isGroupButton(toolbarItem)) {
            outItems.push(toolbarItem);
            continue;
        }
        const newChildren = nestedAddItemToSpecifiedParentGroup(toolbarItem.items, groupChildren);
        const foundIndices = [];
        groupChildren.forEach((entry, index) => {
            if (entry.parentToolGroupId === toolbarItem.id) {
                foundIndices.push(index);
            }
        });
        // istanbul ignore else
        if (foundIndices.length) {
            // process in reverse order so groupChildren can be reduced as we find matches
            foundIndices.sort(
            // istanbul ignore next
            (a, b) => a - b).reverse().forEach((foundIndex) => {
                newChildren.push(groupChildren[foundIndex]);
                groupChildren.splice(foundIndex);
            });
        }
        outItems.push({ ...toolbarItem, items: newChildren });
    }
    return outItems;
}
function cloneGroup(inGroup) {
    const childItems = [];
    inGroup.items.forEach((item) => {
        if (ToolbarItemUtilities.isGroupButton(item))
            childItems.push(cloneGroup(item));
        else
            childItems.push(item);
    });
    const clonedGroup = { ...inGroup, items: childItems };
    return clonedGroup;
}
function getItemSortValue(item) {
    const groupValue = undefined === item.groupPriority ? 0 : /* istanbul ignore next */ item.groupPriority;
    return groupValue * 10000 + item.itemPriority;
}
function getSortedChildren(group) {
    const sortedChildren = group.items
        .filter((item) => !(ConditionalBooleanValue.getValue(item.isHidden)))
        .sort((a, b) => getItemSortValue(a) - getItemSortValue(b))
        .map((i) => {
        if (ToolbarItemUtilities.isGroupButton(i)) {
            return { ...i, items: getSortedChildren(i) };
        }
        return i;
    });
    return sortedChildren;
}
/** local function to combine items from Stage and from Extensions */
function combineItems(defaultItems, addonItems) {
    let items = [];
    const groupChildren = [];
    // istanbul ignore else
    if (defaultItems.length) {
        defaultItems.forEach((srcItem) => {
            if (-1 === items.findIndex((item) => item.id === srcItem.id)) {
                // if the default item is a group that an addon may insert into copy it so we don't mess with original
                const toolbarItem = ToolbarItemUtilities.isGroupButton(srcItem) ? cloneGroup(srcItem) : srcItem;
                if (toolbarItem.parentToolGroupId && (ToolbarItemUtilities.isGroupButton(toolbarItem) || ToolbarItemUtilities.isActionButton(toolbarItem)))
                    groupChildren.push(toolbarItem);
                else
                    items.push(toolbarItem);
            }
        });
    }
    // istanbul ignore else
    if (addonItems.length) {
        addonItems.forEach((srcItem) => {
            if (-1 === items.findIndex((item) => item.id === srcItem.id)) {
                // if the default item is a group that an addon may insert into copy it so we don't mess with original
                const toolbarItem = ToolbarItemUtilities.isGroupButton(srcItem) ? cloneGroup(srcItem) : srcItem;
                if (toolbarItem.parentToolGroupId && (ToolbarItemUtilities.isGroupButton(toolbarItem) || ToolbarItemUtilities.isActionButton(toolbarItem)))
                    groupChildren.push(toolbarItem);
                else
                    items.push(toolbarItem);
            }
        });
    }
    // if an item from an addon has specified a parent group then try to find it and insert it.  If no parent is found, add item at root level.
    if (groupChildren.length) {
        items = addItemToSpecifiedParentGroup(items, groupChildren);
        if (groupChildren.length) {
            groupChildren.forEach((toolbarItem) => {
                Logger.logWarning("ToolbarComposer", `Requested Parent Group [${toolbarItem.parentToolGroupId}] not found, so item [${toolbarItem.id}] is added directly to toolbar.`);
                items.push(toolbarItem);
            });
        }
    }
    const availableItems = items
        .filter((item) => !(ConditionalBooleanValue.getValue(item.isHidden)))
        .sort((a, b) => getItemSortValue(a) - getItemSortValue(b))
        .map((i) => {
        if (ToolbarItemUtilities.isGroupButton(i)) {
            return { ...i, items: getSortedChildren(i) };
        }
        return i;
    });
    return availableItems;
}
const useProximityOpacitySetting = () => {
    const [proximityOpacity, setProximityOpacity] = React.useState(UiFramework.visibility.useProximityOpacity);
    React.useEffect(() => {
        // istanbul ignore next
        const handleUiVisibilityChanged = () => {
            setProximityOpacity(UiFramework.visibility.useProximityOpacity);
        };
        UiFramework.onUiVisibilityChanged.addListener(handleUiVisibilityChanged);
        return () => {
            UiFramework.onUiVisibilityChanged.removeListener(handleUiVisibilityChanged);
        };
    }, []);
    return proximityOpacity;
};
/**
 * Toolbar that is populated and maintained by item managers.
 * @public
 */
export function ToolbarComposer(props) {
    const { usage, orientation } = props;
    const [defaultItemsManager, setDefaultItemsManager] = React.useState(() => new ToolbarItemsManager(props.items));
    React.useEffect(() => {
        setDefaultItemsManager(new ToolbarItemsManager(props.items));
    }, [props.items]); // eslint-disable-line react-hooks/exhaustive-deps
    // process default items
    const defaultItems = useDefaultToolbarItems(defaultItemsManager);
    const syncIdsOfInterest = React.useMemo(() => ToolbarItemsManager.getSyncIdsOfInterest(defaultItems), [defaultItems]);
    useToolbarItemSyncEffect(defaultItemsManager, syncIdsOfInterest);
    // process items from addon UI providers
    const [addonItemsManager] = React.useState(() => new ToolbarItemsManager());
    const addonItems = useUiItemsProviderToolbarItems(addonItemsManager, usage, orientation);
    const addonSyncIdsOfInterest = React.useMemo(() => ToolbarItemsManager.getSyncIdsOfInterest(addonItems), [addonItems]);
    useToolbarItemSyncEffect(addonItemsManager, addonSyncIdsOfInterest);
    const toolbarItems = React.useMemo(() => combineItems(defaultItems, addonItems), [defaultItems, addonItems]);
    const toolbarOrientation = orientation === ToolbarOrientation.Horizontal ? Orientation.Horizontal : Orientation.Vertical; // eslint-disable-line deprecation/deprecation
    // eslint-disable-next-line deprecation/deprecation
    const expandsTo = toolbarOrientation === Orientation.Horizontal ? Direction.Bottom : usage === ToolbarUsage.ViewNavigation ? Direction.Left : Direction.Right;
    // eslint-disable-next-line deprecation/deprecation
    const panelAlignment = (toolbarOrientation === Orientation.Horizontal && usage === ToolbarUsage.ViewNavigation) ? ToolbarPanelAlignment.End : ToolbarPanelAlignment.Start;
    const version = useFrameworkVersion(); // eslint-disable-line deprecation/deprecation
    const isDragEnabled = React.useContext(ToolbarDragInteractionContext);
    const useProximityOpacity = useProximityOpacitySetting();
    if ("1" === version) {
        return (React.createElement(ToolbarUi1, { items: toolbarItems, expandsTo: expandsTo, panelAlignment: panelAlignment }));
    }
    return React.createElement(ToolbarWithOverflow, { expandsTo: expandsTo, panelAlignment: panelAlignment, items: toolbarItems, useDragInteraction: isDragEnabled, toolbarOpacitySetting: useProximityOpacity && !UiFramework.isMobile() ? ToolbarOpacitySetting.Proximity : /* istanbul ignore next */ ToolbarOpacitySetting.Defaults });
}
/** Toolbar rendered in 1.0 mode.
 * @internal
 */
const ToolbarUi1 = React.memo(function ToolbarUi1({ items, expandsTo, panelAlignment, }) {
    const createReactNodes = () => {
        if (0 === items.length)
            return null;
        const createdNodes = items.map((item) => {
            return ToolbarHelper.createNodeForToolbarItem(item);
        });
        return createdNodes;
    };
    return React.createElement(Toolbar // eslint-disable-line deprecation/deprecation
    , { expandsTo: expandsTo, panelAlignment: panelAlignment, items: React.createElement(React.Fragment, null, createReactNodes()) });
});
//# sourceMappingURL=ToolbarComposer.js.map