/* *
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  A commercial license may be required depending on use.
 *  See www.highcharts.com/license
 *
 *
 * */
'use strict';
import Series from '../../Core/Series/Series.js';
import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
import { defined, isObject, merge } from '../../Shared/Utilities.js';
/* *
 *
 *  Class
 *
 * */
/**
 * The line series is the base type and is therefor the series base prototype.
 *
 * @internal
 */
class LineSeries extends Series {
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Draw the graph. Called internally when rendering line-like series
     * types. The first time it generates the `series.graph` item and
     * optionally other series-wide items like `series.area` for area
     * charts. On subsequent calls these items are updated with new
     * positions and attributes.
     *
     * @function Highcharts.Series#drawGraph
     * @internal
     */
    drawGraph() {
        const options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode;
        // Draw the graph
        [this, ...this.zones].forEach((owner, i) => {
            let attribs, graph = owner.graph;
            const verb = graph ? 'animate' : 'attr', dashStyle = owner.dashStyle ||
                options.dashStyle;
            if (graph) {
                graph.endX = this.preventGraphAnimation ?
                    null :
                    graphPath.xMap;
                graph.animate({ d: graphPath });
            }
            else if (graphPath.length) { // #1487
                /**
                 * SVG element of line-based charts. Can be used for styling
                 * purposes. If zones are configured, this element will be
                 * hidden and replaced by multiple zone lines, accessible
                 * via `series.zones[i].graph`.
                 *
                 * @name Highcharts.Series#graph
                 * @type {Highcharts.SVGElement|undefined}
                 * @internal
                 */
                owner.graph = graph = this.chart.renderer
                    .path(graphPath)
                    .addClass('highcharts-graph' +
                    (i ? ` highcharts-zone-graph-${i - 1} ` : ' ') +
                    ((i && owner.className) || ''))
                    .attr({ zIndex: 1 }) // #1069
                    .add(this.group);
            }
            if (graph && !styledMode) {
                attribs = {
                    'stroke': ((!i && options.lineColor) || // Series only
                        owner.color ||
                        this.color ||
                        "#cccccc" /* Palette.neutralColor20 */),
                    'stroke-width': options.lineWidth || 0,
                    // Polygon series use filled graph
                    'fill': (this.fillGraph && this.color) || 'none'
                };
                // Apply dash style
                if (dashStyle) {
                    attribs.dashstyle = dashStyle;
                    // The reason for the `else if` is that linecaps don't mix well
                    // with dashstyle. The gaps get partially filled by the
                    // linecap.
                }
                else if (options.linecap !== 'square') {
                    attribs['stroke-linecap'] =
                        attribs['stroke-linejoin'] = 'round';
                }
                graph[verb](attribs);
                // Add shadow only to the main series (not zones)
                // If shadow is defined, use `filterUnits: 'userSpaceOnUse'`
                // to avoid known SVG filter bug (#19093)
                if (options.shadow) {
                    const isInverted = this.chart.inverted;
                    const filterUnits = { filterUnits: 'userSpaceOnUse' };
                    const shadowOptions = isObject(options.shadow) ?
                        merge(isInverted ? {} : filterUnits, options.shadow) : (isInverted ? true : filterUnits);
                    graph.shadow(shadowOptions);
                }
            }
            // Helpers for animation
            if (graph) {
                graph.startX = graphPath.xMap;
                graph.isArea = graphPath.isArea; // For arearange animation
            }
        });
    }
    // eslint-disable-next-line valid-jsdoc
    /**
     * Get the graph path.
     *
     * @internal
     */
    getGraphPath(points, nullsAsZeroes, connectCliffs) {
        const series = this, options = series.options, graphPath = [], xMap = [];
        let gap, step = options.step;
        points = points || series.points;
        // Bottom of a stack is reversed
        const reversed = points.reversed;
        if (reversed) {
            points.reverse();
        }
        // Reverse the steps (#5004)
        step = {
            right: 1,
            center: 2
        }[step] || (step && 3);
        if (step && reversed) {
            step = 4 - step;
        }
        // Remove invalid points, especially in spline (#5015)
        points = this.getValidPoints(points, false, options.nullInteraction || !(options.connectNulls &&
            !nullsAsZeroes &&
            !connectCliffs));
        // Build the line
        points.forEach(function (point, i) {
            const plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1], isNull = point.isNull || typeof plotY !== 'number';
            // The path to this point from the previous
            let pathToPoint;
            if ((point.leftCliff || lastPoint?.rightCliff) &&
                !connectCliffs) {
                gap = true; // ... and continue
            }
            // Line series, nullsAsZeroes is not handled
            if (isNull && !defined(nullsAsZeroes) && i > 0) {
                gap = !options.connectNulls;
                // Area series, nullsAsZeroes is set
            }
            else if (isNull && !nullsAsZeroes) {
                gap = true;
            }
            else {
                if (i === 0 || gap) {
                    pathToPoint = [[
                            'M',
                            point.plotX,
                            point.plotY
                        ]];
                    // Generate the spline as defined in the SplineSeries object
                }
                else if (series.getPointSpline) {
                    pathToPoint = [series.getPointSpline(points, point, i)];
                }
                else if (step) {
                    if (step === 1) { // Right
                        pathToPoint = [[
                                'L',
                                lastPoint.plotX,
                                plotY
                            ]];
                    }
                    else if (step === 2) { // Center
                        pathToPoint = [[
                                'L',
                                (lastPoint.plotX + plotX) / 2,
                                lastPoint.plotY
                            ], [
                                'L',
                                (lastPoint.plotX + plotX) / 2,
                                plotY
                            ]];
                    }
                    else {
                        pathToPoint = [[
                                'L',
                                plotX,
                                lastPoint.plotY
                            ]];
                    }
                    pathToPoint.push([
                        'L',
                        plotX,
                        plotY
                    ]);
                }
                else {
                    // Normal line to next point
                    pathToPoint = [[
                            'L',
                            plotX,
                            plotY
                        ]];
                }
                // Prepare for animation. When step is enabled, there are
                // two path nodes for each x value.
                xMap.push(point.x);
                if (step) {
                    xMap.push(point.x);
                    if (step === 2) { // Step = center (#8073)
                        xMap.push(point.x);
                    }
                }
                graphPath.push.apply(graphPath, pathToPoint);
                gap = false;
            }
        });
        graphPath.xMap = xMap;
        series.graphPath = graphPath;
        return graphPath;
    }
}
/* *
 *
 *  Static Functions
 *
 * */
