/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Cursor
 */
import * as React from "react";
import { BeUiEvent, Logger } from "@itwin/core-bentley";
import { RelativePosition } from "@itwin/appui-abstract";
import { Point, Size } from "@itwin/core-react";
import { UiFramework } from "../../UiFramework";
import { CursorPopup } from "./CursorPopup";
/** CursorPopup component
 * @public
 */
export class CursorPopupManager {
    /** @internal */
    static clearPopups() {
        this._popups.length = 0;
        CursorPopupManager._emitPopupsChangedEvent();
    }
    static get popups() {
        return this._popups;
    }
    static get popupCount() {
        return this._popups.length;
    }
    static _emitPopupsChangedEvent() {
        CursorPopupManager.onCursorPopupsChangedEvent.emit({});
    }
    static pushPopup(popupInfo) {
        CursorPopupManager._popups.push(popupInfo);
        CursorPopupManager._emitPopupsChangedEvent();
    }
    /** Called to open popup with a new set of properties
     */
    static open(id, content, pt, offset, relativePosition, priority = 0, options) {
        const popupInfo = CursorPopupManager._popups.find((info) => id === info.id);
        if (popupInfo) {
            popupInfo.content = content;
            popupInfo.offset = Point.create(offset);
            popupInfo.relativePosition = relativePosition;
            popupInfo.renderRelativePosition = relativePosition;
            popupInfo.priority = priority;
            popupInfo.options = options;
        }
        else {
            const newPopupInfo = {
                id,
                content,
                offset: Point.create(offset),
                relativePosition,
                options,
                renderRelativePosition: relativePosition,
                priority,
            };
            CursorPopupManager.pushPopup(newPopupInfo);
        }
        CursorPopupManager.updatePosition(pt);
    }
    /** Called to update popup with a new set of properties
     */
    static update(id, content, pt, offset, relativePosition, priority = 0) {
        const popupInfo = CursorPopupManager._popups.find((info) => id === info.id);
        if (popupInfo) {
            popupInfo.content = content;
            popupInfo.offset = Point.create(offset);
            popupInfo.relativePosition = relativePosition;
            popupInfo.renderRelativePosition = relativePosition;
            popupInfo.priority = priority;
        }
        else {
            Logger.logError(UiFramework.loggerCategory(this), `update: Could not find popup with id of '${id}'`);
        }
        CursorPopupManager.updatePosition(pt);
    }
    /** Called to move the open popup to new location
     */
    static updatePosition(pt) {
        CursorPopupManager.resetPopupsRenderRelativePosition(Point.create(pt));
        CursorPopupManager.onCursorPopupUpdatePositionEvent.emit({ pt });
    }
    /** Called when tool wants to close the popup
     */
    static close(id, apply, fadeOut) {
        const popupInfo = CursorPopupManager._popups.find((info) => id === info.id);
        if (popupInfo) {
            if (popupInfo.options) {
                if (apply && popupInfo.options.onApply)
                    popupInfo.options.onApply();
                if (popupInfo.options.onClose)
                    popupInfo.options.onClose();
            }
            if (fadeOut) {
                CursorPopupManager.onCursorPopupFadeOutEvent.emit({ id });
                setTimeout(() => {
                    CursorPopupManager.removePopup(id);
                }, CursorPopup.fadeOutTime);
            }
            else {
                CursorPopupManager.removePopup(id);
            }
        }
        else {
            Logger.logError(UiFramework.loggerCategory(this), `close: Could not find popup with id of '${id}'`);
        }
    }
    static removePopup(id) {
        const index = CursorPopupManager._popups.findIndex((popupInfo) => id === popupInfo.id);
        if (index >= 0) {
            CursorPopupManager._popups.splice(index, 1);
            CursorPopupManager._emitPopupsChangedEvent();
        }
    }
    static resetPopupsRenderRelativePosition(pt) {
        CursorPopupManager.popups.forEach((popupInfo) => {
            popupInfo.renderRelativePosition = popupInfo.relativePosition;
            const flipped = CursorPopupManager.validateRenderRelativePosition(pt, popupInfo);
            // If flipped, call again to validate again.
            if (flipped) {
                CursorPopupManager.validateRenderRelativePosition(pt, popupInfo);
            }
        });
    }
    static validateRenderRelativePosition(pt, popupInfo) {
        const popupRect = CursorPopup.getPopupRect(pt, Point.create(popupInfo.offset), popupInfo.popupSize, popupInfo.renderRelativePosition);
        const { outPos, flipped } = CursorPopupManager.autoFlip(popupInfo.renderRelativePosition, popupRect, window.innerWidth, window.innerHeight);
        if (flipped)
            popupInfo.renderRelativePosition = outPos;
        return flipped;
    }
    static autoFlip(inPos, rect, windowWidth, windowHeight) {
        let flipped = false;
        let outPos = inPos;
        if (rect.right > windowWidth) {
            flipped = true;
            switch (inPos) {
                case RelativePosition.Top:
                case RelativePosition.TopRight:
                    outPos = RelativePosition.TopLeft;
                    break;
                case RelativePosition.Right:
                    outPos = RelativePosition.Left;
                    break;
                case RelativePosition.Bottom:
                case RelativePosition.BottomRight:
                    outPos = RelativePosition.BottomLeft;
                    break;
            }
        }
        if (rect.left < 0) {
            flipped = true;
            switch (inPos) {
                case RelativePosition.Top:
                case RelativePosition.TopLeft:
                    outPos = RelativePosition.TopRight;
                    break;
                case RelativePosition.Left:
                    outPos = RelativePosition.Right;
                    break;
                case RelativePosition.Bottom:
                case RelativePosition.BottomLeft:
                    outPos = RelativePosition.BottomRight;
                    break;
            }
        }
        if (rect.bottom > windowHeight) {
            flipped = true;
            switch (inPos) {
                case RelativePosition.Left:
                case RelativePosition.BottomLeft:
                    outPos = RelativePosition.TopLeft;
                    break;
                case RelativePosition.Bottom:
                    outPos = RelativePosition.Top;
                    break;
                case RelativePosition.Right:
                case RelativePosition.BottomRight:
                    outPos = RelativePosition.TopRight;
                    break;
            }
        }
        if (rect.top < 0) {
            flipped = true;
            switch (inPos) {
                case RelativePosition.Left:
                case RelativePosition.TopLeft:
                    outPos = RelativePosition.BottomLeft;
                    break;
                case RelativePosition.Top:
                    outPos = RelativePosition.Bottom;
                    break;
                case RelativePosition.Right:
                case RelativePosition.TopRight:
                    outPos = RelativePosition.BottomRight;
                    break;
            }
        }
        return { outPos, flipped };
    }
}
CursorPopupManager._popups = new Array();
/** @internal */
CursorPopupManager.onCursorPopupUpdatePositionEvent = new BeUiEvent();
/** @internal */
CursorPopupManager.onCursorPopupFadeOutEvent = new BeUiEvent();
/** @internal */
CursorPopupManager.onCursorPopupsChangedEvent = new BeUiEvent();
/** CursorPopupRenderer React component.
 * @public
 */
