import React, { PureComponent, Children, cloneElement, isValidElement } from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
import Pagination from './../Pagination';
import TableBody from './src/TableBody';
import TableFooter from './src/TableFooter';
import TableHeader from './src/TableHeader';
import TableRow from './src/TableRow';
import TableRowColumn from './src/TableRowColumn';
import Autocomplete from '../Autocomplete';
import './src/table.scss';


class Table extends PureComponent {
  static propTypes = {
    /**
     * Children passed to table.
     */
    children: PropTypes.node,
    /**
     * If true, the footer will appear fixed below the table.
     */
    fixedFooter: PropTypes.bool,
    /**
     * If true, the header will appear fixed above the table.
     */
    fixedHeader: PropTypes.bool,
    /**
     * If true, multiple table rows can be selected.
     * CTRL/CMD+Click and SHIFT+Click are valid actions.
     */
    multiSelectable: PropTypes.bool,
    /**
     * Controls the display of the row checkbox.
     */
    displayRowCheckbox: PropTypes.bool,
    /**
     * @ignore
     * Called when a row is selected. selectedRows is an
     * array of all row selections. IF all rows have been selected,
     * the string "all" will be returned instead to indicate that
     * all rows have been selected.
     */
    onChange: PropTypes.func,
    /**
     * If true, table rows can be selected.
     * If multiple row selection is desired, enable multiSelectable.
     */
    selectable: PropTypes.bool,
    className: PropTypes.string,
    style: PropTypes.object,
    filterOptions: PropTypes.object,
    filtering: PropTypes.string,
    paginate: PropTypes.number
  };

  static defaultProps = {
    children: null,
    fixedFooter: false,
    fixedHeader: false,
    multiSelectable: false,
    displayRowCheckbox: false,
    onChange: null,
    selectable: false,
    className: null,
    style: null,
    filterOptions: null,
    filtering: null,
    paginate: null
  };

  state = {
    sort: null,
    order: null,
    pageSelected: 0,
    pageCount: 1
  };

  componentWillReceiveProps() {
    this.reCalculateBodySelection = true;
  }

  onChangeOrder = ({ sort, order }) => {
    this.reCalculateBodySelection = false;
    this.setState({ sort, order });
  };

  setPageCount = pageCount => {
    this.setState({ pageCount });
  }

  reCalculateBodySelection = true;

  createTableHeader(base) {
    const { fixedHeader, selectable, displayRowCheckbox } = this.props;
    if (base === undefined) {
      return {};
    }
    const nodeHeader = cloneElement(
      base,
      {
        adjustForCheckbox: selectable && displayRowCheckbox,
        onChangeOrder: this.onChangeOrder,
        sortedField: this.state.sort,
        order: this.state.order,
        filtering: this.props.filtering
      }
    );

    if (fixedHeader) {
      return {
        tableHeader: (
          <table styleName="table table--header">
            {nodeHeader}
          </table>
        )
      };
    }

    return { inlineHeader: nodeHeader };
  }

  createTableBody(base) {
    const { displayRowCheckbox, selectable, multiSelectable, filterOptions, filtering, paginate } = this.props;
    const { sort, order, pageSelected, pageCount } = this.state;
    this.bodyChildren = base && base.props.children;
    let filterOpts;
    if (filterOptions) {
      filterOpts = filterOptions;
    } else {
      filterOpts = this.state.filteredValues ? { filter: filtering, filterValues: this.state.filteredValues } : null;
    }

    return {
      tableBody: cloneElement(
        base,
        {
          onChange: this.props.onChange,
          multiSelectable,
          onCellClick: this.onCellClick,
          displayRowCheckbox,
          reCalculateSelection: this.reCalculateBodySelection,
          selectable: selectable || displayRowCheckbox || multiSelectable,
          sort,
          order,
          paginate,
          filterOptions: filterOpts,
          pageSelected,
          pageCount,
          setPageCount: this.setPageCount,
          handlePageChange: this.handlePageChange
        }
      )
    };
  }

  createTableFooter(base) {
    const { fixedFooter, displayRowCheckbox, selectable } = this.props;
    if (base === undefined) {
      return {};
    }
    const nodeFooter = cloneElement(base, { adjustForCheckbox: selectable && displayRowCheckbox });
    if (fixedFooter) {
      return {
        tableFooter: (
          <table styleName="table table--footer">
            {nodeFooter}
          </table>
        )
      };
    }
    return { inlineFooter: nodeFooter };
  }

  handleChange = values => {
    this.setState({ filteredValues: values });
  }

  handlePageChange = item => {
    this.setState({ pageSelected: item.selected });
  }

  bodyChildren = null;


  render() {
    const { children, className, style, filtering } = this.props;
    const {
      tableHeader, inlineHeader, tableBody, inlineFooter, tableFooter
    } = Children
        .toArray(children)
        .reduce((resultObject, child) => {
          if (!isValidElement(child)) {
            return resultObject;
          }
          const { type: { componentName } } = child;
          switch (componentName) {
            case 'TableHeader':
              return { ...this.createTableHeader(child), ...resultObject };
            case 'TableBody':
              return { ...this.createTableBody(child), ...resultObject };
            case 'TableFooter':
              return { ...this.createTableFooter(child), ...resultObject };
            default:
              warning(false,
                `Flive-ui: Children of the Table component must be TableBody or TableHeader or TableFooter.
              Nothing is rendered.`
              );
              return resultObject;
          }
        }, {}) || {};

    // If we could not find a table-header and a table-body, do not attempt to display anything.
    if (!(tableHeader || inlineHeader || tableBody) && !(inlineFooter || tableFooter || tableBody)) return null;
    return (
      <div styleName="table-wrapper">
        {filtering && <div styleName="filter-wrapper">
          Search
          <Autocomplete
            placeholder={`Enter ${filtering} or part of it`}
            noMatchLabel="No value matched"
            indexes={this.bodyChildren &&
              Children.toArray(this.bodyChildren).map(item => item.props.value[filtering]
                || '')}
            onChange={this.handleChange}
            styleName="filter"
            hideDropdown
            ignoreCase
          />
        </div>}
        {(!tableHeader && !tableFooter) ? (
          <table
            className={className}
            style={style}
            styleName="table"
          >
            {inlineHeader}
            {inlineFooter}
            {tableBody}
          </table>
        ) :
          (
            <div
              style={style}
              className={className}
              styleName="wrapper-fixed"
            >
              {tableHeader}
              <div styleName="wrapper-scroll">
                <table styleName="table table--scrolled">
                  {inlineHeader}
                  {tableBody}
                  {inlineFooter}
                </table>
              </div>
              {tableFooter}
            </div>
          )}
        {this.props.paginate && this.state.pageCount > 1 &&
          <Pagination
            pageCount={this.state.pageCount}
            pageRangeDisplayed={10}
            marginPagesDisplayed={1}
            forcePage={this.state.pageSelected}
            onPageChange={item => this.handlePageChange(item)}
          />}
      </div>
    );
  }
}


Table.Body = TableBody;
Table.Footer = TableFooter;
Table.Header = TableHeader;
Table.Row = TableRow;
Table.RowColumn = TableRowColumn;

export default Table;
