import * as React from 'react';
import { BasicContainer, runTimeType } from '../../render/core/Container/types';
import { Table } from '@native-ads/antd';
import { Map } from 'immutable';
import componentLoader from '../../render/util/componentLoader';
import * as _ from 'lodash';
import { createChild } from '../../render/util/createChild';
import { compileExpressionString } from '../../render/util/vm';
import { TableProps } from '@native-ads/antd/lib/table';
import { PaginationProps } from '@native-ads/antd/lib/pagination';
import {
    DataSource,
    TablePropsInterface,
    TableStateInterface,
    TableColumnsItem,
    TableConfig,
    TableCustomerColumnControlItem
} from './types';

export class TableDriver extends Table<DataSource> { }

export class AbstractTable extends BasicContainer<TablePropsInterface, TableStateInterface> {
    constructor(props: TablePropsInterface) {
        super(props);

        this.state = {
            total: 0,
            current: 0,
            expandedRowKeys: []
        };

        this.handleTableChange = this.handleTableChange.bind(this);
        this.addExpand = this.addExpand.bind(this);
    }

    componentWillMount() {
        let info = this.getPropsInfo(this.props.info);

        if (info.expandedRowKeys) {
            this.setState({
                expandedRowKeys: info.expandedRowKeys
            });
        }
    }

    // TODO 使用高阶组件来整理组件库
    shouldComponentUpdate() {
        return true;
    }

    private handleTableChange(pagination: PaginationProps | boolean, filters: string[], sorter: Object) {
        if (typeof pagination !== 'boolean') {
            this.commonEventHandler('onPagination', {
                pageSize: pagination.pageSize,
                current: pagination.current
            });
        }
    }

    private addExpand(expanded: boolean, record: DataSource) {
        let expandeds = this.state.expandedRowKeys;
        let rowKey = this.props.info.rowKey || 'key';

        if (expanded) {
            expandeds.push(record[rowKey]);
        } else {
            expandeds = expandeds.filter((expandedsItem: any) => {
                return expandedsItem !== record[rowKey];
            });
        }

        this.setState({
            expandedRowKeys: expandeds
        });
    }

    private mapTableOptions(info: TableConfig): TableProps<{}> {
        return {
            pagination: info.pagination,
            size: info.size,
            loading: info.loading,
            locale: info.locale,
            indentSize: info.indentSize,
            useFixedHeader: info.useFixedHeader,
            bordered: info.bordered,
            showHeader: info.showHeader,
            className: info.className,
            style: info.style
        };
    }

    private rewriteColumn(columnsMapping: TableColumnsItem, column: TableColumnsItem, runTime: runTimeType) {
        let newColumn = compileExpressionString(columnsMapping, {
            ...runTime,
            $item: column
        })!;

        return Object.assign(column, newColumn);
    }

    private columnProcessor(
        columns: TableColumnsItem[],
        info: TableConfig,
        dataSource: DataSource[]
    ): TableColumnsItem[] {
        if (info.extendColumns instanceof Array) {
            columns = columns.concat(info.extendColumns);
        }

        return columns.map(column => {
            // 列映射
            if (info.columnsMapping) {
                let runTime = this.getRuntimeContext();
                this.rewriteColumn(info.columnsMapping, column, runTime);
            }

            if (info.mergeFirstColumn) {
                this.collectColSpanInfo(dataSource);
                this.mergeFColumnControls(columns, dataSource);
            }

            let customConfig = info.customerColumnControls;
            let dataIndex = column.dataIndex;
            let targetCustomConfig = _.find(customConfig, columnConfig => columnConfig.dataIndex === dataIndex);

            if (targetCustomConfig) {
                Object.assign(column, targetCustomConfig);

                column.render = (text: any, record: DataSource, index: number) => {
                    let childElement = this.renderTableCell(
                        targetCustomConfig!,
                        text,
                        record,
                        index
                    );

                    return (
                        <span className={column.className} style={column.style} key={index}>
                            {childElement}
                        </span>
                    );
                };
            }

            return column;
        });
    }

    private dataSourceProcessor(
        dataSource: DataSource[],
        info: TableConfig
    ) {
        if (info.dataSourceMapping) {
            let runTime = this.getRuntimeContext();
            dataSource = dataSource.map(source => {
                let newDataSource = compileExpressionString(info.dataSourceMapping, {
                    ...runTime,
                    $item: source
                });

                return Object.assign(source, newDataSource);
            });
        }

        return dataSource;
    }

