/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Popup
 */
import * as React from "react";
import { PropertyRecord, PropertyValueFormat, UiEvent, } from "@itwin/appui-abstract";
import { Orientation } from "@itwin/components-react";
import { Logger } from "@itwin/core-bentley";
import { Rectangle } from "@itwin/core-react/internal";
import { offsetAndContainInContainer } from "../layout/popup/Tooltip.js";
import { UiFramework } from "../UiFramework.js";
import { CardPopup } from "./CardPopup.js";
import { HTMLElementPopup } from "./HTMLElementPopup.js";
import { InputEditorCommitHandler, InputEditorPopup, } from "./InputEditorPopup.js";
import { KeyinPalettePopup } from "./KeyinPalettePopup.js";
import { ToolbarPopup } from "./ToolbarPopup.js";
import { ToolSettingsPopup } from "./ToolSettingsPopup.js";
import { ComponentPopup } from "./ComponentPopup.js";
import { WrapperContext } from "../configurableui/ConfigurableUiContent.js";
import { mapToPlacement } from "../utils/Placement.js";
import { InternalConfigurableUiManager } from "../configurableui/InternalConfigurableUiManager.js";
/** Popups Changed Event class.
 * @public
 * @deprecated in 4.13.0. This class should not be used by applications to instantiate objects.
 */
// eslint-disable-next-line @typescript-eslint/no-deprecated
export class PopupsChangedEvent extends UiEvent {
}
/** ReactContent type guard.
 * @internal
 */
export const isReactContent = (content) => {
    return content.reactNode !== undefined;
};
/** Popup Manager class
 * @public
 */
