"use strict";
/* eslint-disable complexity */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicUpdatesMixin = void 0;
const locale_1 = __importDefault(require("../locale"));
const defaults_1 = require("../defaults");
const series_1 = require("../utils/series");
const axes_1 = require("../utils/axes");
const common_1 = require("../utils/common");
function setLocaleImpl(yagr, batch, locale) {
    yagr.utils.i18n = (0, locale_1.default)(locale);
    batch.redrawLegend = true;
}
function setThemeImpl(yagr, themeValue, batch) {
    yagr.utils.theme.setTheme(themeValue);
    const availableThemes = ['light', 'light-hc', 'dark', 'dark-hc'];
    const classesToRemove = availableThemes.map((theme) => `yagr_theme_${theme}`);
    yagr.root.classList.remove(...classesToRemove);
    yagr.root.classList.add('yagr_theme_' + themeValue);
    batch.redraw = [false, true];
}
function setAxesImpl(yagr, batch, axes) {
    const { x, ...rest } = axes;
    if (x) {
        const xAxis = yagr.uplot.axes.find(({ scale }) => scale === defaults_1.DEFAULT_X_SCALE);
        if (xAxis) {
            (0, axes_1.updateAxis)(yagr, xAxis, { scale: defaults_1.DEFAULT_X_SCALE, ...x });
        }
    }
    Object.entries(rest).forEach(([scaleName, scaleConfig]) => {
        const axis = yagr.uplot.axes.find(({ scale }) => scale === scaleName);
        if (axis) {
            (0, axes_1.updateAxis)(yagr, axis, { scale: scaleName, ...scaleConfig });
        }
    });
    batch.redraw = (0, axes_1.getRedrawOptionsForAxesUpdate)(axes);
}
function setFocusImpl(yagr, lineId, focus) {
    var _a;
    const seriesIdx = lineId === null ? null : yagr.state.y2uIdx[lineId];
    (_a = yagr.plugins.cursor) === null || _a === void 0 ? void 0 : _a.focus(seriesIdx, focus);
    yagr.uplot.setSeries(seriesIdx, { focus });
}
function setVisibleImpl(yagr, lineId, show, updateLegend, batch) {
    const seriesIdx = lineId === null ? null : yagr.state.y2uIdx[lineId];
    const seriesCfg = lineId === null ? yagr.config.series : [yagr.config.series.find(({ id }) => id === lineId)];
    seriesCfg.forEach((serie) => {
        if (serie) {
            serie.show = show;
        }
    });
    batch.fns.push(() => {
        yagr.uplot.setSeries(seriesIdx, {
            show,
        });
    });
    yagr.options.series = yagr.uplot.series;
    let shouldRebuildStacks = false;
    if (seriesIdx) {
        const series = yagr.uplot.series[seriesIdx];
        series.show = show;
        const scaleName = series.scale || defaults_1.DEFAULT_Y_SCALE;
        const scale = yagr.config.scales[scaleName];
        shouldRebuildStacks = Boolean(scale && scale.stacking);
    }
    else {
        shouldRebuildStacks = yagr.options.series.reduce((acc, serie) => {
            var _a;
            const { scale } = serie;
            serie.show = show;
            return Boolean((scale && ((_a = yagr.config.scales[scale]) === null || _a === void 0 ? void 0 : _a.stacking)) || acc);
        }, false);
    }
    if (shouldRebuildStacks) {
        // batch.reopt = true;
        batch.recalc = true;
        batch.fns.push(() => {
            var _a;
            yagr.uplot.setData(yagr.series, true);
            updateLegend && ((_a = yagr.plugins.legend) === null || _a === void 0 ? void 0 : _a.update());
        });
    }
}
function setScalesImpl(yagr, scales, batch) {
    let stackingIsChanged = false;
    let normalizationIsChanged = false;
    Object.entries(scales).forEach(([scaleName, scaleConfig]) => {
        const scale = yagr.config.scales[scaleName];
        if (scale) {
            const { stacking } = scale;
            const { stacking: newStacking } = scaleConfig;
            if (stacking !== newStacking) {
                stackingIsChanged = true;
            }
            if (scaleConfig.normalize !== scale.normalize ||
                scaleConfig.normalizeBase !== scale.normalizeBase) {
                normalizationIsChanged = true;
            }
        }
    });
    const isChangingOnlyMinMax = Object.entries(scales).every(([key, scaleConfig]) => {
        const cfg = yagr.config.scales[key];
        const { min: pMin, max: pMax, ...pRest } = cfg;
        const { min: nMin, max: nMax, ...nRest } = scaleConfig;
        const isChangedSomething = (0, common_1.deepIsEqual)(nRest, pRest) === false;
        return !isChangedSomething && (pMin !== nMin || pMax !== nMax);
    });
    const isChangingXAxis = Object.keys(scales).includes(defaults_1.DEFAULT_X_SCALE);
    /**
     * In case if we change only min/max on data-scales, then we can just use uplot.setScale,
     * otherwise we need to rebuild all series and stacks in order to apply new scales;
     */
    if (isChangingOnlyMinMax && !isChangingXAxis) {
        return Object.entries(scales).forEach(([scaleName, scaleConfig]) => {
            batch.fns.push(() => {
                yagr.uplot.setScale(scaleName, {
                    min: scaleConfig.min,
                    max: scaleConfig.max,
                });
            });
        });
    }
    if (stackingIsChanged || normalizationIsChanged) {
        batch.reinit = true;
    }
    yagr.config.scales = scales;
    batch.reinit = true;
}
function isChanged(oldConfig, newConfig) {
    return function isChangedPath(path, predicate) {
        const oldValue = (0, common_1.get)(oldConfig, path);
        const newValue = (0, common_1.get)(newConfig, path);
        return predicate ? !predicate(oldValue, newValue) : oldValue !== newValue;
    };
}
function areSeriesChanged(a, b) {
    if (a.length !== (b === null || b === void 0 ? void 0 : b.length)) {
        return true;
    }
    const mapA = new Map();
    const mapB = new Map();
    a.forEach((serie) => {
        mapA.set(serie.id, serie);
    });
    b.forEach((serie) => {
        mapB.set(serie.id, serie);
    });
    if (b.some(({ id }) => !mapA.has(id)) || a.some(({ id }) => !mapB.has(id))) {
        return true;
    }
    return false;
}
function setConfigImpl(yagr, batch, newConfig, fullUpdate = false) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
    if (fullUpdate) {
        yagr.config = { ...yagr.config, ...newConfig };
        batch.reinit = true;
        return;
    }
    const isChangedKey = isChanged(yagr.config, newConfig);
    if (newConfig.title && isChangedKey('title')) {
        yagr.setTitle(newConfig.title);
    }
    if (((_b = (_a = newConfig.chart) === null || _a === void 0 ? void 0 : _a.appearance) === null || _b === void 0 ? void 0 : _b.theme) && isChangedKey('chart.appearance.theme')) {
        yagr.setTheme((_d = (_c = newConfig.chart) === null || _c === void 0 ? void 0 : _c.appearance) === null || _d === void 0 ? void 0 : _d.theme);
    }
    if (((_f = (_e = newConfig.chart) === null || _e === void 0 ? void 0 : _e.appearance) === null || _f === void 0 ? void 0 : _f.locale) && isChangedKey('chart.appearance.locale')) {
        yagr.setLocale((_h = (_g = newConfig.chart) === null || _g === void 0 ? void 0 : _g.appearance) === null || _h === void 0 ? void 0 : _h.locale);
    }
    if (newConfig.axes && isChangedKey('axes', common_1.deepIsEqual)) {
        yagr.setAxes(newConfig.axes);
    }
    if (newConfig.scales && isChangedKey('scales', common_1.deepIsEqual)) {
        yagr.setScales(newConfig.scales);
    }
    const isChangedSeries = Boolean(newConfig.series) && areSeriesChanged(yagr.config.series, newConfig.series);
    if (isChangedSeries) {
        batch.redrawLegend = true;
    }
    if (newConfig.series || newConfig.timeline) {
        yagr.setSeries((_j = newConfig.timeline) !== null && _j !== void 0 ? _j : yagr.config.timeline, (_k = newConfig.series) !== null && _k !== void 0 ? _k : yagr.config.series, {
            incremental: false,
        });
    }
    if (newConfig.tooltip && isChangedKey('tooltip')) {
        (_l = yagr.plugins.tooltip) === null || _l === void 0 ? void 0 : _l.updateOptions(newConfig.tooltip);
    }
    if (newConfig.legend && isChangedKey('legend')) {
        batch.reinit = true;
    }
    (_m = yagr.plugins.tooltip) === null || _m === void 0 ? void 0 : _m.reset();
    batch.reopt = true;
    yagr.config = { ...yagr.config, ...newConfig };
}
function setSeriesImpl(batch, timelineOrSeriesOrId, maybeSeries, options = {
    incremental: true,
    splice: false,
}) {
    let timeline = [], series = [], updateId = null, useIncremental = false, shouldRecalcData = false, useFullyRedraw;
    if (['number', 'string'].includes(typeof timelineOrSeriesOrId)) {
        useIncremental = false;
        useFullyRedraw = false;
        series = [maybeSeries];
        updateId = timelineOrSeriesOrId;
    }
    else if (typeof timelineOrSeriesOrId[0] === 'number') {
        timeline = timelineOrSeriesOrId;
        series = maybeSeries;
        useIncremental = Boolean(options.incremental);
        useFullyRedraw = !options.incremental;
    }
    else {
        series = timelineOrSeriesOrId;
        useFullyRedraw = true;
    }
    if (this.isEmpty && series.some(({ data }) => data === null || data === void 0 ? void 0 : data.length)) {
        batch.reinit = true;
        return;
    }
    if (useFullyRedraw === false) {
        let shouldUpdateCursror = false;
        useIncremental && this.config.timeline.push(...timeline);
        series.forEach((serie) => {
            var _a, _b;
            let matched = typeof updateId === 'number'
                ? this.config.series[0]
                : this.config.series.find(({ id }) => id === serie.id || id === updateId);
            let id = matched === null || matched === void 0 ? void 0 : matched.id;
            if (typeof updateId === 'number' && this.state.y2uIdx[updateId]) {
                matched = this.config.series[updateId];
                id = updateId;
            }
            if (matched && id) {
                const { data, ...rest } = serie;
                const seriesIdx = this.state.y2uIdx[id];
                /** @TODO fixme (see Annotations.1) */
                if (matched.type === 'dots' ||
                    serie.type === 'dots' ||
                    ((_a = this.config.chart.series) === null || _a === void 0 ? void 0 : _a.type) === 'dots') {
                    batch.reinit = true;
                }
                if (useIncremental) {
                    matched.data = data ? matched.data.concat(data) : matched.data;
                }
                else if (data === null || data === void 0 ? void 0 : data.length) {
                    matched.data = data;
                    /**
                     * We're reprocessing data on every series' data change,
                     * cause we don't know is it required to process current line or not
                     * Possible non clear pattern:
                     *  - series may contain string values to process out with `interpolate` option
                     *  - series may be in stack group
                     * @TODO fixme when you got a good idea
                     */
                    shouldRecalcData = true;
                }
                const newSeries = (0, series_1.configureSeries)(this, Object.assign(matched, rest), seriesIdx);
                const opts = this.options.series[seriesIdx];
                const uOpts = this.uplot.series[seriesIdx];
                if (uOpts._focus === null ? true : uOpts._focus !== newSeries.focus) {
                    batch.fns.push(() => {
                        this.uplot.setSeries(seriesIdx, { focus: newSeries.focus });
                    });
                }
                if (uOpts.color !== newSeries.color) {
                    shouldUpdateCursror = true;
                }
                if (newSeries.scale && ((_b = this.config.scales[newSeries.scale]) === null || _b === void 0 ? void 0 : _b.stacking)) {
                    shouldRecalcData = true;
                }
                (0, series_1.overrideSeriesInUpdate)(opts, newSeries);
                (0, series_1.overrideSeriesInUpdate)(uOpts, newSeries);
                /**
                 * Handle showInGraph transitions. uPlot uses `series.show` to
                 * determine which series contribute to scale auto-range; there
                 * is no pre-scale hook, so the only correct way to exclude a
                 * series is to keep it `show: false` in uPlot while marking it
                 * as `_tempHidden` so that Yagr-side consumers (tooltip etc.)
                 * still treat it as visible.
                 *
                 * Snapshot the previous uPlot `show` value BEFORE
                 * `overrideSeriesInUpdate` mutates it below, then derive the
                 * target uPlot `show` value and the expected `_tempHidden`
                 * state. After the override we restore the correct uPlot-side
                 * `show` and only queue a `setSeries` call if uPlot actually
                 * needs to react (otherwise we'd just thrash the batch).
                 */
                const prevUplotShow = uOpts.show;
                const userShow = newSeries.show !== false;
                const hideForGraph = newSeries.showInGraph === false && userShow;
                const targetUplotShow = hideForGraph ? false : userShow;
                const targetTempHidden = hideForGraph;
                /**
                 * `overrideSeriesInUpdate` just synchronously copied
                 * `newSeries.show` (the user-facing value) into `uOpts.show`.
                 * For showInGraph handling we instead want uPlot to see
                 * `targetUplotShow`. Apply our chosen state on top of the
                 * override, and queue an actual `setSeries` call only when
                 * the value visible to uPlot really changed — this is what
                 * forces uPlot to recompute the scale, and it avoids
                 * re-entrant setSeries storms that could freeze the UI.
                 */
                uOpts.show = targetUplotShow;
                uOpts._tempHidden = targetTempHidden;
                opts._tempHidden = targetTempHidden;
                if (prevUplotShow !== targetUplotShow) {
                    batch.fns.push(() => {
                        this.uplot.setSeries(seriesIdx, { show: targetUplotShow });
                    });
                }
            }
            else {
                batch.fns.push(() => {
                    const newSeries = (0, series_1.configureSeries)(this, serie, this.config.series.length);
                    this.state.y2uIdx[newSeries.id] = this.uplot.series.length;
                    this.uplot.addSeries(newSeries, this.config.series.length);
                });
                this.config.series.push(serie);
            }
        });
        if (shouldUpdateCursror) {
            batch.fns.push(() => {
                var _a;
                (_a = this.plugins.cursor) === null || _a === void 0 ? void 0 : _a.updatePoints();
            });
        }
        if (options.splice) {
            const sliceLength = series[0].data.length;
            this.config.series.forEach((s) => {
                s.data.splice(0, sliceLength);
            });
            this.config.timeline.splice(0, timeline.length);
        }
    }
    else {
        this.config.timeline = timeline;
        this.config.series = series;
        batch.reinit = true;
    }
    if (!batch.reinit) {
        this._batch.fns.push(() => { var _a, _b; return (_b = (_a = this.plugins) === null || _a === void 0 ? void 0 : _a.tooltip) === null || _b === void 0 ? void 0 : _b.reset(); });
        if (shouldRecalcData || timeline.length) {
            batch.recalc = true;
            batch.fns.push(() => {
                this.uplot.setData(this.series);
            });
        }
    }
}
class DynamicUpdatesMixin {
    /**
     * @public
     * @param locale string | Record<string, string>
     * @description Set's locale of chart and redraws all locale-dependent elements.
     */
    setLocale(locale) {
        this.batch((batch) => setLocaleImpl(this, batch, locale));
    }
    /**
     * @public
     * @param title YagrTitleConfig
     * @description Set's title of chart and redraws all title-dependent elements.
     */
    setTitle(title) {
        this.batch((batch) => {
            this.config.title = title;
            this.initTitle();
            batch.redraw = [true, true];
        });
    }
    /**
     * @public
     * @param themeValue YagrTheme
     * @description Set's theme of chart and redraws all theme-dependent elements.
     */
    setTheme(themeValue) {
        this.batch((batch) => setThemeImpl(this, themeValue, batch));
    }
    /**
     * @public
     * @param axes axes config
     * @description Sets new axes config and redraws all axes.
     */
    setAxes(axes) {
        this.batch((batch) => setAxesImpl(this, batch, axes));
    }
    setSeries(timelineOrSeriesOrId, maybeSeries, options = {
        incremental: true,
        splice: false,
    }) {
        this.batch((batch) => setSeriesImpl.call(this, batch, timelineOrSeriesOrId, maybeSeries, options));
    }
    /**
     * @public
     * @param lineId string | null
     * @param focus boolean
     * @description Sets focus on line with given id. If id is null, removes focus from all lines.
     * @returns void
     */
    setFocus(lineId, focus) {
        this.batch(() => setFocusImpl(this, lineId, focus));
    }
    /**
     * @public
     * @param lineId string | null
     * @param show boolean
     * @description Sets visibility of line with given id. If id is null, sets visibility of all lines.
     */
    setVisible(lineId, show, updateLegend = true) {
        this.batch((batch) => setVisibleImpl(this, lineId, show, updateLegend, batch));
    }
    /**
     * @public
     * @param scales Record<string, Partial<ScaleConfi>>
     * @description Sets new scales config and redraws.
     */
    setScales(scales) {
        this.batch((batch) => setScalesImpl(this, scales, batch));
    }
    /**
     * @public
     * @experimental
     * @param newConfig Partial<YagrConfig>
     * @descriptino Sets new config and redraws.
     */
    setConfig(newConfig, fullUpdate = false) {
        this.batch((batch) => setConfigImpl(this, batch, newConfig, fullUpdate));
    }
}
exports.DynamicUpdatesMixin = DynamicUpdatesMixin;
/**
 * Annotations:
 * 1. If we're operating with dots type then uPlot will be reinitialized
 *    cause it's not possible to re-render dot's markers without reinit
 *
 */
