import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import Button from '../../button/lib/Button';
import Icon from '../../icon/index';
import Input from '../../input/index';
// @require '../style/index.scss'

const fixPageNum = (page, totalPage) => {
  return Math.max(1, Math.min(page, totalPage));
};

/**
 * 翻页组件
 * 如果页数太大，会有一部分被隐藏，但是第一页和对后一个一直都会显示。中间会显示当前选中页和当前选中页附近的页
 * 从0开始计数
 */
class Page extends React.Component {
  static propTypes = {
    style: PropTypes.object,
    className: PropTypes.string,
    /**
     * @transformable
     * 总页数
     */
    totalPage: PropTypes.number.isRequired,
    /**
     * @transformable
     * 当前选中页从0开始计数
     */
    page: PropTypes.number,
    /**
     * 最大显示的分页按钮的数量,多余这个数量的按钮将被隐藏
     * 在有很多页时会显示所有页，所以需要设置一个最大显示多少页。
     * 不包含 上一页 和 下一页 按钮
     */
    maxDisplayNumber: PropTypes.number,
    /**
     * 当用户翻页时调用 callback(当前点击页,在点击前选中当页)
     * 注意：从0开始计数
     */
    onChange: PropTypes.func,
    /**
     * 在page组件 componentDidMount 时 是否需要回调 onChange
     */
    shouldDidMountOnChange: PropTypes.bool,
    /**
     * 组件大小
     */
    size: PropTypes.oneOf(['m', 's']),
    /**
     * 是否展示可输入指定第x页的分页器，默认值 false
     */
    inputable: PropTypes.bool,
    /**
     * 用于自定义页码的结构，可用于优化SEO
     */
    renderBtn: PropTypes.func,
  };

  static defaultProps = {
    shouldDidMountOnChange: true,
    maxDisplayNumber: 6,
    page: 0,
    size: 'm',
    inputable: false,
    renderBtn: (page) => page,
  };

  constructor(props) {
    super(props);

    this.state = {
      // 当前选中页 从0开始计数
      currentPage: this.props.page || 0,
      // 当前编辑页数
      currentEditPage: this.props.page + 1 || 1,
      // 总页数
      totalPage: this.props.totalPage || 0,
      // 要显示在中间的按钮列表
      displayBtnList: [],
    };

    const { inputable } = this.props;
    const { currentPage, totalPage } = this.state;
    const params = [currentPage, totalPage];
    const btnData = inputable ? this.getPageInputSettedData(...params) : this.getPageBtnData(...params);

    this.state = {
      ...this.state,
      ...btnData,
    };
  }

  componentWillReceiveProps(nextProps) {
    const { page, totalPage } = nextProps;
    this.goPageRender(page || 0, totalPage);
  }

  componentDidMount() {
    const { shouldDidMountOnChange, onChange } = this.props;
    const { currentPage } = this.state;

    if (shouldDidMountOnChange && typeof onChange === 'function') {
      onChange(currentPage, currentPage);
    }
  }

  /**
   * 切页数的数据保存的方式，如果是直出渲染，一开始不使用setState保存数据，而是保存到一个地方
   * @param pageNum 从0开始计数
   * @param totalPage
   */
  prevOnClick = () => {
    this.goPage(this.state.currentPage - 1);
  };

  nextOnClick = () => {
    this.goPage(this.state.currentPage + 1);
  };
  /**
   * 切换到pageNum页
   * @param pageNum 从0开始计数
   * @param totalPage
   */

  goPageRender = (pageNum, totalPage) => {
    const { inputable } = this.props;
    if (!inputable) {
      return this.goPageBtnRender(pageNum, totalPage);
    }
    // 其实我没搞懂为什么要在goPageBtnRender里同时处理数据和渲染

    return this.goPageInputSetted(pageNum, totalPage);
  };
  /**
   * 切换到pageNum页（带输入页数功能）
   * @param pageNum 从0开始计数
   * @param totalPage
   */

  goPageInputSetted = (pageNum, totalPage) => {
    this.setState(this.getPageInputSettedData(pageNum, totalPage));
  };

  getPageInputSettedData = (pageNum, totalPage) => {
    return {
      currentPage: pageNum,
      currentEditPage: pageNum + 1,
      totalPage,
    };
  };

