import React, { Component } from 'react';
import memoizeOne from 'memoize-one';
import { Empty, Spin } from 'antd';
import { ScrollSync, Grid, AutoSizer } from 'react-virtualized';
import _ from 'lodash';
import scrollbarSize from 'dom-helpers/scrollbarSize';
import styles from './Table.less';
import { TableProps, TableState, ColumnProps, RowProps } from './interface';
import Column from './Column';
import Cell from './Cell';
import { getMergeClassName, getMergeStyle } from '../utils/utils';

const borderStyle = '1px solid #ececec';

export default class AirTable extends Component<TableProps, TableState> {
    config: any;

    grid1: any;

    grid2: any;

    grid3: any;

    grid4: any;

    grid5: any;

    grid6: any;

    memoizeData: Function;

    memoizeLeftColumns: Function;

    memoizeRightColumns: Function;

    memoizeColumns: Function;

    memoizeLeftWidth: Function;

    memoizeTotalWidth: Function;

    static getDerivedStateFromProps(nextProps: TableProps, prevState: TableState) {
        const { columns, dataSource } = prevState;
        const nextState: TableState = {
            ...prevState,
        };
        if (JSON.stringify(dataSource) !== JSON.stringify(nextProps.dataSource)) {
            nextState.dataSource = nextProps.dataSource;
        }
        if (JSON.stringify(columns) !== JSON.stringify(nextProps.columns)) {
            nextState.columns = nextProps.columns;
        }
        return nextState;
    }

    constructor(props: TableProps) {
        super(props);
        const { columns, dataSource, width = 0, height = 0, rowHeight } = props;
        this.state = {
            columns,
            dataSource,
            tableWidth: width,
            tableHeight: height,
        };
        this.config = {
            overscanColumnCount: 5,
            overscanRowCount: 5,
            columnWidth: 150,
            rowHeight: rowHeight || 80,
            headerHeight: 60,
        };
        this.memoizeLeftColumns = memoizeOne(this.getLeftColumns);
        this.memoizeRightColumns = memoizeOne(this.getRightColumns);
        this.memoizeColumns = memoizeOne(this.getShowColumns);

        this.memoizeLeftWidth = memoizeOne(this.getLeftWidth);
        this.memoizeTotalWidth = memoizeOne(this.getTotalWidth);

        this.memoizeData = memoizeOne(this.filterData);
    }

    // 获取左侧固定列数量
    getLeftColumns = (columns: ColumnProps[]) => {
        return columns.filter((item) => {
            return !!item.showStatus && item.fixed && item.fixed !== 'right';
        });
    };

    // 获取右侧固定列数量
    getRightColumns = (columns: ColumnProps[]) => {
        return columns.filter((item) => {
            return !!item.showStatus && item.fixed === 'right';
        });
    };

    // 获取表格显示列
    getShowColumns = (columns: ColumnProps[]) => {
        return columns.filter((item) => {
            return !!item.showStatus && (!item.hideScope || item.hideScope.indexOf('LIST') === -1);
        });
    };

    // 获取左侧固定列总宽度
    getLeftWidth = (leftColumns: ColumnProps[], columns: ColumnProps[]) => {
        const { tableWidth = 0 } = this.state;
        const { totalWidth, configWidth, defaultWidthLen } = this.memoizeTotalWidth(columns);
        const { columnWidth } = this.config;
        let colWidth = columnWidth;
        if (totalWidth < tableWidth) {
            colWidth = _.floor((tableWidth - configWidth) / defaultWidthLen);
        }
        let total = 0;
        leftColumns.map((item) => {
            total += item.width || colWidth;
        });
        return total;
    };

    // 获取右侧列总宽度
    getTotalWidth = (columns: ColumnProps[]) => {
        const { columnWidth } = this.config;
        let configWidth = 0;
        let configWidthLen = 0;
        let defaultWidth = 0;
        let defaultWidthLen = 0;
        columns.map((item: any) => {
            if (item.width) {
                configWidth += item.width;
                configWidthLen += 1;
            } else {
                defaultWidth += columnWidth;
                defaultWidthLen += 1;
            }
        });
        const totalWidth = configWidth + defaultWidth;
        return {
            configWidth,
            defaultWidth,
            totalWidth,
            configWidthLen,
            defaultWidthLen,
        };
    };

    // 获取表格显示数据
    filterData = (columns: ColumnProps[], dataSource: RowProps[]) => {
        if (columns.length === 0 || dataSource.length === 0) {
            return [];
        }
        const cloneData = _.cloneDeep(dataSource);
        cloneData.map((item: any) => {
            item.rowData = item.rowData.filter((item2: any) => {
                return columns.some((temp: any) => {
                    return temp.columnName === item2.colName;
                });
            });
            return item;
        });
        return cloneData;
    };

