import React from 'react';
import { List, AutoSizer } from 'react-virtualized';
import cn from 'classnames';
import { NoContent, IconButton, Button } from 'UI';
import { percentOf } from 'App/utils';
import { formatMs } from 'App/date';

import BarRow from './BarRow';
import stl from './timeTable.module.css';

import autoscrollStl from '../autoscroll.module.css'; //aaa

type Timed = {
    time: number;
};

type Durationed = {
    duration: number;
};

type CanBeRed = {
    //+isRed: boolean,
    isRed: () => boolean;
};

type Row = Timed & Durationed & CanBeRed;

type Line = {
    color: string; // Maybe use typescript?
    hint?: string;
    onClick?: any;
} & Timed;

type Column = {
    label: string;
    width: number;
    referenceLines?: Array<Line>;
    style?: Object;
} & RenderOrKey;

// type RenderOrKey = { // Disjoint?
//   render: Row => React.Node
// } | {
//   dataKey: string,
// }
type RenderOrKey =
    | {
          render?: (row: Row) => React.ReactNode;
          key?: string;
      }
    | {
          dataKey: string;
      };

type Props = {
    className?: string;
    rows: Array<Row>;
    children: Array<Column>;
};

type TimeLineInfo = {
    timestart: number;
    timewidth: number;
};

type State = TimeLineInfo & typeof initialState;

//const TABLE_HEIGHT = 195;
let _additionalHeight = 0;
const ROW_HEIGHT = 32;
//const VISIBLE_COUNT = Math.ceil(TABLE_HEIGHT/ROW_HEIGHT);

const TIME_SECTIONS_COUNT = 8;
const ZERO_TIMEWIDTH = 1000;
function formatTime(ms) {
    if (ms < 0) return '';
    return formatMs(ms);
}

function computeTimeLine(rows: Array<Row>, firstVisibleRowIndex: number, visibleCount): TimeLineInfo {
    const visibleRows = rows.slice(firstVisibleRowIndex, firstVisibleRowIndex + visibleCount + _additionalHeight);
    let timestart = visibleRows.length > 0 ? Math.min(...visibleRows.map((r) => r.time)) : 0;
    const timeend = visibleRows.length > 0 ? Math.max(...visibleRows.map((r) => r.time + r.duration)) : 0;
    let timewidth = timeend - timestart;
    const offset = timewidth / 70;
    if (timestart >= offset) {
        timestart -= offset;
    }
    timewidth *= 1.5; // += offset;
    if (timewidth === 0) {
        timewidth = ZERO_TIMEWIDTH;
    }
    return {
        timestart,
        timewidth,
    };
}

const initialState = {
    firstVisibleRowIndex: 0,
};

export default class TimeTable extends React.PureComponent<Props, State> {
    state = {
        ...computeTimeLine(this.props.rows, initialState.firstVisibleRowIndex, this.visibleCount),
        ...initialState,
    };

    get tableHeight() {
        return this.props.tableHeight || 195;
    }

    get visibleCount() {
        return Math.ceil(this.tableHeight / ROW_HEIGHT);
    }

    scroller = React.createRef();
    autoScroll = true;

    componentDidMount() {
        this.scroller.current.scrollToRow(this.props.activeIndex);
    }

    componentDidUpdate(prevProps: any, prevState: any) {
        // if (prevProps.rows.length !== this.props.rows.length &&
        //     this.autoScroll &&
        //     this.scroller.current != null) {
        //   this.scroller.current.scrollToRow(this.props.rows.length);
        // }
        if (
            prevState.firstVisibleRowIndex !== this.state.firstVisibleRowIndex ||
            (this.props.rows.length <= this.visibleCount + _additionalHeight && prevProps.rows.length !== this.props.rows.length)
        ) {
            this.setState({
                ...computeTimeLine(this.props.rows, this.state.firstVisibleRowIndex, this.visibleCount),
            });
        }
        if (this.props.activeIndex >= 0 && prevProps.activeIndex !== this.props.activeIndex) {
            this.scroller.current.scrollToRow(this.props.activeIndex);
        }
    }

    onScroll = ({ scrollTop, scrollHeight, clientHeight }: { scrollTop: number; scrollHeight: number; clientHeight: number }): void => {
        const firstVisibleRowIndex = Math.floor(scrollTop / ROW_HEIGHT + 0.33);

        if (this.state.firstVisibleRowIndex !== firstVisibleRowIndex) {
            this.autoScroll = scrollHeight - clientHeight - scrollTop < ROW_HEIGHT / 2;
            this.setState({ firstVisibleRowIndex });
        }
    };