  getPageBtnData = (pageNum, totalPage) => {
    const self = this;
    let { size, maxDisplayNumber, renderBtn } = this.props;
    const btnRenderIsFun = typeof renderBtn === 'function';
    const displayBtnList = [];

    // 往要显示在中间的按钮列表添加一个元素
    function pushBtn(btnIndex) {
      const page = btnIndex + 1;
      displayBtnList.push(
        <Button
          key={btnIndex}
          size={size}
          color={btnIndex === pageNum ? 'default' : 'weak'}
          onClick={() => self.goPage(btnIndex)}
        >
          {btnRenderIsFun ? renderBtn(page) : page}
        </Button>
      );
    }

    // 去掉第一个和最后一个
    maxDisplayNumber -= 2;

    // page为1的按钮一直都要显示
    if (pageNum !== 0) {
      pushBtn(0);
    }
    const step = Math.floor(maxDisplayNumber / 2);
    let startIndex = pageNum - step;
    if (startIndex < 1) {
      startIndex = 1;
    }
    let endIndex = pageNum + (maxDisplayNumber - step);
    if (endIndex > totalPage - 2) {
      endIndex = totalPage - 2;
    }
    // 中间是否被隐藏了翻页按钮 隐藏的部分显示为...
    if (startIndex > 1) {
      displayBtnList.push(
        <span key="<" className="ellipses">
          ...
        </span>
      );
    }
    for (let i = startIndex; i < pageNum; i++) {
      pushBtn(i);
    }
    for (let i = pageNum; i <= endIndex; i++) {
      pushBtn(i);
    }
    // 中间是否被隐藏了翻页按钮 隐藏的部分显示为...
    if (endIndex < totalPage - 2) {
      displayBtnList.push(
        <span key=">" className="ellipses">
          ...
        </span>
      );
    }

    // 最后一页的按钮必须显示
    pushBtn(totalPage - 1);

    return {
      currentPage: pageNum,
      totalPage,
      displayBtnList,
    };
  };

  /**
   * 切换到pageNum页（带按钮选择功能）
   * @param pageNum 从0开始计数
   * @param totalPage
   */

  goPageBtnRender = (pageNum, totalPage) => {
    this.setState(this.getPageBtnData(pageNum, totalPage));
  };
  /**
   * 跳转到第pageNum页
   * @param pageNum 从0开始计数
   */

  goPage = (pageNum) => {
    const { onChange } = this.props;
    const { totalPage, currentPage } = this.state;

    // 点击当前页数不执行
    if (currentPage === pageNum) {
      return;
    }
    this.goPageRender(pageNum, totalPage);
    if (typeof onChange === 'function') {
      onChange(pageNum, currentPage);
    }
  };

  renderInputer = () => {
    const { currentEditPage, totalPage } = this.state;
    const { size } = this.props;
    return (
      <div className={`im-page-inputer inputer-${size}`}>
        <Input
          size={size}
          style={{
            /** 根据输入的值动态调整输入框的大小 */
            width: `${`${currentEditPage}`.length * 9.4 + 4}px`,
          }}
          value={currentEditPage}
          onChange={(e) => {
            const { target: { value } = {} } = e;
            this.setState({
              currentEditPage: value,
            });
          }}
          type="number"
          onBlur={(e) => {
            const { target: { value } = {} } = e;
            const page = fixPageNum(value, totalPage);
            this.goPage(page - 1);
          }}
        />
        <span className="im-page-inputer-postfix">/{totalPage}</span>
      </div>
    );
  };

  render() {
    const { style, className, size, inputable } = this.props;
    const { currentPage, displayBtnList, totalPage } = this.state;
    const lastPage = totalPage - 1;

    const operate = !inputable ? displayBtnList : this.renderInputer();

    return (
      <div className={classNames('im-page', className)} style={style}>
        <Button
          color="weak"
          className="im-page-arrow"
          size={size}
          disabled={currentPage <= 0}
          onClick={currentPage <= 0 ? null : this.prevOnClick}
        >
          <Icon type="v-left" />
        </Button>
        {operate}
        <Button
          color="weak"
          className="im-page-arrow"
          size={size}
          disabled={currentPage >= lastPage}
          onClick={currentPage >= lastPage ? null : this.nextOnClick}
        >
          <Icon type="v-right" />
        </Button>
      </div>
    );
  }
}

export default Page;
