"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isWithinRectBounds = isWithinRectBounds;
exports.computeRectAnnotationDimensions = computeRectAnnotationDimensions;
exports.getAnnotationRectPropsId = getAnnotationRectPropsId;
const panel_utils_1 = require("../../../../common/panel_utils");
const types_1 = require("../../../../scales/types");
const common_1 = require("../../../../utils/common");
const logger_1 = require("../../../../utils/logger");
const common_2 = require("../../state/utils/common");
const spec_1 = require("../../state/utils/spec");
function isWithinRectBounds({ x, y }, { startX, endX, startY, endY }) {
    const withinXBounds = x >= startX && x <= endX;
    const withinYBounds = y >= startY && y <= endY;
    return withinXBounds && withinYBounds;
}
function computeRectAnnotationDimensions(annotationSpec, yScales, xScale, axesSpecs, smallMultiplesScales, chartRotation, getAxisStyle, isHistogram = false) {
    const { dataValues, groupId, outside, id: annotationSpecId } = annotationSpec;
    const { xAxis, yAxis } = (0, spec_1.getAxesSpecForSpecId)(axesSpecs, groupId, chartRotation);
    const yScale = yScales.get(groupId);
    const rectsProps = [];
    const panelSize = (0, panel_utils_1.getPanelSize)(smallMultiplesScales);
    dataValues.forEach((datum) => {
        const { x0: initialX0, x1: initialX1, y0: initialY0, y1: initialY1 } = datum.coordinates;
        if (initialX0 === null && initialX1 === null && initialY0 === null && initialY1 === null) {
            return;
        }
        let height;
        const [x0, x1] = limitValueToDomainRange(xScale, initialX0, initialX1, isHistogram);
        if (x0 === null || x1 === null) {
            return;
        }
        let xAndWidth = null;
        if ((0, types_1.isBandScale)(xScale)) {
            xAndWidth = scaleXonBandScale(xScale, x0, x1);
        }
        else if ((0, types_1.isContinuousScale)(xScale)) {
            xAndWidth = scaleXonContinuousScale(xScale, x0, x1, isHistogram);
        }
        if (!xAndWidth) {
            return;
        }
        const hasYValues = (0, common_1.isDefined)(initialY0) || (0, common_1.isDefined)(initialY1);
        const outsideDim = annotationSpec.outsideDimension ?? getOutsideDimension(getAxisStyle(xAxis?.id ?? yAxis?.id));
        if (!yScale) {
            if (!hasYValues) {
                const rectDimensions = {
                    ...xAndWidth,
                    ...(outside
                        ? getXOutsideAnnotationDimensions(panelSize, chartRotation, xAxis?.position ?? 'bottom', outsideDim)
                        : {
                            y: 0,
                            height: (0, common_2.isHorizontalRotation)(chartRotation) ? panelSize.height : panelSize.width,
                        }),
                };
                rectsProps.push({
                    specId: annotationSpecId,
                    rect: rectDimensions,
                    datum,
                });
            }
            return;
        }
        const hasXValues = (0, common_1.isDefined)(initialX0) || (0, common_1.isDefined)(initialX1);
        if (outside) {
            if (hasXValues && hasYValues) {
                logger_1.Logger.warn(`The RectAnnotation (${annotationSpecId}) was defined as outside but has both x and y values defined.`);
            }
            else if (hasXValues) {
                const rectDimensions = {
                    ...xAndWidth,
                    ...getXOutsideAnnotationDimensions(panelSize, chartRotation, xAxis?.position ?? 'bottom', outsideDim),
                };
                rectsProps.push({
                    specId: annotationSpecId,
                    rect: rectDimensions,
                    datum,
                });
                return;
            }
        }
        const [y0, y1] = limitValueToDomainRange(yScale, initialY0, initialY1);
        if (!Number.isFinite(y0) || !Number.isFinite(y1))
            return;
        let scaledY1 = yScale.pureScale(y1);
        const scaledY0 = yScale.pureScale(y0);
        if (Number.isNaN(scaledY1) || Number.isNaN(scaledY0))
            return;
        height = Math.abs(scaledY0 - scaledY1);
        if (height === 0 && yScale.domain.length === 2 && yScale.domain[0] === yScale.domain[1]) {
            height = panelSize.height;
            scaledY1 = 0;
        }
        const rectDimensions = {
            ...xAndWidth,
            y: scaledY1,
            height,
            ...(outside &&
                !(hasXValues && hasYValues) &&
                getYOutsideAnnotationDimensions(panelSize, chartRotation, yAxis?.position ?? 'left', outsideDim)),
        };
        rectsProps.push({
            specId: annotationSpecId,
            rect: rectDimensions,
            datum,
        });
    });
    return rectsProps.reduce((acc, props, i) => {
        const duplicated = [];
        smallMultiplesScales.vertical.domain.forEach((vDomainValue) => {
            smallMultiplesScales.horizontal.domain.forEach((hDomainValue) => {
                const id = getAnnotationRectPropsId(annotationSpecId, props.datum, i, vDomainValue, hDomainValue);
                const top = smallMultiplesScales.vertical.scale(vDomainValue);
                const left = smallMultiplesScales.horizontal.scale(hDomainValue);
                if (Number.isNaN(top + left))
                    return;
                const panel = { ...panelSize, top, left };
                duplicated.push({ ...props, panel, id });
            });
        });
        return acc.concat(duplicated);
    }, []);
}
function scaleXonBandScale(xScale, x0, x1) {
    const padding = (xScale.step - xScale.originalBandwidth) / 2;
    let scaledX1 = xScale.scale(x1);
    let scaledX0 = xScale.scale(x0);
    if (Number.isNaN(scaledX1 + scaledX0)) {
        return null;
    }
    scaledX1 += xScale.originalBandwidth + padding;
    if (scaledX1 > xScale.range[1]) {
        scaledX1 = xScale.range[1];
    }
    scaledX0 -= padding;
    if (scaledX0 < xScale.range[0]) {
        scaledX0 = xScale.range[0];
    }
    const width = Math.abs(scaledX1 - scaledX0);
    return {
        x: scaledX0,
        width,
    };
}
function scaleXonContinuousScale(xScale, x0, x1, isHistogramModeEnabled = false) {
    if (typeof x1 !== 'number' || typeof x0 !== 'number') {
        return null;
    }
    const scaledX0 = xScale.scale(x0);
    const scaledX1 = xScale.totalBarsInCluster > 0 && !isHistogramModeEnabled ? xScale.scale(x1 + xScale.minInterval) : xScale.scale(x1);
    const width = Math.abs(scaledX1 - scaledX0);
    return Number.isNaN(width)
        ? null
        : { width, x: scaledX0 - (xScale.bandwidthPadding / 2) * xScale.totalBarsInCluster };
}
function limitValueToDomainRange(scale, minValue, maxValue, isHistogram = false) {
    if ((0, types_1.isContinuousScale)(scale)) {
        const [domainStartValue, domainEndValue] = scale.domain;
        const min = maxOf(domainStartValue, minValue);
        const max = minOf(isHistogram ? domainEndValue + scale.minInterval : domainEndValue, maxValue);
        return min !== null && max !== null && min > max ? [null, null] : [min, max];
    }
    else {
        const min = (0, common_1.isNil)(minValue) || !scale.domain.includes(minValue) ? scale.domain[0] : minValue;
        const max = (0, common_1.isNil)(maxValue) || !scale.domain.includes(maxValue) ? scale.domain.at(-1) : maxValue;
        return [min ?? null, max ?? null];
    }
}
function minOf(base, value) {
    return typeof value === 'number' ? Math.min(value, base) : typeof value === 'string' ? value : base;
}
function maxOf(base, value) {
    return typeof value === 'number' ? Math.max(value, base) : typeof value === 'string' ? value : base;
}
function getOutsideDimension({ tickLine: { visible, size } }) {
    return visible ? size : 1;
}
function getAnnotationRectPropsId(specId, datum, index, verticalValue, horizontalValue) {
    return [specId, verticalValue, horizontalValue, ...Object.values(datum.coordinates), datum.details, index].join('__');
}
function getXOutsideAnnotationDimensions(panelSize, rotation, axisPosition, thickness) {
    const { height, width } = panelSize;
    switch (axisPosition) {
        case common_1.Position.Top:
            return {
                y: rotation === 180 ? height : rotation === 90 ? width : -thickness,
                height: thickness,
            };
        case common_1.Position.Bottom:
            return {
                y: rotation === 0 ? height : rotation === 90 ? width : -thickness,
                height: thickness,
            };
        case common_1.Position.Left:
            return {
                y: rotation === -90 ? -thickness : width,
                height: thickness,
            };
        case common_1.Position.Right:
        default:
            return {
                y: rotation === -90 ? width : -thickness,
                height: thickness,
            };
    }
}
function getYOutsideAnnotationDimensions(panelSize, rotation, axisPosition, thickness) {
    const { height, width } = panelSize;
    switch (axisPosition) {
        case common_1.Position.Left:
            return {
                x: rotation === 180 ? width : rotation === 90 ? height : -thickness,
                width: thickness,
            };
        case common_1.Position.Right:
            return {
                x: rotation === 0 ? width : rotation === 90 ? height : -thickness,
                width: thickness,
            };
        case common_1.Position.Top:
            return {
                x: rotation === -90 ? height : -thickness,
                width: thickness,
            };
        case common_1.Position.Bottom:
        default:
            return {
                x: rotation === -90 ? -thickness : height,
                width: thickness,
            };
    }
}
//# sourceMappingURL=dimensions.js.map