    // 获取每列的宽度
    getColumnWidth = (columns: ColumnProps[], { index }: { index: number }) => {
        const { tableWidth = 0 } = this.state;
        const { totalWidth, configWidth, defaultWidthLen } = this.memoizeTotalWidth(columns);
        const { columnWidth } = this.config;
        let colWidth = columnWidth;
        if (totalWidth < tableWidth) {
            colWidth = _.floor((tableWidth - configWidth) / defaultWidthLen);
        }
        const columnObj = columns[index];
        if (columnObj && columnObj.width) {
            return columnObj.width;
        }
        return colWidth;
    };

    onResize = ({ width }: { width: number }) => {
        this.setState(
            {
                tableWidth: width,
            },
            () => {
                this.grid1 && this.grid1.recomputeGridSize();
                this.grid2 && this.grid2.recomputeGridSize();
                this.grid3 && this.grid3.recomputeGridSize();
                this.grid4 && this.grid4.recomputeGridSize();
                this.grid5 && this.grid5.recomputeGridSize();
                this.grid6 && this.grid6.recomputeGridSize();
            },
        );
    };

    // 下拉加载
    onScroll = ({ clientHeight, scrollHeight, scrollTop }: any) => {
        const height = clientHeight + scrollTop || 0;
        const { onScroll, distanceToLoad = 100 } = this.props;
        if (scrollTop > 0 && height >= scrollHeight - distanceToLoad && typeof onScroll === 'function') {
            onScroll();
        }
    };

    // 渲染表头
    renderHeaderCell = (
        { showColumns, position }: { showColumns: ColumnProps[]; position?: string },
        { columnIndex, key, style }: any,
    ) => {
        if (showColumns.length === 0) return null;
        const { sortConfig, showIndex, rowSelection, dataSource } = this.props;
        const {
            columnType = 1,
            columnName,
            columnChsName = '',
            columnAttrObj = {},
            sortFlag,
            questionText,
        } = showColumns[columnIndex];
        return (
            <div className={styles.headerCell} key={key} style={style}>
                <Column
                    columnType={String(columnType)}
                    columnName={columnName}
                    columnChsName={columnChsName}
                    columnAttrObj={columnAttrObj}
                    sortFlag={sortFlag}
                    sortConfig={sortConfig}
                    columnIndex={columnIndex}
                    showIndex={showIndex}
                    questionText={questionText}
                    rowSelection={position === 'right' ? false : rowSelection}
                    dataSource={dataSource}
                />
            </div>
        );
    };

    renderBodyRow = (
        { showColumns, showData, position }: { showColumns: ColumnProps[]; showData: RowProps; position?: string },
        { columnIndex, key, rowIndex, style }: any,
    ) => {
        const { rowClassName, rowStyle, bordered = false } = this.props;
        // 默认边框覆盖掉传过来的样式
        const rowBorderStyle: any = {
            borderLeft: 'none',
            borderRight: 'none',
            borderTop: 'none',
            borderBottom: borderStyle,
        };
        if (bordered) {
            rowBorderStyle.borderRight = borderStyle;
            if (columnIndex === 0) {
                rowBorderStyle.borderLeft = borderStyle;
            }
        }
        const className = getMergeClassName(styles.bodyCell, rowClassName, { rowIndex });
        const rowStyleObject = getMergeStyle(style, rowStyle, { rowIndex });
        const mergedRowStyle = {
            ...rowStyleObject,
            ...rowBorderStyle,
        };
        return this.renderBodyCellContainer(
            { showColumns, showData, position },
            {
                columnIndex,
                key,
                rowIndex,
                rowClassName: className,
                rowStyle: mergedRowStyle,
            },
        );
    };

    renderBodyCellContainer = (
        { showColumns, showData, position }: { showColumns: ColumnProps[]; showData: RowProps; position?: string },
        { columnIndex, key, rowIndex, rowClassName, rowStyle }: any,
    ) => {
        const columnConfig = showColumns[columnIndex];
        const { cellClassName, cellStyle } = columnConfig;
        const className = getMergeClassName(rowClassName, cellClassName, { columnIndex });
        const mergedCellStyle = getMergeStyle(rowStyle, cellStyle, { columnIndex });
        return this.renderBodyCell(
            { showColumns, showData, position },
            {
                columnIndex,
                key,
                rowIndex,
                cellClassName: className,
                cellStyle: mergedCellStyle,
            },
        );
    };

