/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sophie Bremer
 *
 * */
'use strict';
import DataModifier from './DataModifier.js';
import FormulaParser from '../Formula/FormulaParser.js';
import FormulaProcessor from '../Formula/FormulaProcessor.js';
/* *
 *
 *  Class
 *
 * */
/**
 * Replaces formula strings in a table with calculated values.
 *
 * @class
 * @name Highcharts.DataModifier.types.MathModifier
 * @augments Highcharts.DataModifier
 */
class MathModifier extends DataModifier {
    /* *
     *
     *  Constructor
     *
     * */
    constructor(options) {
        super();
        this.options = {
            ...MathModifier.defaultOptions,
            ...options
        };
    }
    /* *
     *
     *  Functions
     *
     * */
    modifyTable(table, eventDetail) {
        const modifier = this;
        modifier.emit({ type: 'modify', detail: eventDetail, table });
        const alternativeSeparators = modifier.options.alternativeSeparators, formulaColumns = (modifier.options.formulaColumns ||
            table.getColumnIds()), modified = table.getModified();
        for (let i = 0, iEnd = formulaColumns.length, columnId; i < iEnd; ++i) {
            columnId = formulaColumns[i];
            if (formulaColumns.indexOf(columnId) >= 0) {
                modified.setColumn(columnId, modifier.processColumn(table, columnId));
            }
        }
        const columnFormulas = (modifier.options.columnFormulas || []);
        for (let i = 0, iEnd = columnFormulas.length, columnFormula, formula; i < iEnd; ++i) {
            columnFormula = columnFormulas[i];
            formula = FormulaParser.parseFormula(columnFormula.formula, alternativeSeparators);
            modified.setColumn(columnFormula.column, modifier.processColumnFormula(formula, table, columnFormula.rowStart, columnFormula.rowEnd));
        }
        modifier.emit({ type: 'afterModify', detail: eventDetail, table });
        return table;
    }
    /**
     * Process a column by replacing formula strings with calculated values.
     *
     * @private
     *
     * @param {Highcharts.DataTable} table
     * Table to extract column from and use as reference.
     *
     * @param {string} columnId
     * Id of column to process.
     *
     * @param {number} rowIndex
     * Row index to start the replacing process from.
     *
     * @return {Highcharts.DataTableColumn}
     * Returns the processed table column.
     */
    processColumn(table, columnId, rowIndex = 0) {
        const alternativeSeparators = this.options.alternativeSeparators, column = (table.getColumn(columnId, true) || [])
            .slice(rowIndex > 0 ? rowIndex : 0);
        for (let i = 0, iEnd = column.length, cacheFormula = [], cacheString = '', cell; i < iEnd; ++i) {
            cell = column[i];
            if (typeof cell === 'string' &&
                cell[0] === '=') {
                try {
                    // Use cache while formula string is repetitive
                    cacheFormula = (cacheString === cell ?
                        cacheFormula :
                        FormulaParser.parseFormula(cell.substring(1), alternativeSeparators));
                    // Process parsed formula string
                    column[i] =
                        FormulaProcessor.processFormula(cacheFormula, table);
                }
                catch {
                    column[i] = NaN;
                }
            }
        }
        return column;
    }
    /**
     * Process a column by replacing cell values with calculated values from a
     * given formula.
     *
     * @private
     *
     * @param {Highcharts.Formula} formula
     * Formula to use for processing.
     *
     * @param {Highcharts.DataTable} table
     * Table to extract column from and use as reference.
     *
     * @param {number} rowStart
     * Row index to start the replacing process from.
     *
     * @param {number} rowEnd
     * Row index to end the replacing process.
     *
     * @return {Highcharts.DataTableColumn}
     * Returns the processed table column.
     */
    processColumnFormula(formula, table, rowStart = 0, rowEnd = table.getRowCount()) {
        rowStart = rowStart >= 0 ? rowStart : 0;
        rowEnd = rowEnd >= 0 ? rowEnd : table.getRowCount() + rowEnd;
        const column = [], modified = table.getModified();
        for (let i = 0, iEnd = (rowEnd - rowStart); i < iEnd; ++i) {
            try {
                column[i] = FormulaProcessor.processFormula(formula, modified);
            }
            catch {
                column[i] = NaN;
            }
            finally {
                formula = FormulaProcessor.translateReferences(formula, 0, 1);
            }
        }
        return column;
    }
}
/* *
 *
 *  Static Properties
 *
 * */
/**
 * Default options of MathModifier.
 * @private
 */
MathModifier.defaultOptions = {
    type: 'Math',
    alternativeSeparators: false
};
DataModifier.registerType('Math', MathModifier);
/* *
 *
 *  Default Export
 *
 * */
export default MathModifier;