LineSeries.defaultOptions = merge(Series.defaultOptions, {
    /**
     * What type of legend symbol to render for this series. Can be one
     * of `areaMarker`, `lineMarker` or `rectangle`.
     *
     * @sample {highcharts} highcharts/series/legend-symbol/
     *         Change the legend symbol
     *
     * @type      {string}
     * @default   lineMarker
     * @since     11.0.1
     * @apioption plotOptions.line.legendSymbol
     */
    legendSymbol: 'lineMarker'
});
SeriesRegistry.registerSeriesType('line', LineSeries);
/* *
 *
 *  Default Export
 *
 * */
/** @internal */
export default LineSeries;
/* *
 *
 *  API Options
 *
 * */
/**
 * A line series displays information as a series of data points connected by
 * straight line segments.
 *
 * @sample {highcharts} highcharts/demo/line-chart/
 *         Line chart
 * @sample {highstock} stock/demo/basic-line/
 *         Line chart
 *
 * @extends   plotOptions.series
 * @product   highcharts highstock
 * @apioption plotOptions.line
 */
/**
 * A `line` series. If the [type](#series.line.type) option is not
 * specified, it is inherited from [chart.type](#chart.type).
 *
 * @extends   series,plotOptions.line
 * @excluding dataParser,dataURL
 * @product   highcharts highstock
 * @apioption series.line
 */
/**
 * An array of data points for the series. For the `line` series type,
 * points can be given in the following ways:
 *
 * 1. An array of numerical values. In this case, the numerical values will be
 *    interpreted as `y` options. The `x` values will be automatically
 *    calculated, either starting at 0 and incremented by 1, or from
 *    `pointStart` and `pointInterval` given in the series options. If the axis
 *    has categories, these will be used. Example:
 *    ```js
 *    data: [0, 5, 3, 5]
 *    ```
 *
 * 2. An array of arrays with 2 values. In this case, the values correspond to
 *    `x,y`. If the first value is a string, it is applied as the name of the
 *    point, and the `x` value is inferred.
 *    ```js
 *    data: [
 *        [0, 1],
 *        [1, 2],
 *        [2, 8]
 *    ]
 *    ```
 *
 * 3. An array of objects with named values. The following snippet shows only a
 *    few settings, see the complete options set below. If the total number of
 *    data points exceeds the series'
 *    [turboThreshold](#series.line.turboThreshold),
 *    this option is not available.
 *    ```js
 *    data: [{
 *        x: 1,
 *        y: 9,
 *        name: "Point2",
 *        color: "#00FF00"
 *    }, {
 *        x: 1,
 *        y: 6,
 *        name: "Point1",
 *        color: "#FF00FF"
 *    }]
 *    ```
 *
 * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
 * additional declaration to allow custom data types:
 * ```ts
 * declare module `highcharts` {
 *   interface PointOptionsObject {
 *     custom: Record<string, (boolean|number|string)>;
 *   }
 * }
 * ```
 *
 * @sample {highcharts} highcharts/chart/reflow-true/
 *         Numerical values
 * @sample {highcharts} highcharts/series/data-array-of-arrays/
 *         Arrays of numeric x and y
 * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
 *         Arrays of datetime x and y
 * @sample {highcharts} highcharts/series/data-array-of-name-value/
 *         Arrays of point.name and y
 * @sample {highcharts} highcharts/series/data-array-of-objects/
 *         Config objects
 *
 * @declare   Highcharts.PointOptionsObject
 * @type      {Array<number|Array<(number|string),(number|null)>|null|*>}
 * @apioption series.line.data
 */
