import React, { Component } from 'react';
import { Checkbox } from 'antd';
import memoizeOne from 'memoize-one';
import { ScrollSync, Grid, AutoSizer } from 'react-virtualized';
import 'react-virtualized/styles.css';
import _ from 'lodash';
import styles from './Table.less';
import { TableProps, TableState } from './interface';
import Column from './Column';
import Cell from './Cell';
import extendIcon from '../assets/extend.png';
import IconFont from './base/extra/iconFont';
import scrollbarSize from 'dom-helpers/scrollbarSize';

export default class AirTable extends Component<TableProps, TableState> {
    private indexCount: any;
    config: any;
    memoizeLeftCount: Function;
    memoizeData: Function;
    static getDerivedStateFromProps(nextProps: TableProps, prevState: TableState) {
        const { prevProps } = prevState;
        let nextState: TableState = {
            ...prevState,
            prevProps: nextProps,
        };
        if (JSON.stringify(prevProps.dataSource) !== JSON.stringify(nextProps.dataSource)) {
            nextState.dataSource = nextProps.dataSource;
        }
        if (JSON.stringify(prevProps.columns) !== JSON.stringify(nextProps.columns)) {
            nextState.columns = nextProps.columns;
        }
        if (JSON.stringify(prevProps.checked) !== JSON.stringify(nextProps.checked)) {
            nextState.checked = nextProps.checked;
        }
        nextState.tableHeight = document.body.clientHeight;
        nextState.tableWidth = document.body.clientWidth;
        return nextState;
    }
    constructor(props: TableProps) {
        super(props);
        const { columns, dataSource, tableId, checked = [] } = props;
        this.state = {
            columns,
            dataSource,
            checked,
            tableId,
            prevProps: props,
            tableWidth: 0,
            tableHeight: 0,
        };
        this.config = {
            overscanColumnCount: 5,
            overscanRowCount: 5,
            columnWidth: 200,
            rowHeight: 40,
        };
        this.memoizeLeftCount = memoizeOne(this.getLeftCount);
        this.memoizeData = memoizeOne(this.filterData);
        this.indexCount = {};
    }
    componentDidMount() {
        window.addEventListener('resize', this.resize, false);
    }
    //重置窗口高度
    resize = () => {
        this.setState({
            tableHeight: document.body.clientHeight,
            tableWidth: document.body.clientWidth,
        });
    };
    //获取左侧固定列数量
    getLeftCount = (columns: []) => {
        let len = 0;
        columns.map((item: any) => {
            if (item.fixed) {
                len += 1;
            }
        });
        return len;
    };
    //表头隐藏时过滤数据源
    filterData = (columns: [], dataSource: []) => {
        if (columns.length === 0 || dataSource.length === 0) {
            return [];
        }
        let cloneData = _.cloneDeep(dataSource);
        let i = 0;
        this.indexCount = {};
        cloneData.map((item1: any) => {
            const itemData = item1;
            itemData.rowData = itemData.rowData.filter((item2: any) =>
                columns.some((temp: any) => temp.columnName === item2.colName),
            );
            if (itemData.groupId) {
                if (this.indexCount[itemData.groupId]) {
                    this.indexCount[itemData.groupId].push(itemData.id);
                } else {
                    i += 1;
                    this.indexCount[itemData.groupId] = [itemData.id];
                }
                itemData.index = i;
            }
            return itemData;
        });
        return cloneData;
    };
    // 复选框选择
    changeCheckbox = (record: any, flag: boolean) => {
        const { changeChecked, hasGroup } = this.props;
        const { checked, dataSource } = this.state;
        let temp = checked.slice();
        if (flag) {
            if (hasGroup) {
                const selected = dataSource.filter((item: any) => {
                    return item.groupId === record.groupId;
                });
                temp = temp.concat(selected);
            } else {
                temp.push(record);
            }
        } else {
            if (hasGroup) {
                temp = temp.filter((item: any) => {
                    return item.groupId !== record.groupId;
                });
            }
            const index = temp.findIndex((item: any) => {
                return item.id === record.id;
            });
            temp.splice(index, 1);
        }
        if (typeof changeChecked === 'function') {
            changeChecked({ checked: temp });
        }
    };
    // 检测当前分组是否全部选中
    getCheckedAll = () => {
        const { checked, dataSource } = this.state;
        const temp = checked.slice();
        const result = temp.filter((v: any) => {
            return dataSource.some((item: any) => {
                return item.id === v.id;
            });
        });
        return result.length === dataSource.length && result.length !== 0;
    };
    // 全选/反选
    changeCheckboxAll = () => {
        const { checked, dataSource } = this.state;
        const { changeChecked } = this.props;
        const temp = checked.slice();
        const flag = this.getCheckedAll();
        let result = [];
        if (flag) {
            result = temp.concat(dataSource).filter((v: any) => {
                return (
                    temp.some((item: any) => {
                        return item.id === v.id;
                    }) &&
                    !dataSource.some((item: any) => {
                        return item.id === v.id;
                    })
                );
            });
        } else {
            result = temp.concat(
                dataSource.filter((v: any) => {
                    return !temp.some((item: any) => {
                        return item.id === v.id;
                    });
                }),
            );
        }
        if (typeof changeChecked === 'function') {
            changeChecked({ checked: result });
        }
    };
    // 下拉加载
    onScrollRow = ({ clientHeight, scrollTop }: any) => {
        const height = clientHeight + scrollTop || 0;
        const { dataSource = [] } = this.state;
        const { rowHeight } = this.config;
        const { onScroll } = this.props;
        const offset = 100;
        if (dataSource.length === 0) return;
        if (height + offset >= dataSource.length * rowHeight && typeof onScroll === 'function') {
            onScroll();
        }
    };
    //渲染表头
    renderHeaderCell = ({ columnIndex, key, rowIndex, style }: any) => {
        const { columns } = this.state;
        if (columns.length === 0) return null;
        const { columnType, columnChsName, width, columnAttrObj } = columns[columnIndex] || {};
        if (columnIndex === 0) {
            return (
                <div key={key} className={styles.leftHeaderCell} style={{ ...style, width: width || 200 }}>
                    <div className={styles.cellNo}>
                        <Checkbox checked={this.getCheckedAll()} onClick={this.changeCheckboxAll} />
                    </div>
                    <div className={styles.cellContent}>
                        <Column
                            type={String(columnType)}
                            title={columnChsName}
                            width={width}
                            columnAttrObj={columnAttrObj}
                        />
                    </div>
                </div>
            );
        }
        return (
            <div className={styles.leftHeaderCell} key={key} style={{ ...style, width: width || 200 }}>
                <Column type={String(columnType)} title={columnChsName} width={width} columnAttrObj={columnAttrObj} />
            </div>
        );
    };
    // 渲染首列复选框
    renderCheckbox = (record: any, rowIndex: number) => {
        const { checked } = this.state;
        const { hasGroup } = this.props;
        const flag = checked.some((item: any) => {
            return item.id === record.id;
        });
        let checkbox = flag ? (
            <Checkbox checked={true} onClick={this.changeCheckbox.bind(this, record, false)} />
        ) : (
            <>
                <Checkbox
                    className={styles.unchecked}
                    checked={false}
                    onClick={this.changeCheckbox.bind(this, record, true)}
                />
                <div className={styles.no}>{record.index || rowIndex + 1}</div>
            </>
        );
        if (hasGroup) {
            if (
                this.indexCount[record.groupId][Math.floor((this.indexCount[record.groupId].length - 1) / 2)] !==
                record.id
            ) {
                // 填充透明的复选框使样式保持一致
                checkbox = <Checkbox style={{ opacity: 0 }} />;
            }
        }
        return checkbox;
    };