export class PopupManager {
    static get popupCount() {
        return this._popups.length;
    }
    static get popups() {
        return this._popups;
    }
    static set popups(popups) {
        if (this._popups === popups)
            return;
        this._popups = popups;
        this.onPopupsChangedEvent.emit({ popups });
    }
    /** @internal */
    static clearPopups() {
        this.popups = [];
    }
    static addPopup(popupInfo) {
        const popups = [...PopupManager._popups, popupInfo];
        this.popups = popups;
    }
    static updatePopup(popupInfo, itemIndex) {
        if (itemIndex < 0)
            return;
        const popups = [
            ...PopupManager._popups.slice(0, itemIndex),
            popupInfo,
            ...PopupManager._popups.slice(itemIndex + 1),
        ];
        this.popups = popups;
    }
    static addOrUpdatePopup(popupInfo) {
        const itemIndex = PopupManager._popups.findIndex((info) => info.id === popupInfo.id);
        if (itemIndex >= 0)
            PopupManager.updatePopup(popupInfo, itemIndex);
        else
            PopupManager.addPopup(popupInfo);
    }
    static removePopup(id) {
        const index = PopupManager._popups.findIndex((info) => id === info.id);
        let result = true;
        if (index >= 0) {
            const popups = PopupManager._popups.filter((info) => {
                return info.id !== id;
            });
            this.popups = popups;
        }
        else {
            Logger.logError(UiFramework.loggerCategory("PopupManager"), `removePopup: Could not find popup with id of '${id}'`);
            result = false;
        }
        return result;
    }
    static get defaultOffset() {
        return PopupManager._defaultOffset;
    }
    static set defaultOffset(offset) {
        PopupManager._defaultOffset = offset;
    }
    static showInputEditor(el, pt, value, propertyDescription, onCommit, onCancel) {
        const primitiveValue = {
            value,
            valueFormat: PropertyValueFormat.Primitive,
        };
        const record = new PropertyRecord(primitiveValue, propertyDescription);
        const commitHandler = new InputEditorCommitHandler(onCommit);
        const id = PopupManager._editorId;
        const component = (React.createElement(InputEditorPopup, { id: id, el: el, pt: pt, offset: PopupManager.defaultOffset, record: record, onCancel: onCancel, commitHandler: commitHandler }));
        const popupInfo = {
            id,
            pt,
            component,
            parentDocument: el.ownerDocument,
            parent: el.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static hideInputEditor() {
        return PopupManager.removePopup(PopupManager._editorId);
    }
    static showKeyinPalette(keyins, el, onItemExecuted, onCancel) {
        const id = PopupManager._keyPalettePopupId;
        const cancelFn = onCancel ?? (() => PopupManager.hideKeyinPalette());
        const component = (React.createElement(KeyinPalettePopup, { keyins: keyins, id: id, 
            // eslint-disable-next-line @typescript-eslint/no-deprecated
            el: el, onItemExecuted: onItemExecuted, onCancel: cancelFn }));
        // since the command palette popup is always at top center of specified HTML element and it does not need to move like
        // cursor popups just set pt to 0,0.
        const pt = { x: 0, y: 0 };
        const popupInfo = {
            id,
            pt,
            component,
            parentDocument: el?.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static hideKeyinPalette() {
        return PopupManager.removePopup(PopupManager._keyPalettePopupId);
    }
    /** @deprecated in 4.16.0. Use {@link PopupManager.displayToolbar} instead. */
    static showToolbar(toolbarProps, el, pt, offset, onItemExecuted, onCancel, relativePosition) {
        PopupManager.displayToolbar(toolbarProps.items, {
            anchor: el,
            location: pt,
            offset,
            onCancel,
            placement: mapToPlacement(relativePosition),
            onItemExecuted,
        });
        return true;
    }
    static displayToolbar(items, options) {
        const id = options.id ?? PopupManager._toolbarId;
        const { anchor, location, offset, onCancel, placement, onItemExecuted } = options;
        const component = (React.createElement(ToolbarPopup, { id: id, el: anchor, pt: location, offset: offset, items: items, placement: placement, orientation: Orientation.Horizontal, onCancel: onCancel, onItemExecuted: onItemExecuted }));
        const popupInfo = {
            id,
            pt: location,
            component,
            parentDocument: anchor?.ownerDocument ??
                InternalConfigurableUiManager.getWrapperDocument(),
            parent: anchor?.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static hideToolbar() {
        return PopupManager.removePopup(PopupManager._toolbarId);
    }
    /** @deprecated in 4.16.0. Use {@link PopupManager.showComponent} instead. */
    static showHTMLElement(displayElement, el, pt, offset, onCancel, relativePosition) {
        const id = PopupManager._htmlElementId;
        const component = (
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        React.createElement(HTMLElementPopup, { id: id, el: el, pt: pt, offset: offset, element: displayElement, relativePosition: relativePosition, orientation: Orientation.Horizontal, onCancel: onCancel }));
        const popupInfo = {
            id,
            pt,
            component,
            parentDocument: el.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    /** @deprecated in 4.16.0. Use {@link PopupManager.hideComponent} instead. */
    static hideHTMLElement() {
        return PopupManager.removePopup(PopupManager._htmlElementId);
    }
    /**
     * Displays a React component as a popup.
     * @param displayElement The React component to display.
     * @param options for displaying the component.
     */
    static showComponent(displayElement, options) {
        const { onCancel, location, offset, placement, anchor, id } = options;
        const _id = PopupManager._htmlElementId;
        const component = (React.createElement(ComponentPopup, { id: id ?? _id, anchor: anchor ?? undefined, pt: location, offset: offset, placement: placement, orientation: Orientation.Horizontal, onCancel: onCancel }, displayElement));
        const popupInfo = {
            id: id ?? _id,
            pt: location,
            component,
            parentDocument: anchor?.ownerDocument ??
                InternalConfigurableUiManager.getWrapperDocument(),
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static hideComponent(id) {
        return PopupManager.removePopup(id ?? PopupManager._htmlElementId);
    }
    /** @deprecated in 4.16.0. Use {@link PopupManager.displayCard} instead. */
    static showCard(content, title, toolbarProps, el, pt, offset, onItemExecuted, onCancel, relativePosition) {
        const id = PopupManager._cardId;
        const placement = mapToPlacement(relativePosition);
        const component = (React.createElement(CardPopup, { id: id, el: el, pt: pt, offset: offset, content: content, title: title, items: toolbarProps ? toolbarProps.items : undefined, placement: placement, 
            // eslint-disable-next-line @typescript-eslint/no-deprecated
            orientation: Orientation.Horizontal, onCancel: onCancel, onItemExecuted: onItemExecuted }));
        const popupInfo = {
            id,
            pt,
            component,
            parentDocument: el.ownerDocument,
            parent: el.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static displayCard(content, options) {
        const id = options.id ?? PopupManager._cardId;
        const { onCancel, location, placement, offset, anchor, title, toolbarProps, onItemExecuted, } = options;
        const component = (React.createElement(CardPopup, { id: id, el: anchor, pt: location, offset: offset, content: { reactNode: content }, title: title, items: toolbarProps?.items, placement: placement, 
            // eslint-disable-next-line @typescript-eslint/no-deprecated
            orientation: Orientation.Horizontal, onCancel: onCancel, onItemExecuted: onItemExecuted }));
        const popupInfo = {
            id,
            pt: location,
            component,
            parentDocument: anchor?.ownerDocument ??
                InternalConfigurableUiManager.getWrapperDocument(),
            parent: anchor?.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static hideCard(id) {
        const index = PopupManager._popups.findIndex((info) => id ?? PopupManager._cardId === info.id);
        if (index >= 0)
            return PopupManager.removePopup(PopupManager._cardId);
        return false;
    }
    static openToolSettings(dataProvider, el, pt, offset, onCancel, relativePosition) {
        const id = PopupManager._toolSettingsId;
        const component = (React.createElement(ToolSettingsPopup, { id: id, el: el, pt: pt, offset: offset, dataProvider: dataProvider, relativePosition: relativePosition, orientation: Orientation.Horizontal, onCancel: onCancel }));
        const popupInfo = {
            id,
            pt,
            component,
            parentDocument: el.ownerDocument,
        };
        PopupManager.addOrUpdatePopup(popupInfo);
        return true;
    }
    static closeToolSettings() {
        return PopupManager.removePopup(PopupManager._toolSettingsId);
    }
    static getPopupPosition(el, pt, offset, size) {
        const containerBounds = Rectangle.create(el.getBoundingClientRect());
        const relativeBounds = Rectangle.createFromSize(size).offset(pt);
        const adjustedPosition = offsetAndContainInContainer(relativeBounds, containerBounds.getSize(), offset);
        const position = adjustedPosition.offset(containerBounds.topLeft());
        return position;
    }
}
PopupManager._popups = [];
PopupManager._editorId = "InputEditor";
PopupManager._toolbarId = "Toolbar";
PopupManager._htmlElementId = "HTMLElement";
PopupManager._cardId = "Card";
PopupManager._toolSettingsId = "ToolSettings";
PopupManager._keyPalettePopupId = "KeyinPalette";
PopupManager._defaultOffset = { x: 8, y: 8 };
// eslint-disable-next-line @typescript-eslint/no-deprecated
PopupManager.onPopupsChangedEvent = new PopupsChangedEvent();
/** Popup Renderer.
 * @public
 */
export class PopupRenderer extends React.Component {
    constructor() {
        super(...arguments);
        this.state = {
            parentDocument: null,
            popups: PopupManager.popups,
        };
        this._handleRefSet = (popupDiv) => {
            this.setState({ parentDocument: popupDiv?.ownerDocument ?? null });
        };
        this._handlePopupsChangedEvent = (args) => {
            this.setState({ popups: args.popups });
        };
    }
    componentDidMount() {
        Logger.logInfo("PopupManager", `mounting PopupManager`);
        PopupManager.onPopupsChangedEvent.addListener(this._handlePopupsChangedEvent);
    }
    componentWillUnmount() {
        Logger.logInfo("PopupManager", `un-mounting PopupManager`);
        PopupManager.onPopupsChangedEvent.removeListener(this._handlePopupsChangedEvent);
    }
    render() {
        if (PopupManager.popupCount <= 0)
            return null;
        return (React.createElement("div", { className: "appui-react-popup-render-container", ref: this._handleRefSet }, this.state.parentDocument &&
            this.state.popups
                .filter((info) => (info.parent ??
                // eslint-disable-next-line @typescript-eslint/no-deprecated
                info.parentDocument ??
                this.context.ownerDocument) === this.state.parentDocument)
                .map((popupInfo) => {
                return (React.createElement(React.Fragment, { key: popupInfo.id }, popupInfo.component));
            })));
    }
}
/** @internal */
PopupRenderer.contextType = WrapperContext;
//# sourceMappingURL=PopupManager.js.map