"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasOneVisibleLine = exports.getSeriesInLegend = void 0;
const defaults_1 = require("../../defaults");
const common_1 = require("../../utils/common");
const events_1 = require("../../utils/events");
const utils_1 = require("./utils");
const ALL_SERIES_IDX = 'null';
const PAGINATION_BUTTON_HEIGHT = 18;
const TOTAL_LEGEND_VERTICAL_PADDING_BOTTOM = 20;
const TOTAL_LEGEND_VERTICAL_PADDING_TOP = 48;
const DEFAULT_FONT_SIZE = 12;
const DEFAULT_LEGEND_PLACE_RATIO = 0.3;
const getSeriesInLegend = (series) => {
    return series.filter(({ id, showInLegend }) => id !== defaults_1.DEFAULT_X_SERIE_NAME && showInLegend !== false);
};
exports.getSeriesInLegend = getSeriesInLegend;
const hasOneVisibleLine = (series) => {
    return series.some(({ id, show, showInLegend, showInGraph }) => id !== defaults_1.DEFAULT_X_SERIE_NAME && showInLegend !== false && show && showInGraph !== false);
};
exports.hasOneVisibleLine = hasOneVisibleLine;
/**
 * Any series with show: true in the legend (regardless of showInGraph).
 * Needed for behaviour: 'extended' — solo / show all are tied to show, as before showInGraph.
 */