    /**
     * 获取表格单元格自定义渲染元素
     *
     * @param info Table的配置
     * @param column 每列的配置
     * @param text 单元格要渲染的文本
     * @param record 整行的数据对象
     * @param index 行数
     * @returns {React.ReactNode}
     */
    private renderTableCell(
        column: TableCustomerColumnControlItem,
        text: any,
        record: DataSource,
        index: number
    ): React.ReactNode {
        if (column.controls) {
            return column.controls.map((controlInfo, controlIndex) => {
                let defaultProps = this.getChildProps(controlInfo, {
                    key: `table_column_${controlInfo.type}_${controlIndex}}`
                });
                return createChild(controlInfo, {
                    ...defaultProps,
                    $item: record,
                    $index: index
                });
            });
        }

        return text;
    }

    private collectColSpanInfo(dataSource: DataSource[]) {
        dataSource.forEach(source => {
            let sourceKeys = Object.keys(source);
            let sourceInfo = {};

            sourceKeys.forEach(key => {
                sourceInfo[key] = 1;
            });

            source.colSpanInfo = Map(sourceInfo);
        });
    }

    private mergeFColumnControls(columns: TableColumnsItem[], dataSource: DataSource) {
        let item = columns[0];
        item.render = (source, row, index) => {
            const obj = {
                children: source,
                props: {
                    rowSpan: item.dataIndex ? row.colSpanInfo.get(item.dataIndex) : 1
                }
            };

            if (this.props.info.mergeFirstColumn && item.dataIndex && row.colSpanInfo.get(item.dataIndex) > 0) {
                let start = index + 1;
                let count = 1;
                let remain = dataSource.length - index;
                let pointer = start;
                for (let i = 1; (pointer = start) &&
                    i < remain &&
                    source === dataSource[pointer][item.dataIndex]; i++) {
                    dataSource[pointer].colSpanInfo = dataSource[pointer].colSpanInfo.set(item.dataIndex, 0);
                    start++;
                    count++;
                }
                obj.props.rowSpan = count;
            }
            return obj;
        };
        return item;
    }

    render() {
        let info = this.getPropsInfo(this.props.info, this.props,
            ['columnsMapping', 'dataSourceMapping', 'customerColumnControls', 'mergeFirstColumn'], true);
        let dataSource: DataSource[] = info.dataSource || [];
        let columns: TableColumnsItem[] = info.columns || [];
        let $loading = info.loading;
        let tableProps = this.mapTableOptions(info);
        const tableStyle = {
            width: '100%',
            ...info.style
        };

        if (this.isUnderContainerEnv()) {
            $loading = this.getValueFromDataStore('$loading') || false;
        }

        if (info.expandedRowKeys && Array.isArray(info.expandedRowKeys)) {
            tableProps.expandedRowKeys = this.state.expandedRowKeys;
            tableProps.onExpand = this.addExpand;
        }

        if (info.fixedHeader) {
            tableProps.scroll = {
                y: info.fixedHeader.fixedHeight || '',
                x: info.fixedHeader.fixedWidth || ''
            };
        }

        if (info.rowSelection && _.isPlainObject(info.rowSelection)) {
            tableProps.rowSelection = {
                fixed: info.rowSelection.fixed,
                type: info.rowSelection.type,
                hideDefaultSelections: info.rowSelection.hideDefaultSelections,
                onChange: (selectedRowKeys: string[] | number[], selectedRows: Object[]) => {
                    this.commonEventHandler('onRowSelectionChange', {
                        selectedRowKeys: selectedRowKeys,
                        selectedRows: selectedRows
                    });
                },
                onSelect: (record: DataSource, selected: boolean, selectedRows: Object[]) => {
                    this.commonEventHandler('onRowSelectionSelect', {
                        record: record,
                        selected: selected,
                        selectedRows: selectedRows
                    });
                }
            };
        }

        columns = this.columnProcessor(columns, info, dataSource);
        dataSource = this.dataSourceProcessor(dataSource, info);

        return (
            <TableDriver
                {...tableProps}
                rowKey={(record) => {
                    if (info.rowKey) {
                        return record[info.rowKey];
                    } else if (record['key']) {
                        return record['key'];
                    } else {
                        console.error('请提供rowKey属性来让表格组件工作在正常的状态下');
                    }
                }}
                columns={columns}
                dataSource={dataSource}
                loading={$loading}
                style={tableStyle}
                onChange={this.handleTableChange}
            />
        );
    }
}

componentLoader.addComponent('table', AbstractTable, TablePropsInterface);
