/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
/* eslint-disable react/no-array-index-key, react/jsx-no-bind */
import dist from 'distributions';
import { Component } from 'react';
import { Table, Tr, Td, Thead, Th } from 'reactable';
import PropTypes from 'prop-types';
export const dataPropType = PropTypes.arrayOf(PropTypes.shape({
    group: PropTypes.arrayOf(PropTypes.string),
    values: PropTypes.arrayOf(PropTypes.shape({
        x: PropTypes.number,
        y: PropTypes.number,
    })),
}));
const propTypes = {
    alpha: PropTypes.number,
    data: dataPropType.isRequired,
    groups: PropTypes.arrayOf(PropTypes.string).isRequired,
    liftValPrec: PropTypes.number,
    metric: PropTypes.string.isRequired,
    pValPrec: PropTypes.number,
};
const defaultProps = {
    alpha: 0.05,
    liftValPrec: 4,
    pValPrec: 6,
};
class TTestTable extends Component {
    constructor(props) {
        super(props);
        this.state = {
            control: 0,
            liftValues: [],
            pValues: [],
        };
    }
    componentDidMount() {
        const { control } = this.state;
        this.computeTTest(control); // initially populate table
    }
    getLiftStatus(row) {
        const { control, liftValues } = this.state;
        // Get a css class name for coloring
        if (row === control) {
            return 'control';
        }
        const liftVal = liftValues[row];
        if (Number.isNaN(liftVal) || !Number.isFinite(liftVal)) {
            return 'invalid'; // infinite or NaN values
        }
        return liftVal >= 0 ? 'true' : 'false'; // green on true, red on false
    }
    getPValueStatus(row) {
        const { control, pValues } = this.state;
        if (row === control) {
            return 'control';
        }
        const pVal = pValues[row];
        if (Number.isNaN(pVal) || !Number.isFinite(pVal)) {
            return 'invalid';
        }
        return ''; // p-values won't normally be colored
    }
    getSignificance(row) {
        const { control, pValues } = this.state;
        const { alpha } = this.props;
        // Color significant as green, else red
        if (row === control) {
            return 'control';
        }
        // p-values significant below set threshold
        return pValues[row] <= alpha;
    }
    computeLift(values, control) {
        const { liftValPrec } = this.props;
        // Compute the lift value between two time series
        let sumValues = 0;
        let sumControl = 0;
        values.forEach((value, i) => {
            sumValues += value.y;
            sumControl += control[i].y;
        });
        return (((sumValues - sumControl) / sumControl) * 100).toFixed(liftValPrec);
    }
    computePValue(values, control) {
        const { pValPrec } = this.props;
        // Compute the p-value from Student's t-test
        // between two time series
        let diffSum = 0;
        let diffSqSum = 0;
        let finiteCount = 0;
        values.forEach((value, i) => {
            const diff = control[i].y - value.y;
            /* eslint-disable-next-line */
            if (isFinite(diff)) {
                finiteCount += 1;
                diffSum += diff;
                diffSqSum += diff * diff;
            }
        });
        const tvalue = -Math.abs(diffSum *
            Math.sqrt((finiteCount - 1) / (finiteCount * diffSqSum - diffSum * diffSum)));
        try {
            return (2 * new dist.Studentt(finiteCount - 1).cdf(tvalue)).toFixed(pValPrec); // two-sided test
        }
        catch (error) {
            return NaN;
        }
    }
    computeTTest(control) {
        // Compute lift and p-values for each row
        // against the selected control
        const { data } = this.props;
        const pValues = [];
        const liftValues = [];
        if (!data) {
            return;
        }
        for (let i = 0; i < data.length; i += 1) {
            if (i === control) {
                pValues.push('control');
                liftValues.push('control');
            }
            else {
                pValues.push(this.computePValue(data[i].values, data[control].values));
                liftValues.push(this.computeLift(data[i].values, data[control].values));
            }
        }
        this.setState({ control, liftValues, pValues });
    }
    render() {
        const { data, metric, groups } = this.props;
        const { control, liftValues, pValues } = this.state;
        if (!Array.isArray(groups) || groups.length === 0) {
            throw Error('Group by param is required');
        }
        // Render column header for each group
        const columns = groups.map((group, i) => (<Th key={i} column={group}>
        {group}
      </Th>));
        const numGroups = groups.length;
        // Columns for p-value, lift-value, and significance (true/false)
        columns.push(<Th key={numGroups + 1} column="pValue">
        p-value
      </Th>);
        columns.push(<Th key={numGroups + 2} column="liftValue">
        Lift %
      </Th>);
        columns.push(<Th key={numGroups + 3} column="significant">
        Significant
      </Th>);
        const rows = data.map((entry, i) => {
            const values = groups.map((group, j) => <Td key={j} column={group} data={entry.group[j]}/>);
            values.push(<Td key={numGroups + 1} className={this.getPValueStatus(i)} column="pValue" data={pValues[i]}/>);
            values.push(<Td key={numGroups + 2} className={this.getLiftStatus(i)} column="liftValue" data={liftValues[i]}/>);
            values.push(<Td key={numGroups + 3} className={this.getSignificance(i).toString()} column="significant" data={this.getSignificance(i)}/>);
            return (<Tr key={i} className={i === control ? 'control' : ''} onClick={this.computeTTest.bind(this, i)}>
          {values}
        </Tr>);
        });
        // When sorted ascending, 'control' will always be at top
        const sortConfig = groups.concat([
            {
                column: 'pValue',
                sortFunction: (a, b) => {
                    if (a === 'control') {
                        return -1;
                    }
                    if (b === 'control') {
                        return 1;
                    }
                    return a > b ? 1 : -1; // p-values ascending
                },
            },
            {
                column: 'liftValue',
                sortFunction: (a, b) => {
                    if (a === 'control') {
                        return -1;
                    }
                    if (b === 'control') {
                        return 1;
                    }
                    return parseFloat(a) > parseFloat(b) ? -1 : 1; // lift values descending
                },
            },
            {
                column: 'significant',
                sortFunction: (a, b) => {
                    if (a === 'control') {
                        return -1;
                    }
                    if (b === 'control') {
                        return 1;
                    }
                    return a > b ? -1 : 1; // significant values first
                },
            },
        ]);
        return (<div>
        <h3>{metric}</h3>
        <Table className="table" id={`table_${metric}`} sortable={sortConfig}>
          <Thead>{columns}</Thead>
          {rows}
        </Table>
      </div>);
    }
}
TTestTable.propTypes = propTypes;
TTestTable.defaultProps = defaultProps;
export default TTestTable;
//# sourceMappingURL=TTestTable.jsx.map