const hasAnySeriesWithShowTrue = (series) => {
    return series.some(({ id, show, showInLegend }) => id !== defaults_1.DEFAULT_X_SERIE_NAME && showInLegend !== false && show);
};
const isSeriesVisibleOnGraph = (serie) => {
    return serie.show !== false && serie.showInGraph !== false;
};
/** If the line hidden with option showInGraph, but it showed at tooltip — we are disabling legend item. */
const isLegendItemDisabled = (serie) => {
    return serie.showInGraph === false;
};
const shouldRestoreShowAfterShowAll = (serie) => {
    return serie.show === false && serie.showInGraph === false;
};
const getPrependingTitle = (i18n, series) => {
    return series.length > 3 && i18n((0, exports.hasOneVisibleLine)(series) ? 'hide-all' : 'show-all');
};
const getPrependingTitleId = (series) => {
    return (series.length > 3 && ALL_SERIES_IDX) || undefined;
};
class LegendPlugin {
    constructor() {
        this.pagesCount = 0;
        this.state = {
            page: 0,
            pages: 1,
            pageSize: 0,
            rowsPerPage: 1,
            paginated: false,
            requiredSpace: 0,
            totalSpace: 0,
        };
        this.itemsHtml = '';
        this.preInit = (yagr, options, uplotOptions) => {
            this.yagr = yagr;
            this.options = Object.assign({
                show: false,
                position: 'bottom',
                fontSize: DEFAULT_FONT_SIZE,
                maxLegendSpace: DEFAULT_LEGEND_PLACE_RATIO,
                className: undefined,
                behaviour: 'basic',
            }, options || {});
            this.calc(uplotOptions);
        };
        this.init = (u) => {
            var _a;
            if (!this.options.show) {
                return;
            }
            this.uplot = u;
            /** Removing native uPlot legend */
            (_a = u.root.querySelector('.u-legend')) === null || _a === void 0 ? void 0 : _a.remove();
            this.render();
        };
        this.update = () => {
            const series = this.yagr.root.querySelectorAll('[data-serie-id]');
            series.forEach((serieNode) => {
                var _a;
                const serieId = serieNode.getAttribute('data-serie-id');
                if (!serieId || serieId === ALL_SERIES_IDX) {
                    return;
                }
                const serie = (_a = this.uplot) === null || _a === void 0 ? void 0 : _a.series[this.yagr.state.y2uIdx[serieId]];
                const hidden = (serie === null || serie === void 0 ? void 0 : serie.show) === false;
                const disabled = Boolean(serie && isLegendItemDisabled(serie));
                serieNode.classList[hidden ? 'add' : 'remove']('yagr-legend__item_hidden');
                serieNode.classList[disabled ? 'add' : 'remove']('yagr-legend__item_disabled');
            });
        };
        this.measureLegend = (body) => {
            const rootEl = this.yagr.root;
            const pseudo = (0, common_1.html)('div', {
                class: 'yagr-legend',
                style: { visibility: 'hidden' },
            }, body);
            rootEl.appendChild(pseudo);
            const items = pseudo.childNodes[0];
            const result = items.getBoundingClientRect();
            pseudo.remove();
            return result;
        };
        this.nextPage = () => {
            const { state } = this;
            this.state.page += 1;
            if (this.items) {
                this.items.style.transform = `translate(0, ${-1 * state.page * state.pageSize}px)`;
                this.renderPagination();
            }
        };
        this.prevPage = () => {
            const { state } = this;
            this.state.page -= 1;
            if (this.items) {
                this.items.style.transform = `translate(0, ${-1 * state.page * state.pageSize}px)`;
                this.renderPagination();
            }
        };
    }
    redraw() {
        if (!this.options.show) {
            return;
        }
        this.render();
    }
    destroy() {
        var _a;
        if (this._onDestroy) {
            this._onDestroy();
        }
        (_a = this.legendEl) === null || _a === void 0 ? void 0 : _a.remove();
    }
    applyHandlers() {
        const { yagr, uplot: u } = this;
        if (!u) {
            return () => { };
        }
        const series = yagr.root.querySelectorAll('[data-serie-id]');
        const unsubsribe = [];
        const onSerieClickBasic = (serieNode) => () => {
            if (serieNode.classList.contains('yagr-legend__item_disabled')) {
                return;
            }
            const serieId = serieNode.getAttribute('data-serie-id');
            const seriesToToggle = [];
            const series = (0, exports.getSeriesInLegend)(u.series);
            let isShowAll = false;
            if (serieId === ALL_SERIES_IDX) {
                const nextToggleState = !(0, exports.hasOneVisibleLine)(series);
                isShowAll = nextToggleState;
                series.forEach((s) => {
                    if (isLegendItemDisabled(s)) {
                        return;
                    }
                    seriesToToggle.push([s, nextToggleState]);
                });
            }
            else {
                const serie = series.find(({ id }) => id === serieId);
                if (!serie || isLegendItemDisabled(serie)) {
                    return;
                }
                seriesToToggle.push([serie, !isSeriesVisibleOnGraph(serie)]);
            }
            seriesToToggle.forEach(([serie, nextState]) => {
                if (isSeriesVisibleOnGraph(serie) === nextState) {
                    return;
                }
                const node = yagr.root.querySelector(`[data-serie-id="${(0, utils_1.escapeAttribute)(serie.id)}"]`);
                yagr.setVisible(serie.id, nextState, false);
                node === null || node === void 0 ? void 0 : node.classList[nextState ? 'remove' : 'add']('yagr-legend__item_hidden');
            });
            if (serieId === ALL_SERIES_IDX && isShowAll) {
                series.forEach((s) => {
                    if (!shouldRestoreShowAfterShowAll(s)) {
                        return;
                    }
                    const node = yagr.root.querySelector(`[data-serie-id="${(0, utils_1.escapeAttribute)(s.id)}"]`);
                    yagr.setVisible(s.id, true, false);
                    node === null || node === void 0 ? void 0 : node.classList.remove('yagr-legend__item_hidden');
                });
            }
            const allSeriesItem = yagr.root.querySelector('.yagr-legend__all-series');
            if (allSeriesItem) {
                const title = getPrependingTitle(this.yagr.utils.i18n, series);
                allSeriesItem.innerHTML = title || '';
            }
        };
        const onSerieClickExtended = (serieNode) => {
            const changeVisibility = (id, visibility) => {
                const node = yagr.root.querySelector(`[data-serie-id="${(0, utils_1.escapeAttribute)(id)}"]`);
                yagr.setVisible(id, visibility, false);
                node === null || node === void 0 ? void 0 : node.classList[visibility ? 'remove' : 'add']('yagr-legend__item_hidden');
            };
            const toggleSerie = (serie) => {
                changeVisibility(serie.id, !isSeriesVisibleOnGraph(serie));
            };
            const selectSerie = (serie) => {
                this.state.startSerieRange = serie;
                const series = (0, exports.getSeriesInLegend)(u.series);
                const otherSeries = series.filter((s) => s.id !== serie.id);
                const otherVisibility = !hasAnySeriesWithShowTrue(otherSeries) && serie.show !== false;
                series.forEach((s) => {
                    if (isLegendItemDisabled(s)) {
                        return;
                    }
                    const visibility = serie.id === s.id ? true : otherVisibility;
                    changeVisibility(s.id, visibility);
                });
                if (otherVisibility) {
                    series.forEach((s) => {
                        if (!shouldRestoreShowAfterShowAll(s)) {
                            return;
                        }
                        const node = yagr.root.querySelector(`[data-serie-id="${(0, utils_1.escapeAttribute)(s.id)}"]`);
                        yagr.setVisible(s.id, true, false);
                        node === null || node === void 0 ? void 0 : node.classList.remove('yagr-legend__item_hidden');
                    });
                }
                else {
                    // In solo mode, hide series with showInGraph: false from tooltip
                    series.forEach((s) => {
                        if (s.showInGraph === false) {
                            yagr.setVisible(s.id, false, false);
                        }
                    });
                }
            };
            const selectRange = (serie) => {
                const series = (0, exports.getSeriesInLegend)(u.series);
                // If startSerieRange is undefined then startSerieRange = first valid legend element
                if (!this.state.startSerieRange) {
                    this.state.startSerieRange = series[0];
                }
                const range = [];
                series.forEach((s, i) => {
                    var _a;
                    if (s.id === serie.id) {
                        range.push(i);
                    }
                    // There is no 'else' because exist case when startSerieRange and target serie are same elements
                    if (s.id === ((_a = this.state.startSerieRange) === null || _a === void 0 ? void 0 : _a.id)) {
                        range.push(i);
                    }
                });
                series.forEach((s, i) => {
                    if (isLegendItemDisabled(s)) {
                        return;
                    }
                    const visibility = i >= range[0] && i <= range[1];
                    changeVisibility(s.id, visibility);
                });
            };
            return (e) => {
                const serieId = serieNode.getAttribute('data-serie-id');
                const serie = u.series.find(({ id }) => id === serieId);
                if (!serie) {
                    return;
                }
                if (isLegendItemDisabled(serie)) {
                    e.preventDefault();
                    return;
                }
                e.preventDefault();
                if (e.ctrlKey || e.metaKey) {
                    toggleSerie(serie);
                }
                else if (e.shiftKey) {
                    selectRange(serie);
                }
                else {
                    selectSerie(serie);
                }
            };
        };
        const onSerieClick = {
            basic: onSerieClickBasic,
            extended: onSerieClickExtended,
        };
        const onSerieMouseEnter = (serieNode) => () => {
            const serieId = serieNode.getAttribute('data-serie-id');
            if (serieNode.classList.contains('yagr-legend__item_hidden') ||
                serieNode.classList.contains('yagr-legend__item_disabled') ||
                serieId === ALL_SERIES_IDX) {
                return;
            }
            const targetSerie = this.yagr.uplot.series.find(({ id }) => id === serieId);
            if (targetSerie) {
                yagr.setFocus(targetSerie.id, true);
                // fix bug with focus in stacking column chart
                yagr.redraw(true, false);
            }
        };
        const onSerieMouseLeave = () => {
            yagr.setFocus(null, true);
            // fix bug with focus in stacking column chart
            yagr.redraw(true, false);
        };
        series.forEach((serieNode) => {
            const onClick = onSerieClick[this.options.behaviour || 'basic'](serieNode);
            const onFocus = onSerieMouseEnter(serieNode);
            serieNode.addEventListener('click', onClick);
            serieNode.addEventListener('mouseenter', onFocus);
            serieNode.addEventListener('mouseleave', onSerieMouseLeave);
            serieNode.addEventListener('mousedown', events_1.preventMouseEvents);
            unsubsribe.push(() => {
                serieNode.removeEventListener('click', onClick);
                serieNode.removeEventListener('mouseenter', onFocus);
                serieNode.removeEventListener('mouseleave', onSerieMouseLeave);
                serieNode.removeEventListener('mousedown', events_1.preventMouseEvents);
            });
        });
        const destroy = () => unsubsribe.forEach((fn) => fn());
        this._onDestroy = destroy;
        return destroy;
    }
    render() {
        var _a, _b;
        let reRender = false;
        const { uplot: u, options } = this;
        if (!u) {
            return;
        }
        let legendEl = this.yagr.root.querySelector('.yagr-legend');
        if (legendEl) {
            reRender = true;
        }
        else {
            legendEl = (0, common_1.html)('div', {
                class: `yagr-legend yagr-legend__${this.options.position} ${(options === null || options === void 0 ? void 0 : options.className) || ''}`,
            });
        }
        if (!legendEl) {
            return;
        }
        if (!reRender) {
            if (options.position === 'top') {
                u.root.before(legendEl);
            }
            else {
                (_a = u.root) === null || _a === void 0 ? void 0 : _a.after(legendEl);
            }
        }
        this.legendEl = legendEl;
        if (!this.itemsHtml || reRender) {
            this.calc(this.yagr.options);
        }
        legendEl.innerHTML = `<div class="yagr-legend__container" style="height: ${this.state.requiredSpace}px">${this.itemsHtml}</div>`;
        this.items = legendEl.querySelector('.yagr-legend__items');
        this.container = legendEl.querySelector('.yagr-legend__container');
        if (this.state.paginated) {
            const pagination = this.renderPagination();
            (_b = this.container) === null || _b === void 0 ? void 0 : _b.after(pagination);
        }
        else {
            this.items.style.justifyContent = 'center';
        }
        this.applyHandlers();
    }
    renderPagination() {
        const { state } = this;
        let pagination = this.yagr.root.querySelector('.yagr-legend__pagination');
        if (pagination) {
            const nextPage = pagination.querySelector('.yagr-legend__icon-down');
            const prevPage = pagination.querySelector('.yagr-legend__icon-up');
            nextPage.removeEventListener('click', this.nextPage);
            prevPage.removeEventListener('click', this.prevPage);
        }
        else {
            pagination = (0, common_1.html)('div', {
                class: 'yagr-legend__pagination',
            });
        }
        const upClassName = state.page === 0 ? 'yagr-legend__icon-up_disabled' : '';
        const downClassName = state.page === state.pages - 1 ? 'yagr-legend__icon-down_disabled' : '';
        pagination.innerHTML = `<span class="yagr-legend__icon-up ${upClassName}"></span>
<span class="yagr-legend__pagination-text">${state.page + 1}/${state.pages}</span>
<span class="yagr-legend__icon-down ${downClassName}"></span>`;
        const nextPage = pagination.querySelector('.yagr-legend__icon-down');
        const prevPage = pagination.querySelector('.yagr-legend__icon-up');
        if (!downClassName) {
            nextPage.addEventListener('click', this.nextPage);
        }
        if (!upClassName) {
            prevPage.addEventListener('click', this.prevPage);
        }
        return pagination;
    }
    createIconLineElement(serie) {
        const iconLineElement = (0, common_1.html)('span', {
            class: `yagr-legend__icon yagr-legend__icon_${serie.type}`,
            style: { 'background-color': this.yagr.getSerieLegendColor(serie) },
        });
        return iconLineElement;
    }
    createSerieNameElement(serie) {
        const serieNameElement = (0, common_1.html)('span', { class: 'yagr-legend__name' });
        serieNameElement.textContent = serie.name || 'unnamed';
        return serieNameElement;
    }
    renderItems(uplotOptions) {
        const seriesInLegend = (0, exports.getSeriesInLegend)(uplotOptions.series);
        const title = getPrependingTitle(this.yagr.utils.i18n, seriesInLegend);
        const titleId = this.options.behaviour !== 'extended' && getPrependingTitleId(seriesInLegend);
        const series = titleId
            ? [titleId, ...seriesInLegend]
            : [...seriesInLegend];
        const content = series
            .map((serie) => {
            let serieContent;
            let sId;
            let additionalCn = ' ';
            if (serie === ALL_SERIES_IDX) {
                serieContent = title;
                sId = titleId;
                additionalCn = ' yagr-legend__all-series ';
            }
            else {
                sId = (0, utils_1.escapeAttribute)(serie.id);
                const icon = this.createIconLineElement(serie);
                const name = this.createSerieNameElement(serie);
                serieContent = `${icon.outerHTML}${name.outerHTML}`;
            }
            if (typeof serie === 'string') {
                return `<div class="yagr-legend__item${additionalCn}" data-serie-id="${sId}">${serieContent}</div>`;
            }
            const isHidden = !isSeriesVisibleOnGraph(serie);
            const isDisabled = isLegendItemDisabled(serie);
            let itemClass = `yagr-legend__item${additionalCn}`.trim();
            if (isHidden) {
                itemClass += ' yagr-legend__item_hidden';
            }
            if (isDisabled) {
                itemClass += ' yagr-legend__item_disabled';
            }
            const aria = isDisabled ? ' aria-disabled="true"' : '';
            return `<div class="${itemClass}" data-serie-id="${sId}"${aria}>${serieContent}</div>`;
        })
            .join('');
        return `<div class="yagr-legend__items">${content}</div>`;
    }
    calc(uplotOptions) {
        if (!this.options.show) {
            return;
        }
        const chartHeight = uplotOptions.height - this.VERTICAL_PADDING;
        const html = this.renderItems(uplotOptions);
        const { height: requiredHeight } = this.measureLegend(html);
        const rowHeight = this.options.fontSize + 2;
        const maxPossiblePlace = chartHeight * this.options.maxLegendSpace;
        const rowsPerPage = Math.floor(maxPossiblePlace / rowHeight);
        const itemsRowsPerPage = rowsPerPage - 1;
        const itemsPageSize = Math.min(itemsRowsPerPage * rowHeight, maxPossiblePlace);
        const paginatedPageSize = Math.min(rowsPerPage * rowHeight, maxPossiblePlace);
        const paginated = requiredHeight > itemsPageSize && itemsPageSize > 0;
        const requiredSpace = Math.min(paginated ? paginatedPageSize : itemsPageSize, requiredHeight);
        const pages = Math.ceil(requiredHeight / itemsPageSize);
        const additionalSpace = paginated
            ? this.VERTICAL_PADDING + PAGINATION_BUTTON_HEIGHT
            : this.VERTICAL_PADDING;
        this.state.requiredSpace = requiredSpace;
        this.state.totalSpace = requiredSpace + additionalSpace;
        this.state.paginated = paginated;
        this.state.page = this.state.page || 0;
        this.state.pages = pages;
        this.state.pageSize = itemsPageSize;
        this.state.rowsPerPage = rowsPerPage;
        this.itemsHtml = html;
    }
    get VERTICAL_PADDING() {
        return this.options.position === 'bottom'
            ? TOTAL_LEGEND_VERTICAL_PADDING_BOTTOM
            : TOTAL_LEGEND_VERTICAL_PADDING_TOP;
    }
}
exports.default = LegendPlugin;
