"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isolatedPointRadius = isolatedPointRadius;
exports.renderPoints = renderPoints;
exports.getPointStyleOverrides = getPointStyleOverrides;
exports.getDatumYValue = getDatumYValue;
exports.getRadiusFn = getRadiusFn;
const point_style_1 = require("./point_style");
const utils_1 = require("./utils");
const common_1 = require("../../../utils/common");
const data_processing_1 = require("../../../utils/data/data_processing");
const geometry_1 = require("../../../utils/geometry");
const indexed_geometry_map_1 = require("../utils/indexed_geometry_map");
const series_1 = require("../utils/series");
const specs_1 = require("../utils/specs");
function isolatedPointRadius(lineStrokeWidth) {
    return lineStrokeWidth + 0.5;
}
function renderPoints(shift, dataSeries, xScale, yScale, panel, color, pointStyle, considerIsolatedPoints, isBandedSpec, markSizeOptions, useSpatialIndex, styleAccessor) {
    const indexedGeometryMap = new indexed_geometry_map_1.IndexedGeometryMap();
    const getRadius = markSizeOptions.enabled
        ? getRadiusFn(dataSeries.data, pointStyle.strokeWidth, markSizeOptions.ratio)
        : () => 0;
    const geometryType = useSpatialIndex ? indexed_geometry_map_1.GeometryType.spatial : indexed_geometry_map_1.GeometryType.linear;
    const y1Fn = (0, utils_1.getY1ScaledValueFn)(yScale);
    const y0Fn = (0, utils_1.getY0ScaledValueFn)(yScale);
    const yDefined = (0, utils_1.isYValueDefinedFn)(yScale, xScale);
    const needSorting = !markSizeOptions.enabled;
    let style = (0, point_style_1.buildPointGeometryStyles)(color, pointStyle);
    const seriesIdentifier = (0, series_1.getSeriesIdentifierFromDataSeries)(dataSeries);
    const { pointGeometries, minDistanceBetweenPoints } = dataSeries.data.reduce((acc, datum, dataIndex) => {
        const { x: xValue, mark } = datum;
        if (!xScale.isValueInDomain(xValue))
            return acc;
        const x = xScale.scale(xValue);
        if (!(0, common_1.isFiniteNumber)(x))
            return acc;
        const prev = dataSeries.data[dataIndex - 1];
        const next = dataSeries.data[dataIndex + 1];
        if ((0, common_1.isFiniteNumber)(acc.prevX) && !(0, utils_1.isDatumFilled)(datum)) {
            acc.minDistanceBetweenPoints = Math.min(acc.minDistanceBetweenPoints, Math.abs(x - acc.prevX));
        }
        acc.prevX = x;
        const yDatumKeyNames = isBandedSpec ? ['y0', 'y1'] : ['y1'];
        const isPointIsolated = considerIsolatedPoints
            ? isIsolatedPoint(dataIndex, dataSeries.data.length, datum, isBandedSpec, y1Fn, y0Fn, prev, next)
            : false;
        let styleOverrides = undefined;
        if (styleAccessor) {
            styleOverrides = getPointStyleOverrides(datum, seriesIdentifier, isPointIsolated, styleAccessor);
            style = (0, point_style_1.buildPointGeometryStyles)(color, pointStyle, styleOverrides);
        }
        yDatumKeyNames.forEach((yDatumKeyName, keyIndex) => {
            const valueAccessor = (0, utils_1.getYDatumValueFn)(yDatumKeyName);
            const y = yDatumKeyName === 'y1' ? y1Fn(datum) : y0Fn(datum);
            const originalY = getDatumYValue(datum, keyIndex === 0, isBandedSpec, dataSeries.stackMode);
            const radius = markSizeOptions.enabled
                ? Math.max(getRadius(mark), pointStyle.radius)
                : styleOverrides?.radius ?? pointStyle.radius;
            const pointGeometry = {
                x,
                y: y === null ? NaN : y,
                radius,
                color,
                style,
                value: {
                    x: xValue,
                    y: originalY,
                    mark,
                    accessor: isBandedSpec && keyIndex === 0 ? geometry_1.BandedAccessorType.Y0 : geometry_1.BandedAccessorType.Y1,
                    datum: datum.datum,
                },
                transform: {
                    x: shift,
                    y: 0,
                },
                seriesIdentifier,
                panel,
                isolated: isPointIsolated,
                seriesType: dataSeries.seriesType,
            };
            indexedGeometryMap.set(pointGeometry, geometryType);
            if ((0, common_1.isFiniteNumber)(y) &&
                yDefined(datum, valueAccessor) &&
                yScale.isValueInDomain(valueAccessor(datum)) &&
                !(0, utils_1.isDatumFilled)(datum)) {
                if (needSorting) {
                    (0, data_processing_1.inplaceInsertInSortedArray)(acc.pointGeometries, pointGeometry, (p) => p?.radius ?? NaN);
                }
                else {
                    acc.pointGeometries.push(pointGeometry);
                }
            }
        });
        return acc;
    }, { pointGeometries: [], minDistanceBetweenPoints: Infinity, prevX: undefined });
    return {
        pointGeometries,
        minDistanceBetweenPoints,
        indexedGeometryMap,
    };
}
function getPointStyleOverrides(datum, seriesIdentifier, isolatedPoint, pointStyleAccessor) {
    const styleOverride = pointStyleAccessor && pointStyleAccessor(datum, seriesIdentifier, isolatedPoint);
    if (!styleOverride) {
        return;
    }
    if (typeof styleOverride === 'string') {
        return {
            stroke: styleOverride,
        };
    }
    return styleOverride;
}
function getDatumYValue({ y1, y0, initialY1, initialY0 }, lookingForY0, isBandedSpec, stackMode) {
    if (isBandedSpec) {
        return stackMode === specs_1.StackMode.Percentage ? (lookingForY0 ? y0 : y1) : lookingForY0 ? initialY0 : initialY1;
    }
    return stackMode === specs_1.StackMode.Percentage ? ((0, common_1.isNil)(y1) || (0, common_1.isNil)(initialY1) ? null : y1 - (y0 ?? 0)) : initialY1;
}
function getRadiusFn(data, lineWidth, markSizeRatio = 50) {
    if (data.length === 0) {
        return () => 0;
    }
    const { min, max } = data.reduce((acc, { mark }) => mark === null
        ? acc
        : {
            min: Math.min(acc.min, mark / 2),
            max: Math.max(acc.max, mark / 2),
        }, { min: Infinity, max: -Infinity });
    const adjustedMarkSizeRatio = Math.min(Math.max(markSizeRatio, 0), 100);
    const radiusStep = (max - min || max * 100) / Math.pow(adjustedMarkSizeRatio, 2);
    return function getRadius(mark, defaultRadius = 0) {
        if (mark === null) {
            return defaultRadius;
        }
        const circleRadius = (mark / 2 - min) / radiusStep;
        const baseMagicNumber = 2;
        return circleRadius ? Math.sqrt(circleRadius + baseMagicNumber) + lineWidth : lineWidth;
    };
}
function isInitialDatumDefined(datum, isBandedSpec, y1Scale, y0Scale) {
    return ((0, common_1.isNil)(datum.filled?.x) &&
        (0, common_1.isNil)(datum.filled?.y1) &&
        (0, common_1.isFiniteNumber)(datum.initialY1) &&
        (0, common_1.isFiniteNumber)(y1Scale(datum)) &&
        (isBandedSpec ? (0, common_1.isNil)(datum.filled?.y0) && (0, common_1.isFiniteNumber)(datum.initialY0) && (0, common_1.isFiniteNumber)(y0Scale(datum)) : true));
}
function isIsolatedPoint(index, length, current, isBandedSpec, y1Scale, y0Scale, prev, next) {
    if (!isInitialDatumDefined(current, isBandedSpec, y1Scale, y0Scale)) {
        return true;
    }
    const isNextNotDefined = (0, common_1.isNil)(next) || !isInitialDatumDefined(next, isBandedSpec, y1Scale, y0Scale);
    if (index === 0 && isNextNotDefined) {
        return true;
    }
    const isPrevNotDefined = (0, common_1.isNil)(prev) || !isInitialDatumDefined(prev, isBandedSpec, y1Scale, y0Scale);
    if (index === length - 1 && isPrevNotDefined) {
        return true;
    }
    return isNextNotDefined && isPrevNotDefined;
}
//# sourceMappingURL=points.js.map