import React, { Component } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import Table from './Table';
import TableSort from './TableSort';
import Page from '../../page/lib/Page';
import StatusBox from '../../statusbox/lib/Statusbox';
// @require '../style/auto_table.scss'

// 加载中显示
const Loading = <StatusBox type="loading" size="l">加载中</StatusBox>;

export default class AutoTable extends Component {

  static propTypes = {
    style: PropTypes.object,
    className: PropTypes.string,
    /**
     * 透传到Table的属性
     */
    tableProps: PropTypes.shape(Table.propTypes),
    /**
     * 一页里最大显示行数，如果要显示的行数超过max就在底部显示出分页组件
     */
    maxRow: PropTypes.number,
    /**
     * 是否缓存数据。
     * 如果开启缓存已经加载过的数据就不会再触发onNeedMoreData去询问更多数据
     * 如果没有开启就会在每次切换页面时触发onNeedMoreData去询问更多数据
     * 默认开启
     */
    cache: PropTypes.bool,
    /**
     * 配置每列表头
     */
    cols: PropTypes.arrayOf(PropTypes.shape({
      /**
       * 表格头部一列名称
       */
      display: PropTypes.any,
      /**
       * 表格一列所占宽度百分比
       */
      width: PropTypes.number,
      /**
       * 跨列
       * 默认为1
       */
      colspan: PropTypes.number,
      /**
       * 该表头是否需要排序，如果需要排序
       * 1.该列只有一个排序选项：请传入一个只有一个元素的[SortType]类型的Arrays
       * 2.该列有多个排序选项:请传入一个[SortType]类型的Arrays
       */
      sort: PropTypes.arrayOf(PropTypes.shape({
        /**
         * 显示在表头的排序按钮
         */
        display: PropTypes.any.isRequired,
        /**
         * 用于表示该排序方法的唯一key
         */
        key: PropTypes.string.isRequired,
        /**
         * 默认排序方法
         * 1=顺序
         * -1=逆序
         * 如果==null表示当前排序方法默认不启用
         */
        value: PropTypes.oneOf([-1, 1]),
      })),
    })).isRequired,
    /**
     * fetch负责通过当前的查询参数去请求CGI获取数据
     * fetch函数必须返回一个会resolve {rows:Array ,total:number}的Promise
     * resolve rows的数组代表在当前查询条件下查询到的所有行
     * resolve total 属性代表一共有多少行数据
     * 如果请求数据发生错误请 reject(错误原因)
     * 如果resolveData告诉了total才去更新state里的total，否则沿用以前的
     * fetch({
     *    sort:{
     *        表示该排序方法的唯一key:顺序还是逆序,
     *        表示该排序方法的唯一key:顺序还是逆序,
     *    },
     *    page:1,
     *    count:5
     * }) => Promise(resolve({rows:Array ,total:number}),reject(errMsg))
     */
    fetch: PropTypes.func.isRequired,
    /**
     * 给你代表表格一行的原数据，你给我代表这行的jsx用于展示
     * 最后返回的是一个数组
     * translateRow(rowData,index) => [jsx]
     */
    translateRow: PropTypes.func.isRequired,
    /**
     * 持久化 AutoTable的state
     */
    persistence: PropTypes.shape({
      get: PropTypes.func,
      set: PropTypes.func,
    }),
    /**
     * 监听页数变化
     * (newPage: number, prevPage: number) => void
     */
    onPageChange: PropTypes.func,
  }

  static defaultProps = {
    maxRow: 5,
    cache: true,
    persistence: {},
    onPageChange: () => {},
  }

  state = {
    /**
     * 这些数据的总行数
     */
    total: 0,
    /**
     * 当前选中页
     */
    page: 0,
    /**
     * 当前的排序方法
     */
    sort: {},
    /**
     * 拉取到的所有数据
     */
    data: [],
    /**
     * 当前要展示在table里的rows
     */
    visibleData: [],
    /**
     * 当前悬浮展示在表格的正中间
     */
    overlap: Loading,
  }

  constructor(props) {
    super(props);
    const { persistence } = props;
    if (typeof persistence.get === 'function') {
      let persistenceState = persistence.get();
      if (typeof persistenceState === 'object' && persistenceState !== null) {
        this.state = persistenceState;
      }
    }
    // props.cols根据求出state.sort
    props.cols.forEach(col => {
      let sort = col.sort;
      if (Array.isArray(sort)) {
        sort.forEach(one => {
          if (typeof one.value === 'number') {
            this.state.sort[one.key] = one.value;
          }
        });
      }
    });
  }

  componentDidMount() {
    this.doFetch();
  }

  persistence = () => {
    const { persistence } = this.props;
    if (typeof persistence.set === 'function') {
      persistence.set(this.state);
    }
  }

  handleNeedMoreData = (page) => {
    this.setState({
      page,
      overlap: Loading,
    }, () => {
      this.doFetch();
      this.persistence();
    });
  }

  handleSortChange = (sortParam) => {
    const { sort } = this.state;
    sort[sortParam.sort] = sortParam.order;
    this.reset();
  }

