"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const mobx_react_1 = require("mobx-react");
const react_styles_1 = require("@patternfly/react-styles");
const react_core_1 = require("@patternfly/react-core");
const check_circle_icon_1 = tslib_1.__importDefault(require("@patternfly/react-icons/dist/esm/icons/check-circle-icon"));
const exclamation_circle_icon_1 = tslib_1.__importDefault(require("@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon"));
const exclamation_triangle_icon_1 = tslib_1.__importDefault(require("@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon"));
const topology_components_1 = tslib_1.__importDefault(require("../../css/topology-components"));
const types_1 = require("../../types");
const Decorator_1 = tslib_1.__importDefault(require("../decorators/Decorator"));
const layers_1 = require("../layers");
const const_1 = require("../../const");
const utils_1 = require("../../utils");
const NodeLabel_1 = tslib_1.__importDefault(require("./labels/NodeLabel"));
const NodeShadows_1 = tslib_1.__importStar(require("./NodeShadows"));
const shapes_1 = require("./shapes");
const StatusQuadrant = types_1.TopologyQuadrant.upperLeft;
const getStatusIcon = (status) => {
    switch (status) {
        case types_1.NodeStatus.danger:
            return (0, jsx_runtime_1.jsx)(exclamation_circle_icon_1.default, { className: "pf-m-danger" });
        case types_1.NodeStatus.warning:
            return (0, jsx_runtime_1.jsx)(exclamation_triangle_icon_1.default, { className: "pf-m-warning" });
        case types_1.NodeStatus.success:
            return (0, jsx_runtime_1.jsx)(check_circle_icon_1.default, { className: "pf-m-success" });
        default:
            return null;
    }
};
const SCALE_UP_TIME = 200;
const DefaultNodeInner = (0, mobx_react_1.observer)(({ className, element, selected, hover, scaleNode, showLabel = true, label, secondaryLabel, labelClassName, labelPosition, scaleLabel, truncateLength, labelIconClass, labelIcon, labelIconPadding, nodeStatus, showStatusBackground, showStatusDecorator = false, statusDecoratorTooltip, getCustomShape, getShapeDecoratorCenter, onStatusDecoratorClick, badge, badgeColor, badgeTextColor, badgeBorderColor, badgeClassName, badgeLocation, onSelect, children, attachments, dragNodeRef, dragging, edgeDragging, canDrop, dropTarget, dndDropRef, onHideCreateConnector, onShowCreateConnector, onContextMenu, contextMenuOpen, raiseLabelOnHover = true, hideContextMenuKebab }) => {
    const [hovered, hoverRef] = (0, utils_1.useHover)();
    const status = nodeStatus || element.getNodeStatus();
    const refs = (0, utils_1.useCombineRefs)(hoverRef, dragNodeRef);
    const { width, height } = element.getDimensions();
    const isHover = hover !== undefined ? hover : hovered;
    const [nodeScale, setNodeScale] = (0, react_1.useState)(1);
    const decoratorRef = (0, react_1.useRef)(null);
    const statusDecorator = (0, react_1.useMemo)(() => {
        if (!status || !showStatusDecorator) {
            return null;
        }
        const icon = getStatusIcon(status);
        if (!icon) {
            return null;
        }
        const { x, y } = getShapeDecoratorCenter
            ? getShapeDecoratorCenter(StatusQuadrant, element)
            : (0, shapes_1.getDefaultShapeDecoratorCenter)(StatusQuadrant, element);
        const decorator = ((0, jsx_runtime_1.jsx)(Decorator_1.default, { x: x, y: y, radius: shapes_1.DEFAULT_DECORATOR_RADIUS, showBackground: true, onClick: (e) => onStatusDecoratorClick(e, element), icon: (0, jsx_runtime_1.jsx)("g", Object.assign({ className: (0, react_styles_1.css)(topology_components_1.default.topologyNodeDecoratorStatus) }, { children: icon })), ariaLabel: status, innerRef: decoratorRef }));
        if (statusDecoratorTooltip) {
            return ((0, jsx_runtime_1.jsx)(react_core_1.Tooltip, Object.assign({ triggerRef: decoratorRef, content: statusDecoratorTooltip, position: react_core_1.TooltipPosition.left }, { children: decorator })));
        }
        return decorator;
    }, [status, showStatusDecorator, getShapeDecoratorCenter, element, statusDecoratorTooltip, onStatusDecoratorClick]);
    (0, react_1.useEffect)(() => {
        if (isHover) {
            onShowCreateConnector && onShowCreateConnector();
        }
        else {
            onHideCreateConnector && onHideCreateConnector();
        }
    }, [isHover, onShowCreateConnector, onHideCreateConnector]);
    const ShapeComponent = (getCustomShape && getCustomShape(element)) || (0, shapes_1.getShapeComponent)(element);
    const groupClassName = (0, react_styles_1.css)(topology_components_1.default.topologyNode, className, isHover && 'pf-m-hover', (dragging || edgeDragging) && 'pf-m-dragging', canDrop && 'pf-m-highlight', canDrop && dropTarget && 'pf-m-drop-target', selected && 'pf-m-selected', utils_1.StatusModifier[status]);
    const backgroundClassName = (0, react_styles_1.css)(topology_components_1.default.topologyNodeBackground, showStatusBackground && utils_1.StatusModifier[status], showStatusBackground && selected && 'pf-m-selected');
    let filter;
    if (status === 'danger') {
        filter = (0, utils_1.createSvgIdUrl)(NodeShadows_1.NODE_SHADOW_FILTER_ID_DANGER);
    }
    else if (isHover || dragging || edgeDragging || dropTarget) {
        filter = (0, utils_1.createSvgIdUrl)(NodeShadows_1.NODE_SHADOW_FILTER_ID_HOVER);
    }
    const nodeLabelPosition = labelPosition || element.getLabelPosition();
    const scale = element.getGraph().getScale();
    const animationRef = (0, react_1.useRef)(null);
    const scaleGoal = (0, react_1.useRef)(1);
    const nodeScaled = (0, react_1.useRef)(false);
    (0, react_1.useEffect)(() => {
        if (!scaleNode || scale >= 1) {
            setNodeScale(1);
            nodeScaled.current = false;
            if (animationRef.current) {
                cancelAnimationFrame(animationRef.current);
                animationRef.current = 0;
            }
        }
        else {
            scaleGoal.current = 1 / scale;
            const scaleDelta = scaleGoal.current - scale;
            const initTime = performance.now();
            const bumpScale = (bumpTime) => {
                const scalePercent = (bumpTime - initTime) / SCALE_UP_TIME;
                const nextScale = Math.min(scale + scaleDelta * scalePercent, scaleGoal.current);
                setNodeScale(nextScale);
                if (nextScale < scaleGoal.current) {
                    animationRef.current = requestAnimationFrame(bumpScale);
                }
                else {
                    nodeScaled.current = true;
                    animationRef.current = 0;
                }
            };
            if (nodeScaled.current) {
                setNodeScale(scaleGoal.current);
            }
            else if (!animationRef.current) {
                animationRef.current = requestAnimationFrame(bumpScale);
            }
        }
        return () => {
            if (animationRef.current) {
                cancelAnimationFrame(animationRef.current);
                animationRef.current = 0;
            }
        };
    }, [scale, scaleNode]);
    // counter scale label
    const counterScale = (scale, scaleMin, scaleMax, valueMin, valueMax) => {
        if (scale >= scaleMax) {
            return valueMin;
        }
        else if (scale <= scaleMin) {
            return valueMax;
        }
        return valueMin + (1 - (scale - scaleMin) / (scaleMax - scaleMin)) * (valueMax - valueMin);
    };
    const labelScale = scaleLabel ? counterScale(scale, 0.35, 0.85, 1, 1.6) : 1;
    const labelPositionScale = scaleLabel ? Math.min(1, 1 / labelScale) : 1;
    const { translateX, translateY } = (0, react_1.useMemo)(() => {
        if (!scaleNode) {
            return { translateX: 0, translateY: 0 };
        }
        const bounds = element.getBounds();
        const translateX = bounds.width / 2 - (bounds.width / 2) * nodeScale;
        const translateY = bounds.height / 2 - (bounds.height / 2) * nodeScale;
        return { translateX, translateY };
    }, [element, nodeScale, scaleNode]);
    const renderLabel = () => {
        if (!showLabel || !(label || element.getLabel())) {
            return null;
        }
        let labelX;
        let labelY;
        const labelPaddingX = 8;
        const labelPaddingY = 4;
        if (nodeLabelPosition === types_1.LabelPosition.right) {
            labelX = (width + labelPaddingX) * labelPositionScale;
            labelY = height / 2;
        }
        else if (nodeLabelPosition === types_1.LabelPosition.left) {
            labelX = 0;
            labelY = height / 2 - labelPaddingY;
        }
        else if (nodeLabelPosition === types_1.LabelPosition.top) {
            labelX = width / 2;
            labelY = labelPaddingY + labelPaddingY / 2;
        }
        else {
            labelX = (width / 2) * labelPositionScale;
            labelY = height + labelPaddingY + labelPaddingY / 2;
        }
        const nodeLabel = ((0, jsx_runtime_1.jsx)("g", Object.assign({ transform: raiseLabelOnHover && isHover
                ? `${scaleNode ? `translate(${translateX}, ${translateY})` : ''} scale(${nodeScale})`
                : undefined }, { children: (0, jsx_runtime_1.jsx)("g", Object.assign({ transform: `scale(${labelScale})` }, { children: (0, jsx_runtime_1.jsx)(NodeLabel_1.default, Object.assign({ className: (0, react_styles_1.css)(topology_components_1.default.topologyNodeLabel, labelClassName), x: labelX, y: labelY * labelPositionScale, position: nodeLabelPosition, paddingX: 8, paddingY: 4, secondaryLabel: secondaryLabel, truncateLength: truncateLength, status: status, badge: badge, badgeColor: badgeColor, badgeTextColor: badgeTextColor, badgeBorderColor: badgeBorderColor, badgeClassName: badgeClassName, badgeLocation: badgeLocation, onContextMenu: onContextMenu, contextMenuOpen: contextMenuOpen, hideContextMenuKebab: hideContextMenuKebab, hover: isHover, labelIconClass: labelIconClass, labelIcon: labelIcon, labelIconPadding: labelIconPadding }, { children: label || element.getLabel() })) })) })));
        if (isHover && raiseLabelOnHover) {
            return (0, jsx_runtime_1.jsx)(layers_1.Layer, Object.assign({ id: const_1.TOP_LAYER }, { children: nodeLabel }));
        }
        return nodeLabel;
    };
    return ((0, jsx_runtime_1.jsxs)("g", Object.assign({ className: groupClassName, transform: `${scaleNode ? `translate(${translateX}, ${translateY})` : ''} scale(${nodeScale})` }, { children: [(0, jsx_runtime_1.jsx)(NodeShadows_1.default, {}), (0, jsx_runtime_1.jsxs)("g", Object.assign({ ref: refs, onClick: onSelect, onContextMenu: onContextMenu }, { children: [ShapeComponent && ((0, jsx_runtime_1.jsx)(ShapeComponent, { className: backgroundClassName, element: element, width: width, height: height, dndDropRef: dndDropRef, filter: filter })), renderLabel(), children] })), statusDecorator, attachments] })));
});
const DefaultNode = (_a) => {
    var { element, showLabel = true, showStatusDecorator = false } = _a, rest = tslib_1.__rest(_a, ["element", "showLabel", "showStatusDecorator"]);
    if (!(0, types_1.isNode)(element)) {
        throw new Error('DefaultNode must be used only on Node elements');
    }
    return ((0, jsx_runtime_1.jsx)(DefaultNodeInner, Object.assign({ element: element, showLabel: showLabel, showStatusDecorator: showStatusDecorator }, rest)));
};
exports.default = DefaultNode;
//# sourceMappingURL=DefaultNode.js.map