/*
 * Copyright 2021 The Kubernetes Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import React from 'react';
import stringColoring from 'string-similarity-coloring';
import { sameRow } from '@kui-shell/core';
import { Chart, ChartAxis, ChartBar, ChartLabel } from '@patternfly/react-charts';
function sameState(A, B) {
    return (A.scale === B.scale &&
        A.counts &&
        A.counts.length === B.counts.length &&
        A.counts.every((countBefore, idx) => countBefore === B.counts[idx]) &&
        A.rows &&
        A.rows.every((rowBefore, idx) => sameRow(rowBefore, B.rows[idx])));
}
export default class Histogram extends React.PureComponent {
    constructor(props) {
        super(props);
        this.horizontal = true;
        this.barHeight = 10;
        this.barSpacing = 0.15; // 15% of barHeight spacing between bars
        this.axisLabelFontSize = 0.9 * this.barHeight;
        this.minAxisLabelChars = 4;
        this.maxAxisLabelChars = 13;
        this.barLabelFontSize = 0.65 * this.barHeight;
        this.minBarLabelChars = 1;
        this.maxBarLabelChars = 6;
        this.state = Histogram.filterRows(props.response.body, props.response.colorBy !== undefined);
    }
    componentDidCatch(error, errorInfo) {
        console.error(error, errorInfo);
    }
    static getDerivedStateFromProps(props, state) {
        const newState = Histogram.filterRows(props.response.body, props.response.colorBy !== undefined);
        // to avoid re-renders when nothing has changed...
        if (state && sameState(state, newState)) {
            // nothing has changed, disable animation
            // see https://github.com/IBM/kui/issues/7175
            return {
                animate: false
            };
        }
        else {
            // otherwise, we have changes to the state
            return newState;
        }
    }
    static filterRows(rows, useFancyColoring) {
        const countOf = (row) => parseInt(row.attributes.find(_ => _.key && /^count$/i.test(_.key)).value, 10);
        const counts = rows.map(countOf);
        const yMax = counts.reduce((yMax, count) => Math.max(yMax, count), 0);
        const filteredRowsPriorToSorting = rows.filter((row, ridx) => (!Histogram.isTiny(counts[ridx], yMax) ? 1 : 0));
        if (!useFancyColoring) {
            const filteredRows = filteredRowsPriorToSorting;
            return {
                rows: filteredRows,
                counts: filteredRows.map(countOf),
                animate: filteredRows.length < Histogram.maxAnimatableRows,
                scale: filteredRows.length === rows.length ? 'linear' : 'log',
                colors: undefined
            };
        }
        else {
            // assign colors to the rows, and then sort the rows to group
            // nearby colors(in the color space) so that they are also close
            // geographically
            const sortedByCount = filteredRowsPriorToSorting.slice().sort((a, b) => countOf(b) - countOf(a));
            const colors = stringColoring(sortedByCount.map(_ => _.rowKey || _.name), { theme: 'patternfly4' });
            const filteredRowsForSorting = sortedByCount
                .map((row, idx) => ({ row, color: colors[idx] }))
                .sort((a, b) => {
                // note how we move the "random" color assignments to the
                // bottom; these are color assignments due to running out of
                // primary colors; they should only affect the smaller buckets
                return a.color.isRandomAssignment
                    ? -1
                    : b.color.isRandomAssignment
                        ? 1
                        : b.color.primary - a.color.primary ||
                            countOf(a.row) - countOf(b.row) ||
                            b.color.secondary - a.color.secondary;
            });
            const filteredRows = filteredRowsForSorting.map(_ => _.row);
            return {
                rows: filteredRows,
                counts: filteredRows.map(countOf),
                animate: filteredRows.length < Histogram.maxAnimatableRows,
                scale: filteredRows.length === rows.length ? 'linear' : 'log',
                colors: filteredRowsForSorting.map(_ => _.color.color)
            };
        }
    }
    /** heuristic to allow "just enough" space for axis labels */
    leftPad() {
        const longestAxisLabel = this.state.rows.reduce((maxLength, row) => Math.max(maxLength, (row.rowKey || row.name).length), 0);
        const leftPad = Math.min(this.maxAxisLabelChars * this.axisLabelFontSize, Math.max(this.minAxisLabelChars * this.axisLabelFontSize, longestAxisLabel * this.axisLabelFontSize));
        return leftPad;
    }
    /** heuristic to allow right space for bar labels */
    rightPad() {
        const longestBarLabel = this.state.counts.reduce((maxLength, count) => Math.max(maxLength, `${count}`.length), 0);
        const rightPad = Math.min(this.maxBarLabelChars * this.barLabelFontSize, Math.max(this.minBarLabelChars * this.barLabelFontSize, longestBarLabel * this.barLabelFontSize));
        return rightPad;
    }
    rows() {
        // re: 0.375, see https://github.com/IBM/kui/issues/7209
        return (React.createElement(Chart, { domainPadding: Histogram.domainPadding, height: 0.375 * Histogram.domainPadding + this.state.rows.length * this.barHeight * (1 + this.barSpacing) + 10 * 2, horizontal: this.horizontal, padding: {
                left: this.leftPad(),
                right: this.rightPad(),
                top: 0,
                bottom: 0
            } },
            this.axis(),
            this.bars()));
    }
    axis() {
        return (React.createElement(ChartAxis, { style: {
                axis: { stroke: 'var(--color-base04)' },
                tickLabels: {
                    fontFamily: 'var(--font-sans-serif)',
                    fontSize: this.axisLabelFontSize,
                    fill: 'var(--color-text-02)'
                }
            } }));
    }
    static isTiny(count, yMax) {
        return count / yMax < 0.01;
    }
    bars() {
        return (React.createElement(ChartBar, { animate: this.state.animate && { onLoad: { duration: 0 }, duration: 200 }, barWidth: this.barHeight, scale: { y: this.state.scale }, style: {
                data: {
                    fill: !this.state.colors
                        ? 'var(--color-base05)'
                        : ({ index }) => this.state.colors[index] || 'var(--color-base05)',
                    stroke: !this.state.colors ? 'var(--color-base04)' : undefined,
                    strokeWidth: 0.5
                },
                labels: { fontFamily: 'var(--font-sans-serif)', fontSize: this.barLabelFontSize }
            }, data: this.state.rows.map((row, ridx) => ({
                x: row.rowKey || row.name,
                y: this.state.counts[ridx]
            })), labels: _ => Math.round(_.datum.y), labelComponent: React.createElement(ChartLabel, null) }));
    }
    render() {
        if (!this.state || !this.state.rows || this.state.rows.length === 0) {
            return React.createElement(React.Fragment, null);
        }
        return (React.createElement("div", { className: "kui--data-table-container kui--data-table-container" },
            React.createElement("div", { className: "kui--table-like-wrapper pf-c-table pf-m-compact kui--histogram" }, this.rows())));
    }
}
Histogram.domainPadding = 10;
Histogram.maxAnimatableRows = 50; // Victory doesn't seem to animate well when a great many rows; too much lag
//# sourceMappingURL=Histogram.js.map