  /**
   * 重置table，会
   * 1.清空数据
   * 2.跑到第一页
   * 3.去拉取第一页的数据
   * @param visibleData 用来设置当前的可见数据
   */
  reset = (visibleData) => {
    this.setState({
      page: 0,
      data: [],
      visibleData: Array.isArray(visibleData) ? visibleData : this.state.visibleData,
      overlap: Loading,
    }, () => {
      this.doFetch();
      this.persistence();
    });
  }

  /**
   * 重新拉取当前 pageNum 的数据
   */
  refreshCurrentPage = () => {
    const {
      page,
    } = this.state;
    this.goPage(page);
  }

  /**
   * 拉取数据
   */
  doFetch() {
    const { fetch, maxRow, translateRow } = this.props;
    const { sort, page, data } = this.state;
    fetch({
      sort,
      page,
      count: maxRow,
    }).then(resolveData => {
      const { rows, total } = resolveData;
      (Array.isArray(rows) ? rows : []).forEach((oneRow, index) => {
        // 把CGI返回的数据转换为需要显示的样子
        data[page * maxRow + index] = translateRow(oneRow, index);
      });
      const visibleDataStartIndex = page * maxRow;
      let visibleDataEndIndex = visibleDataStartIndex + maxRow;
      if (typeof total === 'number' && total < visibleDataEndIndex) {
        visibleDataEndIndex = total;
      }
      const updateState = {
        data,
        overlap: null,
        // 当前需要展示的rows，并过滤掉无效数据
        visibleData: data.slice(
          visibleDataStartIndex,
          visibleDataEndIndex,
        ).filter(item => !!item),
      };
      // 如果resolveData告诉了total才去更新state里的total，否则沿用以前的
      if (typeof total === 'number') {
        updateState.total = total;
      }
      this.setState(updateState, () => {
        this.persistence();
      });
    }).catch(err => {
      this.setState({
        overlap: <StatusBox type="alert" size="l">{err}</StatusBox>,
      }, () => {
        this.persistence();
      });
    });
  }

  /**
   * 跳转到第page页，
   * 如果没有第page页的数据会回调fetch去加载更多的数据
   * @param page 跳转到第page页，从0开始计数
   */
  goPage = (page) => {
    const { maxRow, cache, onPageChange } = this.props;
    const { data } = this.state;
    let startIndex = page * maxRow;
    // page - 即将跳转的页面
    // this.state.page - 跳转前的页面
    onPageChange(page, this.state.page);
    // 1. 或者中间数据为空时
    // 2. 没有足够的数据去显示时
    if (!Array.isArray(data[startIndex]) || startIndex >= data.length) {
      // 通知去加载更多的数据,显示正在加载中
      this.handleNeedMoreData(page);
    } else {
      // 有数据可以显示
      // 如果关闭缓存就去通知onNeedMoreData
      if (cache === false) {
        this.handleNeedMoreData(page);
      } else {
        let visibleDataStartIndex = page * maxRow;
        this.setState({
          page,
          visibleData: data.slice(visibleDataStartIndex, visibleDataStartIndex + maxRow),
        }, () => {
          this.persistence();
        });
      }
    }
  };

  /**
   * 显示第page页
   * 如果没有第page页的数据 不会 回调fetch去加载更多的数据
   * 业务侧自己保证第page页有数据
   * @param page 跳转到第page页，从0开始计数
   */
  showPage = (page) => {
    const { maxRow } = this.props;
    const { data } = this.state;
    let visibleDataStartIndex = page * maxRow;
    this.setState({
      page,
      visibleData: data.slice(visibleDataStartIndex, visibleDataStartIndex + maxRow),
    }, () => {
      this.persistence();
    });
  }

  /**
   * 渲染出分页表头
   */
  renderCols = () => {
    const { cols } = this.props;
    const { sort } = this.state;
    cols.forEach(col => {
      let colSort = col.sort;
      // 解析渲染出排序
      if (Array.isArray(colSort)) {
        let tableSortActive = false;
        let tableSortKey = null;
        colSort.forEach(one => {
          if (typeof sort[one.key] === 'number') {
            tableSortActive = true;
            tableSortKey = one.key;
          }
        });
        col.display = (
          <TableSort
            options={colSort}
            active={tableSortActive}
            tableSortKey={tableSortKey}
            defaultOrder={sort[tableSortKey]}
            onChange={this.handleSortChange}
          />
        );
      }
    });
    return cols;
  }

  /**
   * 渲染出分页
   */
  renderPager = () => {
    const { maxRow } = this.props;
    const { total, page } = this.state;
    // 是否显示分页
    let showPager = total > maxRow;
    if (showPager) {
      // 当rows数量大于maxRow时采用分页时的总页数
      let totalPage = Math.ceil(total / maxRow);
      return (
        <Page
          shouldDidMountOnChange={false}
          size="s"
          totalPage={totalPage}
          page={page}
          onChange={(pageNum) => {
            this.goPage(pageNum, true);
          }}
        />
      );
    }
    return null;
  }

  renderTable = () => {
    const { visibleData, overlap } = this.state;
    return (
      <Table
        {...this.props.tableProps}
        cols={this.renderCols()}
        data={visibleData}
        overlap={overlap}
      />
    );
  }

  render() {
    const { className, style } = this.props;
    return (
      <div className={classnames('im-auto-table', className)} style={style}>
        {this.renderTable()}
        {this.renderPager()}
      </div>
    );
  }
}