    // 渲染数据
    renderBodyCell = (
        { showColumns, showData, position }: { showColumns: ColumnProps[]; showData: RowProps; position?: string },
        { columnIndex, key, rowIndex, cellClassName, cellStyle }: any,
    ) => {
        const { columns, dataSource } = this.state;
        const {
            emitChangeCell,
            paginationConfig,
            showIndex,
            emptyPlaceholder,
            cellEditable,
            rowSelection,
        } = this.props;
        if (showColumns.length === 0 || showData.length === 0) {
            return;
        }
        const record: any = dataSource[rowIndex]; // 行数据
        const rowData: any = showData[rowIndex]; // 行数据
        const columnConfig: any = showColumns[columnIndex]; // 列属性
        const columnData: any = rowData.rowData[columnIndex]; // 列数据
        const cellData: any = columnData && columnData.cellValueList; // 列数据
        return (
            <Cell
                key={key}
                rowIndex={rowIndex}
                columnIndex={columnIndex}
                columnConfig={columnConfig}
                value={cellData}
                record={record}
                emitChangeCell={emitChangeCell}
                cellClassName={cellClassName}
                cellStyle={cellStyle}
                columns={columns}
                paginationConfig={paginationConfig}
                showIndex={showIndex}
                emptyPlaceholder={emptyPlaceholder}
                cellEditable={cellEditable}
                rowSelection={position === 'right' ? false : rowSelection}
            />
        );
    };