    //渲染数据
    renderBodyCell = (data: any[], { columnIndex, key, rowIndex, style }: any) => {
        const { columns, dataSource } = this.state;
        const {
            updateData,
            hasGroup,
            showForm = () => {},
            delData,
            getData,
            extraMenu,
            noAdd,
            noEdit,
            noDel,
            tableId,
            flush,
            showHistory = () => {},
        } = this.props;
        if (columns.length === 0 || data.length === 0) {
            return;
        }
        const filterRowData: any = data[rowIndex]; // 过滤掉隐藏项并追加行号的行数据，只在渲染逻辑中使用
        const rowData: any = dataSource[rowIndex]; // 原始行数据，真实使用的数据
        const columnConfig: any = columns[columnIndex]; // 列属性
        const columnData: any = rowData.rowData[columnIndex]; // 列数据
        const cellData: any = columnData.cellValueList; // 列数据
        let className = styles.indexCell;
        // 只读列不可编辑样式
        if (columnConfig.readOnlyFlag || rowData.isLocked || noEdit) {
            className += ` ${styles.disabled}`;
        }
        let extraIndexCellStyle = {};
        let extraCellContentStyle = {};
        if (hasGroup) {
            if (columnConfig.splitFlag) {
                // 正常列额外样式
                extraIndexCellStyle = {
                    borderTop:
                        this.indexCount[rowData.groupId][0] !== rowData.id ? '1px solid #F7F7F7' : '1px solid #e8e8e8',
                    borderBottom: rowIndex + 1 < data.length ? 0 : '1px solid #e8e8e8',
                };
            } else {
                // 不可拆分列额外样式
                extraIndexCellStyle = {
                    borderTop: this.indexCount[rowData.groupId][0] !== rowData.id ? 0 : '1px solid #e8e8e8',
                    borderBottom: rowIndex + 1 < data.length ? 0 : '1px solid #e8e8e8',
                };
                // 不可拆分列数据样式
                extraCellContentStyle = {
                    display:
                        this.indexCount[rowData.groupId][
                            Math.floor((this.indexCount[rowData.groupId].length - 1) / 2)
                        ] !== rowData.id
                            ? 'none'
                            : 'auto',
                };
            }
        }
        if (columnIndex === 0) {
            return (
                <div
                    key={key}
                    className={className}
                    style={{
                        ...style,
                        ...extraIndexCellStyle,
                    }}
                >
                    <div className={styles.cellNo}>
                        {this.renderCheckbox(filterRowData, rowIndex)}
                        <div className={styles.showFormBtnCls}>
                            <img
                                alt="edit"
                                className={styles.showFormBtn}
                                src={extendIcon}
                                onClick={showForm.bind(this, { rowId: rowData.id })}
                            />
                        </div>
                    </div>
                    <div className={styles.cellContent} style={extraCellContentStyle}>
                        <Cell
                            overlayClassName={cellData && cellData.length > 0 ? styles.firstCell : styles.empty}
                            columns={columns}
                            columnConfig={columnConfig}
                            rowData={rowData}
                            cellData={cellData}
                            updateData={updateData}
                            showForm={showForm}
                            delData={delData}
                            hasGroup={hasGroup}
                            getData={getData}
                            extraMenu={extraMenu}
                            noAdd={noAdd}
                            noEdit={noEdit}
                            noDel={noDel}
                            tableId={tableId}
                            flush={flush}
                        />
                        {(tableId === 1 || tableId === 14) && (
                            <div
                                className={styles.historyCount}
                                role="presentation"
                                onClick={showHistory.bind(this, {
                                    rowId: rowData.id,
                                    rowData: rowData.rowData,
                                    initType: 'detail',
                                })}
                            >
                                {filterRowData.groupHistoryCount && filterRowData.groupHistoryCount > 1 && (
                                    <span className={styles.countNo}>
                                        {`(${
                                            filterRowData.groupHistoryCount > 99
                                                ? '99+'
                                                : filterRowData.groupHistoryCount
                                        })`}
                                    </span>
                                )}
                            </div>
                        )}
                    </div>
                    {(tableId === 1 || tableId === 14) && (
                        <div
                            className={styles.addCls}
                            role="presentation"
                            onClick={showHistory.bind(this, {
                                rowId: rowData.id,
                                rowData: rowData.rowData,
                                initType: 'add',
                            })}
                        >
                            <IconFont className={styles.addIcon} type="iconxinzeng" />
                        </div>
                    )}
                </div>
            );
        }
        return (
            <div
                key={key}
                className={className}
                style={{
                    ...style,
                    ...extraIndexCellStyle,
                }}
            >
                <div className={styles.cellContent} style={extraCellContentStyle}>
                    <Cell
                        columns={columns}
                        columnConfig={columnConfig}
                        rowData={rowData}
                        cellData={cellData}
                        updateData={updateData}
                        showForm={showForm}
                        delData={delData}
                        hasGroup={hasGroup}
                        getData={getData}
                        extraMenu={extraMenu}
                        noAdd={noAdd}
                        noEdit={noEdit}
                        noDel={noDel}
                        tableId={tableId}
                        flush={flush}
                    />
                </div>
            </div>
        );
    };