export class CursorPopupRenderer extends React.Component {
    constructor(props) {
        super(props);
        this._handlePopupChangedEvent = () => {
            this.forceUpdate();
        };
        this._handleCursorPopupUpdatePositionEvent = (args) => {
            this.setState({ pt: Point.create(args.pt) });
        };
        this.state = {
            pt: new Point(),
        };
    }
    render() {
        if (CursorPopupManager.popupCount <= 0)
            return null;
        return React.createElement(React.Fragment, null, this.renderPositions());
    }
    _handleSizeKnown(popupInfo, size) {
        popupInfo.popupSize = Size.create(size);
    }
    renderPositions() {
        const positions = new Array();
        let renderedPosition;
        const begin = RelativePosition.Left;
        const end = RelativePosition.BottomRight;
        for (let position = begin; position <= end; position++) {
            renderedPosition = this.renderRelativePosition(position);
            if (renderedPosition)
                positions.push(renderedPosition);
        }
        return positions;
    }
    renderRelativePosition(relativePosition) {
        const filteredInfo = CursorPopupManager.popups.filter((popupInfo) => popupInfo.renderRelativePosition === relativePosition);
        if (filteredInfo.length > 0) {
            let totalDimension = 0;
            const ascending = relativePosition === RelativePosition.Right ||
                relativePosition === RelativePosition.BottomRight ||
                relativePosition === RelativePosition.Bottom ||
                relativePosition === RelativePosition.BottomLeft;
            const positionPopups = filteredInfo
                .sort((a, b) => ascending ? a.priority - b.priority : b.priority - a.priority)
                .map((popupInfo, index) => {
                const title = popupInfo.options !== undefined ? popupInfo.options.title : "";
                const shadow = popupInfo.options !== undefined ? popupInfo.options.shadow : false;
                let offset = popupInfo.offset;
                if (index > 0)
                    offset = this.adjustOffset(offset, popupInfo.renderRelativePosition, totalDimension);
                if (popupInfo.popupSize)
                    totalDimension += this.getDimension(popupInfo.popupSize, relativePosition);
                return (React.createElement(CursorPopup, { key: popupInfo.id, id: popupInfo.id, content: popupInfo.content, pt: this.state.pt, offset: offset, relativePosition: popupInfo.renderRelativePosition, title: title, shadow: shadow, onSizeKnown: (size) => this._handleSizeKnown(popupInfo, size) }));
            });
            return (React.createElement(React.Fragment, { key: relativePosition.toString() }, positionPopups));
        }
        return null;
    }
    getDimension(popupSize, relativePosition) {
        let dimension = 0;
        switch (relativePosition) {
            case RelativePosition.Top:
            case RelativePosition.Bottom:
            case RelativePosition.TopLeft:
            case RelativePosition.TopRight:
            case RelativePosition.BottomLeft:
            case RelativePosition.BottomRight:
                dimension = popupSize.height;
                break;
            case RelativePosition.Left:
            case RelativePosition.Right:
                dimension = popupSize.width;
                break;
        }
        return dimension;
    }
    adjustOffset(offset, relativePosition, dimension) {
        let outOffset = Point.create(offset.toProps());
        switch (relativePosition) {
            case RelativePosition.Top:
            case RelativePosition.Bottom:
            case RelativePosition.TopLeft:
            case RelativePosition.TopRight:
            case RelativePosition.BottomLeft:
            case RelativePosition.BottomRight:
                outOffset = Point.create({ x: offset.x, y: offset.y + dimension });
                break;
            case RelativePosition.Left:
            case RelativePosition.Right:
                outOffset = Point.create({ x: offset.x + dimension, y: offset.y });
                break;
        }
        return outOffset;
    }
    componentDidMount() {
        CursorPopupManager.onCursorPopupsChangedEvent.addListener(this._handlePopupChangedEvent);
        CursorPopupManager.onCursorPopupUpdatePositionEvent.addListener(this._handleCursorPopupUpdatePositionEvent);
    }
    componentWillUnmount() {
        CursorPopupManager.onCursorPopupsChangedEvent.removeListener(this._handlePopupChangedEvent);
        CursorPopupManager.onCursorPopupUpdatePositionEvent.removeListener(this._handleCursorPopupUpdatePositionEvent);
    }
}
//# sourceMappingURL=CursorPopupManager.js.map