    render() {
        const { loading, noDataPlaceholder } = this.props;
        const { columns, dataSource, tableWidth = 0 } = this.state;
        const { overscanColumnCount, overscanRowCount, rowHeight, columnWidth, headerHeight } = this.config;
        const scrollbarWidth = scrollbarSize() || 0;
        const showColumns = this.memoizeColumns(columns);
        const leftColumns = this.memoizeLeftColumns(columns);
        const rightColumns = this.memoizeRightColumns(columns);
        // 有隐藏列时，修正数据与列头不匹配问题
        const showData = this.memoizeData(showColumns, dataSource);
        // 左侧固定列数量
        const leftCount = leftColumns.length;
        const rightCount = rightColumns.length;
        const columnCount = showColumns.length;
        const rowCount = showData.length;
        const leftWidth = this.memoizeLeftWidth(leftColumns, showColumns);
        const rightWidth = this.memoizeLeftWidth(rightColumns, showColumns);
        let totalHeight: number = rowCount * rowHeight;
        const { totalWidth } = this.memoizeTotalWidth(showColumns);
        if (totalWidth > tableWidth) {
            totalHeight = rowCount * rowHeight + scrollbarWidth;
        }
        return (
            <ScrollSync>
                {({ onScroll, scrollLeft, scrollTop }: any) => {
                    return (
                        <div className={styles.tableContainer} style={{ height: totalHeight + headerHeight }}>
                            {totalWidth > tableWidth && leftCount > 0 && (
                                <div
                                    className={styles.leftSideContainer}
                                    style={{
                                        width: `${leftWidth}px`,
                                        height: `${totalHeight - scrollbarWidth + headerHeight}px`,
                                    }}
                                >
                                    {
                                        // 左侧表头
                                        <div
                                            className={styles.leftSideHeaderContainer}
                                            style={{
                                                left: 0,
                                            }}
                                        >
                                            <Grid
                                                ref={(dom: any) => {
                                                    this.grid1 = dom;
                                                }}
                                                className={styles.headerGrid}
                                                columnWidth={this.getColumnWidth.bind(this, showColumns)}
                                                columnCount={leftCount}
                                                width={leftWidth}
                                                rowHeight={headerHeight}
                                                rowCount={1}
                                                height={headerHeight}
                                                cellRenderer={this.renderHeaderCell.bind(this, {
                                                    showColumns: leftColumns,
                                                })}
                                            />
                                        </div>
                                    }
                                    {
                                        // 左侧固定列
                                        <div
                                            className={styles.leftSideGridContainer}
                                            style={{
                                                left: 0,
                                                top: headerHeight,
                                            }}
                                        >
                                            <Grid
                                                ref={(dom: any) => {
                                                    this.grid2 = dom;
                                                }}
                                                className={styles.sideGrid}
                                                overscanRowCount={overscanRowCount}
                                                cellRenderer={this.renderBodyRow.bind(this, {
                                                    showColumns: leftColumns,
                                                    showData,
                                                })}
                                                columnWidth={this.getColumnWidth.bind(this, showColumns)}
                                                columnCount={leftCount}
                                                width={leftWidth}
                                                rowHeight={rowHeight}
                                                rowCount={rowCount}
                                                height={totalHeight - scrollbarWidth}
                                                scrollTop={scrollTop}
                                            />
                                        </div>
                                    }
                                </div>
                            )}
                            <div className={styles.centerContainer}>
                                <AutoSizer disableHeight onResize={this.onResize}>
                                    {({ width }: any) => {
                                        return (
                                            <div>
                                                {
                                                    // 中部表头
                                                    <div
                                                        style={{
                                                            backgroundColor: 'rgba(246, 246, 246, 1)',
                                                            height: headerHeight,
                                                            width,
                                                        }}
                                                    >
                                                        <Grid
                                                            ref={(dom: any) => {
                                                                this.grid3 = dom;
                                                            }}
                                                            className={styles.headerGrid}
                                                            overscanColumnCount={overscanColumnCount}
                                                            columnWidth={this.getColumnWidth.bind(this, showColumns)}
                                                            columnCount={columnCount}
                                                            width={width}
                                                            rowHeight={headerHeight}
                                                            rowCount={1}
                                                            height={headerHeight}
                                                            scrollLeft={scrollLeft}
                                                            cellRenderer={this.renderHeaderCell.bind(this, {
                                                                showColumns,
                                                            })}
                                                        />
                                                    </div>
                                                }
                                                {
                                                    // 中部内容
                                                    <div
                                                        style={{
                                                            height: totalHeight,
                                                            minHeight:
                                                                !dataSource || dataSource.length === 0
                                                                    ? '300px'
                                                                    : 'auto',
                                                            width,
                                                        }}
                                                    >
                                                        {rowCount > 0 ? (
                                                            <Grid
                                                                ref={(dom: any) => {
                                                                    this.grid4 = dom;
                                                                }}
                                                                className={styles.centerGrid}
                                                                overscanColumnCount={overscanColumnCount}
                                                                overscanRowCount={overscanRowCount}
                                                                columnWidth={this.getColumnWidth.bind(
                                                                    this,
                                                                    showColumns,
                                                                )}
                                                                columnCount={columnCount}
                                                                width={width}
                                                                rowHeight={rowHeight}
                                                                rowCount={rowCount}
                                                                onScroll={(...arg: Array<Object>) => {
                                                                    onScroll(...arg);
                                                                    this.onScroll(arg[0]);
                                                                }}
                                                                height={totalHeight}
                                                                cellRenderer={this.renderBodyRow.bind(this, {
                                                                    showColumns,
                                                                    showData,
                                                                })}
                                                            />
                                                        ) : (
                                                            columnCount > 0 &&
                                                            !loading && (
                                                                <Empty
                                                                    imageStyle={{ marginTop: '100px' }}
                                                                    description={
                                                                        noDataPlaceholder || 'It’s empty here.'
                                                                    }
                                                                />
                                                            )
                                                        )}
                                                    </div>
                                                }
                                            </div>
                                        );
                                    }}
                                </AutoSizer>
                            </div>
                            {totalWidth > tableWidth && rightCount > 0 && (
                                <div
                                    className={styles.rightSideContainer}
                                    style={{
                                        width: `${rightWidth}px`,
                                        height: `${totalHeight - scrollbarWidth + headerHeight}px`,
                                    }}
                                >
                                    {
                                        // 右侧表头
                                        <div
                                            className={styles.rightSideHeaderContainer}
                                            style={{
                                                right: 0,
                                            }}
                                        >
                                            <Grid
                                                ref={(dom: any) => {
                                                    this.grid5 = dom;
                                                }}
                                                className={styles.headerGrid}
                                                columnWidth={this.getColumnWidth.bind(this, showColumns)}
                                                columnCount={rightCount}
                                                width={rightWidth}
                                                rowHeight={headerHeight}
                                                rowCount={1}
                                                height={headerHeight}
                                                cellRenderer={this.renderHeaderCell.bind(this, {
                                                    showColumns: rightColumns,
                                                    position: 'right',
                                                })}
                                            />
                                        </div>
                                    }
                                    {
                                        // 右侧固定列
                                        <div
                                            className={styles.rightSideGridContainer}
                                            style={{
                                                position: 'absolute',
                                                right: 0,
                                                top: headerHeight,
                                            }}
                                        >
                                            <Grid
                                                ref={(dom: any) => {
                                                    this.grid6 = dom;
                                                }}
                                                className={styles.sideGrid}
                                                overscanRowCount={overscanRowCount}
                                                cellRenderer={this.renderBodyRow.bind(this, {
                                                    showColumns: rightColumns,
                                                    showData,
                                                    position: 'right',
                                                })}
                                                columnWidth={this.getColumnWidth.bind(this, showColumns)}
                                                columnCount={rightCount}
                                                width={rightWidth}
                                                rowHeight={rowHeight}
                                                rowCount={rowCount}
                                                height={totalHeight - scrollbarWidth}
                                                scrollTop={scrollTop}
                                            />
                                        </div>
                                    }
                                </div>
                            )}
                            <div className={styles.fillHandleWrapper} />
                            <div className={styles.loading}>
                                {/*<Spin spinning={loading} />*/}
                            </div>
                        </div>
                    );
                }}
            </ScrollSync>
        );
    }
}