    render() {
        const { columns, dataSource, checked, tableHeight, tableWidth } = this.state;
        const { changeChecked, operateConfig = {}, hasGroup } = this.props;
        const { groupConfig } = operateConfig;
        const { overscanColumnCount, overscanRowCount, columnWidth, rowHeight } = this.config;
        let totalHeight = tableHeight;
        let colWidth = columnWidth;
        const columnCount = columns.length;
        const rowCount = dataSource.length;
        if (rowCount > 0 && rowCount * rowHeight < tableHeight) {
            // 表格行数少于一屏高度时，高度增加横向滚动条高度
            totalHeight = rowCount * rowHeight + scrollbarSize();
        }
        if (columnCount > 0 && columnCount * columnWidth < tableWidth) {
            // 表格宽度不足一屏时，按屏幕宽度平分每列宽度
            colWidth = Math.floor(tableWidth / columnCount);
        }
        //有隐藏列时，修正数据与列头不匹配问题
        const data = this.memoizeData(columns, dataSource);
        //左侧固定列数量
        const leftCount = this.memoizeLeftCount(columns);

        return (
            <>
                <ScrollSync>
                    {({ onScroll, scrollLeft, scrollTop }: any) => {
                        return (
                            <div className={styles.GridRow}>
                                <div
                                    className={styles.LeftSideContainer}
                                    style={{
                                        width: `${colWidth * leftCount}px`,
                                        height: `${totalHeight - scrollbarSize() + rowHeight}px`,
                                    }}
                                >
                                    {
                                        //左侧表头
                                        <div
                                            className={styles.LeftSideGridContainer}
                                            style={{
                                                position: 'absolute',
                                                left: 0,
                                                top: 0,
                                                backgroundColor: `rgba(246, 246, 246, 1)`,
                                                borderLeft: '1px solid #e8e8e8',
                                            }}
                                        >
                                            <Grid
                                                className={styles.HeaderGrid}
                                                columnData={columns}
                                                columnWidth={colWidth}
                                                columnCount={leftCount}
                                                width={colWidth * leftCount}
                                                rowHeight={rowHeight}
                                                rowCount={1}
                                                height={rowHeight}
                                                checked={checked}
                                                changeChecked={changeChecked}
                                                groupConfig={groupConfig}
                                                dataSource={dataSource}
                                                cellRenderer={this.renderHeaderCell}
                                            />
                                        </div>
                                    }
                                    {
                                        //左侧固定列
                                        <div
                                            className={styles.LeftSideGridContainer}
                                            style={{
                                                position: 'absolute',
                                                left: 0,
                                                top: rowHeight,
                                                borderLeft: '1px solid #e8e8e8',
                                            }}
                                        >
                                            <Grid
                                                className={styles.LeftSideGrid}
                                                overscanRowCount={overscanRowCount}
                                                cellRenderer={this.renderBodyCell.bind(this, data)}
                                                columnWidth={colWidth}
                                                columnCount={leftCount}
                                                width={colWidth * leftCount}
                                                rowHeight={rowHeight}
                                                rowCount={rowCount}
                                                height={totalHeight - scrollbarSize()}
                                                scrollTop={scrollTop}
                                                checked={checked}
                                                dataSource={dataSource}
                                                columns={columns}
                                                hasGroup={hasGroup}
                                            />
                                        </div>
                                    }
                                </div>
                                <div className={styles.GridColumn}>
                                    <AutoSizer disableHeight>
                                        {({ width }: any) => {
                                            // 如果列太少不满一屏时，列头宽度按列计算
                                            let headerWidth = width;
                                            if (rowCount * rowHeight > tableHeight) {
                                                // 表格行数大于一屏的高度时出现竖向滚动条，此时宽度减出滚动条宽度
                                                headerWidth -= scrollbarSize();
                                            }
                                            return (
                                                <div>
                                                    {
                                                        //右侧表头
                                                        <div
                                                            style={{
                                                                backgroundColor: `rgba(246, 246, 246, 1)`,
                                                                height: rowHeight,
                                                                width: headerWidth,
                                                            }}
                                                        >
                                                            <Grid
                                                                className={styles.HeaderGrid}
                                                                overscanColumnCount={overscanColumnCount}
                                                                columnData={columns}
                                                                columnWidth={colWidth}
                                                                columnCount={columnCount}
                                                                width={headerWidth}
                                                                rowHeight={rowHeight}
                                                                rowCount={1}
                                                                height={rowHeight}
                                                                scrollLeft={scrollLeft}
                                                                cellRenderer={this.renderHeaderCell}
                                                                checked={checked}
                                                            />
                                                        </div>
                                                    }
                                                    {
                                                        //右侧内容
                                                        <div
                                                            style={{
                                                                height:totalHeight,
                                                                width,
                                                            }}
                                                        >
                                                            <Grid
                                                                className={styles.BodyGrid}
                                                                overscanColumnCount={overscanColumnCount}
                                                                overscanRowCount={overscanRowCount}
                                                                dataSource={dataSource}
                                                                columnWidth={colWidth}
                                                                columnCount={columnCount}
                                                                width={width}
                                                                rowHeight={rowHeight}
                                                                rowCount={rowCount}
                                                                height={totalHeight}
                                                                onScroll={(...arg: Array<Object>) => {
                                                                    console.log('arg', arg);
                                                                    onScroll(...arg);
                                                                    this.onScrollRow(arg[0]);
                                                                }}
                                                                cellRenderer={this.renderBodyCell.bind(this, data)}
                                                                columns={columns}
                                                                checked={checked}
                                                                hasGroup={hasGroup}
                                                            />
                                                        </div>
                                                    }
                                                </div>
                                            );
                                        }}
                                    </AutoSizer>
                                </div>
                            </div>
                        );
                    }}
                </ScrollSync>
            </>
        );
    }
}