/**
 * An additional, individual class name for the data point's graphic
 * representation. Changes to a point's color will also be reflected in a
 * chart's legend and tooltip.
 *
 * @sample {highcharts} highcharts/css/point-series-classname
 *         Series and point class name
 *
 * @type      {string}
 * @since     5.0.0
 * @product   highcharts highstock gantt
 * @apioption series.line.data.className
 */
/**
 * Individual color for the point. By default the color is pulled from
 * the global `colors` array.
 *
 * In styled mode, the `color` option doesn't take effect. Instead, use
 * `colorIndex`.
 *
 * @sample {highcharts} highcharts/point/color/
 *         Mark the highest point
 *
 * @type      {Highcharts.ColorType}
 * @product   highcharts highstock gantt
 * @apioption series.line.data.color
 */
/**
 * A specific color index to use for the point, so its graphic representations
 * are given the class name `highcharts-color-{n}`. In styled mode this will
 * change the color of the graphic. In non-styled mode, the color is set by the
 * `fill` attribute, so the change in class name won't have a visual effect by
 * default.
 *
 * Since v11, CSS variables on the form `--highcharts-color-{n}` make changing
 * the color scheme very convenient.
 *
 * @sample    {highcharts} highcharts/css/colorindex/
 *            Series and point color index
 *
 * @type      {number}
 * @since     5.0.0
 * @product   highcharts gantt
 * @apioption series.line.data.colorIndex
 */
/**
 * A reserved subspace to store options and values for customized functionality.
 * Here you can add additional data for your own event callbacks and formatter
 * callbacks.
 *
 * @sample {highcharts} highcharts/point/custom/
 *         Point and series with custom data
 *
 * @type      {Highcharts.Dictionary<*>}
 * @apioption series.line.data.custom
 */
/**
 * Individual data label for each point. The options are the same as
 * the ones for [plotOptions.series.dataLabels](
 * #plotOptions.series.dataLabels) with exception of `zIndex` which is applied
 * on the data label's parent group.
 *
 * @sample highcharts/point/datalabels/
 *         Show a label for the last value
 *
 * @type      {*|Array<*>}
 * @declare   Highcharts.DataLabelsOptions
 * @extends   plotOptions.line.dataLabels
 * @excluding zIndex
 * @product   highcharts highstock gantt
 * @apioption series.line.data.dataLabels
 */
/**
 * An id for the point. This can be used after render time to get a
 * pointer to the point object through `chart.get()`.
 *
 * @sample {highcharts} highcharts/point/id/
 *         Remove an id'd point
 *
 * @type      {string}
 * @since     1.2.0
 * @product   highcharts highstock gantt
 * @apioption series.line.data.id
 */
/**
 * The rank for all this point's data labels in case of collision. If two
 * data labels are about to overlap, only the one with the highest `labelrank`
 * will be drawn.
 *
 * The `labelrank` set on `series.dataLabels` takes precedence over this.
 *
 * @type      {number}
 * @apioption series.line.data.labelrank
 */
/**
 * The name of the point as shown in the legend, tooltip, dataLabels, etc.
 *
 * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
 *
 * @sample {highcharts} highcharts/series/data-array-of-objects/
 *         Point names
 *
 * @type      {string}
 * @apioption series.line.data.name
 */
/**
 * Whether the data point is selected initially.
 *
 * @type      {boolean}
 * @default   false
 * @product   highcharts highstock gantt
 * @apioption series.line.data.selected
 */
/**
 * The x value of the point.
 *
 * For datetime axes, a number value is the timestamp in milliseconds since
 * 1970, while a date string is parsed according to the [current time zone]
 * (https://api.highcharts.com/highcharts/time.timezone) of the
 * chart. Date strings are supported since v12.
 *
 * @type      {number|string}
 * @product   highcharts highstock
 * @apioption series.line.data.x
 */
/**
 * The y value of the point.
 *
 * @type      {number|null}
 * @product   highcharts highstock
 * @apioption series.line.data.y
 */
/**
 * The individual point events.
 *
 * @extends   plotOptions.series.point.events
 * @product   highcharts highstock gantt
 * @apioption series.line.data.events
 */
/**
 * Options for the point markers of line-like series.
 *
 * @declare   Highcharts.PointMarkerOptionsObject
 * @extends   plotOptions.series.marker
 * @product   highcharts highstock
 * @apioption series.line.data.marker
 */
''; // Include precedent doclets in transpiled
