/* *
 *
 *  Highcharts variwide module
 *
 *  (c) 2010-2026 Highsoft AS
 *  Author: Torstein Hønsi
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 * */
'use strict';
import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
const { column: ColumnSeries } = SeriesRegistry.seriesTypes;
import VariwideComposition from './VariwideComposition.js';
import VariwidePoint from './VariwidePoint.js';
import VariwideSeriesDefaults from './VariwideSeriesDefaults.js';
import { addEvent, arrayMax, arrayMin, crisp, extend, merge, pick } from '../../Shared/Utilities.js';
/* *
 *
 *  Class
 *
 * */
/**
 * @private
 * @class
 * @name Highcharts.seriesTypes.variwide
 *
 * @augments Highcharts.Series
 */
class VariwideSeries extends ColumnSeries {
    /* *
     *
     * Functions
     *
     * */
    processData(force) {
        this.totalZ = 0;
        this.relZ = [];
        SeriesRegistry.seriesTypes.column.prototype.processData.call(this, force);
        const zData = this.getColumn('z');
        (this.xAxis.reversed ?
            zData.slice().reverse() :
            zData).forEach(function (z, i) {
            this.relZ[i] = this.totalZ;
            this.totalZ += z;
        }, this);
        if (this.xAxis.categories) {
            this.xAxis.variwide = true;
            this.xAxis.zData = zData; // Used for label rank
        }
        return;
    }
    /**
     * Translate an x value inside a given category index into the distorted
     * axis translation.
     *
     * @private
     * @function Highcharts.Series#postTranslate
     *
     * @param {number} index
     *        The category index
     *
     * @param {number} x
     *        The X pixel position in undistorted axis pixels
     *
     * @param {Highcharts.Point} point
     *        For crosshairWidth for every point
     *
     * @return {number}
     *         Distorted X position
     */
    postTranslate(index, x, point) {
        const axis = this.xAxis, relZ = this.relZ, i = axis.reversed ? relZ.length - index : index, goRight = axis.reversed ? -1 : 1, minPx = axis.toPixels(axis.reversed ?
            (axis.dataMax || 0) + axis.pointRange :
            (axis.dataMin || 0)), maxPx = axis.toPixels(axis.reversed ?
            (axis.dataMin || 0) :
            (axis.dataMax || 0) + axis.pointRange), len = Math.abs(maxPx - minPx), totalZ = this.totalZ, left = this.chart.inverted ?
            maxPx - (this.chart.plotTop - goRight * axis.minPixelPadding) :
            minPx - this.chart.plotLeft - goRight * axis.minPixelPadding, linearSlotLeft = i / relZ.length * len, linearSlotRight = (i + goRight) / relZ.length * len, slotLeft = (pick(relZ[i], totalZ) / totalZ) * len, slotRight = (pick(relZ[i + goRight], totalZ) / totalZ) * len, xInsideLinearSlot = (x - (left + linearSlotLeft));
        // Set crosshairWidth for every point (#8173)
        if (point) {
            point.crosshairWidth = slotRight - slotLeft;
        }
        return left + slotLeft +
            xInsideLinearSlot * (slotRight - slotLeft) /
                (linearSlotRight - linearSlotLeft);
    }
    translate() {
        // Temporarily disable crisping when computing original shapeArgs
        this.crispOption = this.options.crisp;
        this.options.crisp = false;
        super.translate();
        // Reset option
        this.options.crisp = this.crispOption;
    }
    /**
     * Function that corrects stack labels positions
     * @private
     */
    correctStackLabels() {
        const series = this, options = series.options, yAxis = series.yAxis;
        let pointStack, pointWidth, stack, xValue;
        for (const point of series.points) {
            xValue = point.x;
            pointWidth = point.shapeArgs.width;
            stack = yAxis.stacking.stacks[(series.negStacks &&
                point.y < (options.startFromThreshold ?
                    0 :
                    options.threshold) ?
                '-' :
                '') + series.stackKey];
            if (stack) {
                pointStack = stack[xValue];
                if (pointStack && !point.isNull) {
                    pointStack.setOffset(-(pointWidth / 2) || 0, pointWidth || 0, void 0, void 0, point.plotX, series.xAxis);
                }
            }
        }
    }
    getXExtremes(xData) {
        const max = arrayMax(xData), maxZ = this.getColumn('z')[xData.indexOf(max)];
        return {
            min: arrayMin(xData),
            max: max + (this.xAxis.categories ? 0 : maxZ)
        };
    }
}
/* *
 *
 *  Static Properties
 *
 * */
VariwideSeries.compose = VariwideComposition.compose;
VariwideSeries.defaultOptions = merge(ColumnSeries.defaultOptions, VariwideSeriesDefaults);
// Extend translation by distorting X position based on Z.
addEvent(VariwideSeries, 'afterColumnTranslate', function () {
    // Temporarily disable crisping when computing original shapeArgs
    const xAxis = this.xAxis, inverted = this.chart.inverted;
    let i = -1;
    // Distort the points to reflect z dimension
    for (const point of this.points) {
        ++i;
        const shapeArgs = point.shapeArgs || {}, { x = 0, width = 0 } = shapeArgs, { plotX = 0, tooltipPos, z = 0 } = point;
        let left, right;
        if (xAxis.variwide) {
            left = this.postTranslate(i, x, point);
            right = this.postTranslate(i, x + width);
            // For linear or datetime axes, the variwide column should start with X
            // and extend Z units, without modifying the axis.
        }
        else {
            left = plotX;
            right = xAxis.translate(point.x + z, false, false, false, true);
        }
        if (this.crispOption) {
            left = crisp(left, this.borderWidth);
            right = crisp(right, this.borderWidth);
        }
        shapeArgs.x = left;
        shapeArgs.width = Math.max(right - left, 1);
        // Crosshair position (#8083)
        point.plotX = (left + right) / 2;
        // Adjust the tooltip position
        if (tooltipPos) {
            if (!inverted) {
                tooltipPos[0] = shapeArgs.x + shapeArgs.width / 2;
            }
            else {
                tooltipPos[1] = xAxis.len - shapeArgs.x - shapeArgs.width / 2;
            }
        }
    }
    if (this.options.stacking) {
        this.correctStackLabels();
    }
}, { order: 2 });
extend(VariwideSeries.prototype, {
    irregularWidths: true,
    keysAffectYAxis: ['y'],
    pointArrayMap: ['y', 'z'],
    parallelArrays: ['x', 'y', 'z'],
    pointClass: VariwidePoint
});
SeriesRegistry.registerSeriesType('variwide', VariwideSeries);
/* *
 *
 *  Default Export
 *
 * */
export default VariwideSeries;