    renderRow = ({ index, key, style: rowStyle }: any) => {
        const { activeIndex } = this.props;
        const { children: columns, rows, renderPopup, hoverable, onRowClick } = this.props;
        const { timestart, timewidth } = this.state;
        const row = rows[index];
        return (
            <div
                style={rowStyle}
                key={key}
                className={cn('border-b border-color-gray-light-shade', stl.row, {
                    [stl.hoverable]: hoverable,
                    'error color-red': !!row.isRed && row.isRed(),
                    'cursor-pointer': typeof onRowClick === 'function',
                    [stl.activeRow]: activeIndex === index,
                })}
                onClick={typeof onRowClick === 'function' ? () => onRowClick(row, index) : null}
                id="table-row"
            >
                {columns.map(({ dataKey, render, width }) => (
                    <div className={stl.cell} style={{ width: `${width}px` }}>
                        {render ? render(row) : row[dataKey] || <i className="color-gray-light">{'empty'}</i>}
                    </div>
                ))}
                <div className={cn('relative flex-1 flex', stl.timeBarWrapper)}>
                    <BarRow resource={row} timestart={timestart} timewidth={timewidth} popup={renderPopup} />
                </div>
            </div>
        );
    };

    onPrevClick = () => {
        let prevRedIndex = -1;
        for (let i = this.state.firstVisibleRowIndex - 1; i >= 0; i--) {
            if (this.props.rows[i].isRed()) {
                prevRedIndex = i;
                break;
            }
        }
        if (this.scroller.current != null) {
            this.scroller.current.scrollToRow(prevRedIndex);
        }
    };

    onNextClick = () => {
        let prevRedIndex = -1;
        for (let i = this.state.firstVisibleRowIndex + 1; i < this.props.rows.length; i++) {
            if (this.props.rows[i].isRed()) {
                prevRedIndex = i;
                break;
            }
        }
        if (this.scroller.current != null) {
            this.scroller.current.scrollToRow(prevRedIndex);
        }
    };

    render() {
        const { className, rows, children: columns, navigation = false, referenceLines = [], additionalHeight = 0, activeIndex } = this.props;
        const { timewidth, timestart } = this.state;

        _additionalHeight = additionalHeight;

        const sectionDuration = Math.round(timewidth / TIME_SECTIONS_COUNT);
        const timeColumns = [];
        if (timewidth > 0) {
            for (let i = 0; i < TIME_SECTIONS_COUNT; i++) {
                timeColumns.push(timestart + i * sectionDuration);
            }
        }

        const visibleRefLines = referenceLines.filter(({ time }) => time > timestart && time < timestart + timewidth);

        const columnsSumWidth = columns.reduce((sum, { width }) => sum + width, 0);

        return (
            <div className={cn(className, 'relative')}>
                {navigation && (
                    <div className={cn(autoscrollStl.navButtons, 'flex items-center')}>
                        <Button variant="text-primary" icon="chevron-up" onClick={this.onPrevClick} />
                        <Button variant="text-primary" icon="chevron-down" onClick={this.onNextClick} />
                        {/* <IconButton 
              size="small" 
              icon="chevron-up" 
              onClick={this.onPrevClick}
            /> */}
                        {/* <IconButton 
              size="small" 
              icon="chevron-down"
              onClick={this.onNextClick}
            /> */}
                    </div>
                )}
                <div className={stl.headers}>
                    <div className={stl.infoHeaders}>
                        {columns.map(({ label, width }) => (
                            <div className={stl.headerCell} style={{ width: `${width}px` }}>
                                {label}
                            </div>
                        ))}
                    </div>
                    <div className={stl.waterfallHeaders}>
                        {timeColumns.map((time, i) => (
                            <div className={stl.timeCell} key={`tc-${i}`}>
                                {formatTime(time)}
                            </div>
                        ))}
                    </div>
                </div>

                <NoContent size="small" show={rows.length === 0}>
                    <div className="relative">
                        <div className={stl.timePart} style={{ left: `${columnsSumWidth}px` }}>
                            {timeColumns.map((_, index) => (
                                <div key={`tc-${index}`} className={stl.timeCell} />
                            ))}
                            {visibleRefLines.map(({ time, color, onClick }) => (
                                <div
                                    className={cn(stl.refLine, `bg-${color}`)}
                                    style={{
                                        left: `${percentOf(time - timestart, timewidth)}%`,
                                        cursor: typeof onClick === 'function' ? 'click' : 'auto',
                                    }}
                                    onClick={onClick}
                                />
                            ))}
                        </div>
                        <AutoSizer disableHeight>
                            {({ width }) => (
                                <List
                                    ref={this.scroller}
                                    className={stl.list}
                                    height={this.tableHeight + additionalHeight}
                                    width={width}
                                    overscanRowCount={20}
                                    rowCount={rows.length}
                                    rowHeight={ROW_HEIGHT}
                                    rowRenderer={this.renderRow}
                                    onScroll={this.onScroll}
                                    scrollToAlignment="start"
                                    forceUpdateProp={timestart | timewidth | activeIndex}
                                />
                            )}
                        </AutoSizer>
                    </div>
                </NoContent>
            </div>
        );
    }
}
