"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spread = (this && this.__spread) || function () {
    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
    return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
var d3_shape_1 = require("d3-shape");
var constants_1 = require("../../../scales/constants");
var scale_continuous_1 = require("../../../scales/scale_continuous");
var types_1 = require("../../../scales/types");
var specs_1 = require("../../../specs");
var canvas_text_bbox_calculator_1 = require("../../../utils/bbox/canvas_text_bbox_calculator");
var commons_1 = require("../../../utils/commons");
var curves_1 = require("../../../utils/curves");
var geometry_1 = require("../../../utils/geometry");
var indexed_geometry_map_1 = require("../utils/indexed_geometry_map");
var constants_2 = require("./constants");
exports.getYDatumValue = function (datum, valueName, returnFilled) {
    if (valueName === void 0) { valueName = 'y1'; }
    if (returnFilled === void 0) { returnFilled = true; }
    var _a, _b;
    var value = datum[valueName];
    if (value !== null || !returnFilled) {
        return value;
    }
    return _b = (_a = datum.filled) === null || _a === void 0 ? void 0 : _a[valueName], (_b !== null && _b !== void 0 ? _b : null);
};
function getPointStyleOverrides(datum, seriesIdentifier, pointStyleAccessor) {
    var styleOverride = pointStyleAccessor && pointStyleAccessor(datum, seriesIdentifier);
    if (!styleOverride) {
        return;
    }
    if (typeof styleOverride === 'string') {
        return {
            stroke: styleOverride,
        };
    }
    return styleOverride;
}
exports.getPointStyleOverrides = getPointStyleOverrides;
function getBarStyleOverrides(datum, seriesIdentifier, seriesStyle, styleAccessor) {
    var styleOverride = styleAccessor && styleAccessor(datum, seriesIdentifier);
    if (!styleOverride) {
        return seriesStyle;
    }
    if (typeof styleOverride === 'string') {
        return __assign(__assign({}, seriesStyle), { rect: __assign(__assign({}, seriesStyle.rect), { fill: styleOverride }) });
    }
    return commons_1.mergePartial(seriesStyle, styleOverride, {
        mergeOptionalPartialValues: true,
    });
}
exports.getBarStyleOverrides = getBarStyleOverrides;
function getRadiusFn(data, lineWidth, markSizeRatio) {
    if (markSizeRatio === void 0) { markSizeRatio = 50; }
    if (data.length === 0) {
        return function () { return 0; };
    }
    var _a = data.reduce(function (acc, _a) {
        var mark = _a.mark;
        return mark === null
            ? acc
            : {
                min: Math.min(acc.min, mark / 2),
                max: Math.max(acc.max, mark / 2),
            };
    }, { min: Infinity, max: -Infinity }), min = _a.min, max = _a.max;
    var adjustedMarkSizeRatio = Math.min(Math.max(markSizeRatio, 0), 100);
    var radiusStep = (max - min || max * 100) / Math.pow(adjustedMarkSizeRatio, 2);
    return function getRadius(mark, defaultRadius) {
        if (defaultRadius === void 0) { defaultRadius = 0; }
        if (mark === null) {
            return defaultRadius;
        }
        var circleRadius = (mark / 2 - min) / radiusStep;
        var baseMagicNumber = 2;
        return circleRadius ? Math.sqrt(circleRadius + baseMagicNumber) + lineWidth : lineWidth;
    };
}
exports.getRadiusFn = getRadiusFn;
function renderPoints(shift, dataSeries, xScale, yScale, color, lineStyle, hasY0Accessors, markSizeOptions, styleAccessor, spatial, stackMode) {
    if (spatial === void 0) { spatial = false; }
    var indexedGeometryMap = new indexed_geometry_map_1.IndexedGeometryMap();
    var getRadius = markSizeOptions.enabled
        ? getRadiusFn(dataSeries.data, lineStyle.strokeWidth, markSizeOptions.ratio)
        : function () { return 0; };
    var geometryType = spatial ? indexed_geometry_map_1.GeometryType.spatial : indexed_geometry_map_1.GeometryType.linear;
    var y1Fn = getY1ScaledValueOrThrow(yScale);
    var y0Fn = getY0ScaledValueOrThrow(yScale);
    var yDefined = isYValueDefined(yScale, xScale);
    var pointGeometries = dataSeries.data.reduce(function (acc, datum) {
        var xValue = datum.x, mark = datum.mark;
        if (!xScale.isValueInDomain(xValue)) {
            return acc;
        }
        if (isDatumFilled(datum)) {
            return acc;
        }
        var x = xScale.scale(xValue);
        if (x === null) {
            return acc;
        }
        var points = [];
        var yDatumKeyNames = hasY0Accessors ? ['y0', 'y1'] : ['y1'];
        yDatumKeyNames.forEach(function (yDatumKeyName, index) {
            var radius = getRadius(mark);
            var y;
            try {
                y = yDatumKeyName === 'y1' ? y1Fn(datum) : y0Fn(datum);
                if (y === null) {
                    return;
                }
            }
            catch (_a) {
                return;
            }
            var originalY = getDatumYValue(datum, index === 0, hasY0Accessors, stackMode);
            var seriesIdentifier = {
                key: dataSeries.key,
                specId: dataSeries.specId,
                yAccessor: dataSeries.yAccessor,
                splitAccessors: dataSeries.splitAccessors,
                seriesKeys: dataSeries.seriesKeys,
            };
            var styleOverrides = getPointStyleOverrides(datum, seriesIdentifier, styleAccessor);
            var pointGeometry = {
                radius: radius,
                x: x,
                y: y,
                color: color,
                value: {
                    x: xValue,
                    y: originalY,
                    mark: mark,
                    accessor: hasY0Accessors && index === 0 ? geometry_1.BandedAccessorType.Y0 : geometry_1.BandedAccessorType.Y1,
                    datum: datum.datum,
                },
                transform: {
                    x: shift,
                    y: 0,
                },
                seriesIdentifier: seriesIdentifier,
                styleOverrides: styleOverrides,
            };
            indexedGeometryMap.set(pointGeometry, geometryType);
            if (yDefined(datum, yDatumKeyName)) {
                points.push(pointGeometry);
            }
        });
        return __spread(acc, points);
    }, []);
    return {
        pointGeometries: pointGeometries,
        indexedGeometryMap: indexedGeometryMap,
    };
}
function getDatumYValue(_a, lookingForY0, isBandChart, stackMode) {
    var y1 = _a.y1, y0 = _a.y0, initialY1 = _a.initialY1, initialY0 = _a.initialY0;
    if (isBandChart) {
        return stackMode === specs_1.StackMode.Percentage
            ?
                lookingForY0
                    ? y0
                    : y1
            :
                lookingForY0
                    ? initialY0
                    : initialY1;
    }
    return stackMode === specs_1.StackMode.Percentage ? ((y1 !== null && y1 !== void 0 ? y1 : 0)) - ((y0 !== null && y0 !== void 0 ? y0 : 0)) : initialY1;
}
function renderBars(orderIndex, dataSeries, xScale, yScale, color, sharedSeriesStyle, displayValueSettings, styleAccessor, minBarHeight, stackMode) {
    var indexedGeometryMap = new indexed_geometry_map_1.IndexedGeometryMap();
    var barGeometries = [];
    var bboxCalculator = new canvas_text_bbox_calculator_1.CanvasTextBBoxCalculator();
    var padding = 1;
    var _a = sharedSeriesStyle.displayValue, fontSize = _a.fontSize, fontFamily = _a.fontFamily;
    var absMinHeight = minBarHeight && Math.abs(minBarHeight);
    dataSeries.data.forEach(function (datum) {
        var y0 = datum.y0, y1 = datum.y1, initialY1 = datum.initialY1, filled = datum.filled;
        if (y1 === null || initialY1 === null || (filled && filled.y1 !== undefined)) {
            return;
        }
        if (!xScale.isValueInDomain(datum.x)) {
            return;
        }
        var y = 0;
        var y0Scaled;
        if (yScale.type === constants_1.ScaleType.Log) {
            y = y1 === 0 || y1 === null ? yScale.range[0] : yScale.scale(y1);
            if (yScale.isInverted) {
                y0Scaled = y0 === 0 || y0 === null ? yScale.range[1] : yScale.scale(y0);
            }
            else {
                y0Scaled = y0 === 0 || y0 === null ? yScale.range[0] : yScale.scale(y0);
            }
        }
        else {
            y = yScale.scale(y1);
            if (yScale.isInverted) {
                y0Scaled = y0 === null ? yScale.scale(0) : yScale.scale(y0);
            }
            else {
                y0Scaled = y0 === null ? yScale.scale(0) : yScale.scale(y0);
            }
        }
        if (y === null || y0Scaled === null) {
            return;
        }
        var height = y0Scaled - y;
        if (absMinHeight !== undefined && height !== 0 && Math.abs(height) < absMinHeight) {
            var heightDelta = absMinHeight - Math.abs(height);
            if (height < 0) {
                height = -absMinHeight;
                y += heightDelta;
            }
            else {
                height = absMinHeight;
                y -= heightDelta;
            }
        }
        var xScaled = xScale.scale(datum.x);
        if (xScaled === null) {
            return;
        }
        var x = xScaled + xScale.bandwidth * orderIndex;
        var width = xScale.bandwidth;
        var originalY1Value = stackMode === specs_1.StackMode.Percentage ? y1 - ((y0 !== null && y0 !== void 0 ? y0 : 0)) : initialY1;
        var formattedDisplayValue = displayValueSettings && displayValueSettings.valueFormatter
            ? displayValueSettings.valueFormatter(originalY1Value)
            : undefined;
        var displayValueText = displayValueSettings && displayValueSettings.isAlternatingValueLabel
            ? barGeometries.length % 2 === 0
                ? formattedDisplayValue
                : undefined
            : formattedDisplayValue;
        var computedDisplayValueWidth = bboxCalculator.compute(displayValueText || '', padding, fontSize, fontFamily)
            .width;
        var displayValueWidth = displayValueSettings && displayValueSettings.isValueContainedInElement ? width : computedDisplayValueWidth;
        var hideClippedValue = displayValueSettings ? displayValueSettings.hideClippedValue : undefined;
        var displayValue = displayValueSettings && displayValueSettings.showValueLabel
            ? {
                text: displayValueText,
                width: displayValueWidth,
                height: fontSize,
                hideClippedValue: hideClippedValue,
                isValueContainedInElement: displayValueSettings.isValueContainedInElement,
            }
            : undefined;
        var seriesIdentifier = {
            key: dataSeries.key,
            specId: dataSeries.specId,
            yAccessor: dataSeries.yAccessor,
            splitAccessors: dataSeries.splitAccessors,
            seriesKeys: dataSeries.seriesKeys,
        };
        var seriesStyle = getBarStyleOverrides(datum, seriesIdentifier, sharedSeriesStyle, styleAccessor);
        var barGeometry = {
            displayValue: displayValue,
            x: x,
            y: y,
            width: width,
            height: height,
            color: color,
            value: {
                x: datum.x,
                y: originalY1Value,
                mark: null,
                accessor: geometry_1.BandedAccessorType.Y1,
                datum: datum.datum,
            },
            seriesIdentifier: seriesIdentifier,
            seriesStyle: seriesStyle,
        };
        indexedGeometryMap.set(barGeometry);
        barGeometries.push(barGeometry);
    });
    bboxCalculator.destroy();
    return {
        barGeometries: barGeometries,
        indexedGeometryMap: indexedGeometryMap,
    };
}
exports.renderBars = renderBars;
function renderLine(shift, dataSeries, xScale, yScale, color, curve, hasY0Accessors, xScaleOffset, seriesStyle, markSizeOptions, pointStyleAccessor, hasFit) {
    var y1Fn = getY1ScaledValueOrThrow(yScale);
    var definedFn = isYValueDefined(yScale, xScale);
    var pathGenerator = d3_shape_1.line()
        .x(function (_a) {
        var x = _a.x;
        return xScale.scaleOrThrow(x) - xScaleOffset;
    })
        .y(y1Fn)
        .defined(function (datum) {
        return definedFn(datum);
    })
        .curve(curves_1.getCurveFactory(curve));
    var _a = renderPoints(shift - xScaleOffset, dataSeries, xScale, yScale, color, seriesStyle.line, hasY0Accessors, markSizeOptions, pointStyleAccessor), pointGeometries = _a.pointGeometries, indexedGeometryMap = _a.indexedGeometryMap;
    var clippedRanges = getClippedRanges(dataSeries.data, xScale, xScaleOffset);
    var linePath;
    try {
        linePath = pathGenerator(dataSeries.data) || '';
    }
    catch (_b) {
        linePath = '';
    }
    var lineGeometry = {
        line: linePath,
        points: pointGeometries,
        color: color,
        transform: {
            x: shift,
            y: 0,
        },
        seriesIdentifier: {
            key: dataSeries.key,
            specId: dataSeries.specId,
            yAccessor: dataSeries.yAccessor,
            splitAccessors: dataSeries.splitAccessors,
            seriesKeys: dataSeries.seriesKeys,
        },
        seriesLineStyle: seriesStyle.line,
        seriesPointStyle: seriesStyle.point,
        clippedRanges: clippedRanges,
        hideClippedRanges: !hasFit,
    };
    return {
        lineGeometry: lineGeometry,
        indexedGeometryMap: indexedGeometryMap,
    };
}
exports.renderLine = renderLine;
function renderBubble(shift, dataSeries, xScale, yScale, color, hasY0Accessors, seriesStyle, markSizeOptions, isMixedChart, pointStyleAccessor) {
    var _a = renderPoints(shift, dataSeries, xScale, yScale, color, seriesStyle.point, hasY0Accessors, markSizeOptions, pointStyleAccessor, !isMixedChart), pointGeometries = _a.pointGeometries, indexedGeometryMap = _a.indexedGeometryMap;
    var bubbleGeometry = {
        points: pointGeometries,
        color: color,
        seriesIdentifier: {
            key: dataSeries.key,
            specId: dataSeries.specId,
            yAccessor: dataSeries.yAccessor,
            splitAccessors: dataSeries.splitAccessors,
            seriesKeys: dataSeries.seriesKeys,
        },
        seriesPointStyle: seriesStyle.point,
    };
    return {
        bubbleGeometry: bubbleGeometry,
        indexedGeometryMap: indexedGeometryMap,
    };
}
exports.renderBubble = renderBubble;
function renderArea(shift, dataSeries, xScale, yScale, color, curve, hasY0Accessors, xScaleOffset, seriesStyle, markSizeOptions, isStacked, pointStyleAccessor, hasFit, stackMode) {
    if (isStacked === void 0) { isStacked = false; }
    var y1Fn = getY1ScaledValueOrThrow(yScale);
    var y0Fn = getY0ScaledValueOrThrow(yScale);
    var definedFn = isYValueDefined(yScale, xScale);
    var pathGenerator = d3_shape_1.area()
        .x(function (_a) {
        var x = _a.x;
        return xScale.scaleOrThrow(x) - xScaleOffset;
    })
        .y1(y1Fn)
        .y0(y0Fn)
        .defined(function (datum) {
        return definedFn(datum) && (hasY0Accessors ? definedFn(datum, 'y0') : true);
    })
        .curve(curves_1.getCurveFactory(curve));
    var clippedRanges = getClippedRanges(dataSeries.data, xScale, xScaleOffset);
    var y1Line;
    try {
        y1Line = pathGenerator.lineY1()(dataSeries.data);
    }
    catch (_a) {
        y1Line = null;
    }
    var lines = [];
    if (y1Line) {
        lines.push(y1Line);
    }
    if (hasY0Accessors) {
        var y0Line = void 0;
        try {
            y0Line = pathGenerator.lineY0()(dataSeries.data);
        }
        catch (_b) {
            y0Line = null;
        }
        if (y0Line) {
            lines.push(y0Line);
        }
    }
    var _c = renderPoints(shift - xScaleOffset, dataSeries, xScale, yScale, color, seriesStyle.line, hasY0Accessors, markSizeOptions, pointStyleAccessor, false, stackMode), pointGeometries = _c.pointGeometries, indexedGeometryMap = _c.indexedGeometryMap;
    var areaPath;
    try {
        areaPath = pathGenerator(dataSeries.data) || '';
    }
    catch (_d) {
        areaPath = '';
    }
    var areaGeometry = {
        area: areaPath,
        lines: lines,
        points: pointGeometries,
        color: color,
        transform: {
            y: 0,
            x: shift,
        },
        seriesIdentifier: {
            key: dataSeries.key,
            specId: dataSeries.specId,
            yAccessor: dataSeries.yAccessor,
            splitAccessors: dataSeries.splitAccessors,
            seriesKeys: dataSeries.seriesKeys,
        },
        seriesAreaStyle: seriesStyle.area,
        seriesAreaLineStyle: seriesStyle.line,
        seriesPointStyle: seriesStyle.point,
        isStacked: isStacked,
        clippedRanges: clippedRanges,
        hideClippedRanges: !hasFit,
    };
    return {
        areaGeometry: areaGeometry,
        indexedGeometryMap: indexedGeometryMap,
    };
}
exports.renderArea = renderArea;
function isDatumFilled(_a) {
    var filled = _a.filled, initialY1 = _a.initialY1;
    var _b, _c;
    return ((_b = filled) === null || _b === void 0 ? void 0 : _b.x) !== undefined || ((_c = filled) === null || _c === void 0 ? void 0 : _c.y1) !== undefined || initialY1 === null || initialY1 === undefined;
}
exports.isDatumFilled = isDatumFilled;
function getClippedRanges(dataset, xScale, xScaleOffset) {
    var firstNonNullX = null;
    var hasNull = false;
    return dataset.reduce(function (acc, data) {
        var xScaled = xScale.scale(data.x);
        if (xScaled === null) {
            return acc;
        }
        var xValue = xScaled - xScaleOffset + xScale.bandwidth / 2;
        if (isDatumFilled(data)) {
            var endXValue = xScale.range[1] - xScale.bandwidth * (2 / 3);
            if (firstNonNullX !== null && xValue === endXValue) {
                acc.push([firstNonNullX, xValue]);
            }
            hasNull = true;
        }
        else {
            if (hasNull) {
                if (firstNonNullX !== null) {
                    acc.push([firstNonNullX, xValue]);
                }
                else {
                    acc.push([0, xValue]);
                }
                hasNull = false;
            }
            firstNonNullX = xValue;
        }
        return acc;
    }, []);
}
exports.getClippedRanges = getClippedRanges;
function getGeometryStateStyle(seriesIdentifier, highlightedLegendItem, sharedGeometryStyle, individualHighlight) {
    var defaultStyles = sharedGeometryStyle.default, highlighted = sharedGeometryStyle.highlighted, unhighlighted = sharedGeometryStyle.unhighlighted;
    if (highlightedLegendItem != null) {
        var isPartOfHighlightedSeries = seriesIdentifier.key === highlightedLegendItem.seriesIdentifier.key;
        return isPartOfHighlightedSeries ? highlighted : unhighlighted;
    }
    if (individualHighlight) {
        var hasHighlight = individualHighlight.hasHighlight, hasGeometryHover = individualHighlight.hasGeometryHover;
        if (!hasGeometryHover) {
            return highlighted;
        }
        return hasHighlight ? highlighted : unhighlighted;
    }
    return defaultStyles;
}
exports.getGeometryStateStyle = getGeometryStateStyle;
function isPointOnGeometry(xCoordinate, yCoordinate, indexedGeometry, buffer) {
    if (buffer === void 0) { buffer = constants_2.DEFAULT_HIGHLIGHT_PADDING; }
    var x = indexedGeometry.x, y = indexedGeometry.y;
    if (geometry_1.isPointGeometry(indexedGeometry)) {
        var radius = indexedGeometry.radius, transform = indexedGeometry.transform;
        var distance = commons_1.getDistance({
            x: xCoordinate,
            y: yCoordinate,
        }, {
            x: x + transform.x,
            y: y,
        });
        var radiusBuffer = typeof buffer === 'number' ? buffer : buffer(radius);
        if (radiusBuffer === Infinity) {
            return distance <= radius + constants_2.DEFAULT_HIGHLIGHT_PADDING;
        }
        return distance <= radius + radiusBuffer;
    }
    var width = indexedGeometry.width, height = indexedGeometry.height;
    return yCoordinate >= y && yCoordinate <= y + height && xCoordinate >= x && xCoordinate <= x + width;
}
exports.isPointOnGeometry = isPointOnGeometry;
var DEFAULT_ZERO_BASELINE = 0;
var DEFAULT_LOG_ZERO_BASELINE = constants_1.LOG_MIN_ABS_DOMAIN;
function isYValueDefined(yScale, xScale) {
    var isLogScale = types_1.isLogarithmicScale(yScale);
    var domainPolarity = scale_continuous_1.getDomainPolarity(yScale.domain);
    return function (datum, valueName) {
        if (valueName === void 0) { valueName = 'y1'; }
        var yValue = exports.getYDatumValue(datum, valueName);
        return (yValue !== null &&
            !((isLogScale && domainPolarity >= 0 && yValue <= 0) || (domainPolarity < 0 && yValue >= 0)) &&
            xScale.isValueInDomain(datum.x) &&
            yScale.isValueInDomain(yValue));
    };
}
exports.isYValueDefined = isYValueDefined;
function getY1ScaledValueOrThrow(yScale) {
    return function (datum) {
        var yValue = exports.getYDatumValue(datum);
        return yScale.scaleOrThrow(yValue);
    };
}
exports.getY1ScaledValueOrThrow = getY1ScaledValueOrThrow;
function getY0ScaledValueOrThrow(yScale) {
    var isLogScale = types_1.isLogarithmicScale(yScale);
    var domainPolarity = scale_continuous_1.getDomainPolarity(yScale.domain);
    return function (_a) {
        var y0 = _a.y0;
        if (y0 === null) {
            if (isLogScale) {
                return yScale.scaleOrThrow(domainPolarity >= 0 ? DEFAULT_LOG_ZERO_BASELINE : -DEFAULT_LOG_ZERO_BASELINE);
            }
            return yScale.scaleOrThrow(DEFAULT_ZERO_BASELINE);
        }
        if (isLogScale) {
            if ((domainPolarity >= 0 && y0 <= 0) || (domainPolarity < 0 && y0 >= 0)) {
                return yScale.scaleOrThrow(domainPolarity >= 0 ? DEFAULT_LOG_ZERO_BASELINE : -DEFAULT_LOG_ZERO_BASELINE);
            }
            return yScale.scaleOrThrow(y0);
        }
        return yScale.scaleOrThrow(y0);
    };
}
exports.getY0ScaledValueOrThrow = getY0ScaledValueOrThrow;
//# sourceMappingURL=rendering.js.map