import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import List from './list';
import Operation from './operation';
import Search from './search';
import warning from '../_util/warning';
import LocaleReceive from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale-provider/default';

import './style/index';

function noop() {
}

export default class Transfer extends React.Component {
  // For high-level customized Transfer @dqaria
  static List = List;
  static Operation = Operation;
  static Search = Search;

  static defaultProps = {
    dataSource: [],
    render: noop,
    locale: {},
    showSearch: false,
  };

  static propTypes = {
    prefixCls: PropTypes.string,
    disabled: PropTypes.bool,
    dataSource: PropTypes.array,
    render: PropTypes.func,
    targetKeys: PropTypes.array,
    onChange: PropTypes.func,
    height: PropTypes.number,
    style: PropTypes.object,
    listStyle: PropTypes.object,
    operationStyle: PropTypes.object,
    className: PropTypes.string,
    titles: PropTypes.array,
    operations: PropTypes.array,
    showSearch: PropTypes.bool,
    filterOption: PropTypes.func,
    searchPlaceholder: PropTypes.string,
    notFoundContent: PropTypes.node,
    locale: PropTypes.object,
    body: PropTypes.func,
    footer: PropTypes.func,
    rowKey: PropTypes.func,
    lazy: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
  };

  constructor(props) {
    super(props);

    warning(
      !('notFoundContent' in props || 'searchPlaceholder' in props),
      'Transfer[notFoundContent] and Transfer[searchPlaceholder] will be removed, ' +
      'please use Transfer[locale] instead.',
    );

    const { selectedKeys = [], targetKeys = [] } = props;
    this.state = {
      leftFilter: '',
      rightFilter: '',
      sourceSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) === -1),
      targetSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) > -1),
      flag: false // 触发上下移渲染
    };
  }

  componentWillReceiveProps(nextProps) {
    const { sourceSelectedKeys, targetSelectedKeys } = this.state;

    if (nextProps.targetKeys !== this.props.targetKeys ||
      nextProps.dataSource !== this.props.dataSource) {
      // clear cached separated dataSource
      this.separatedDataSource = null;

      if (!nextProps.selectedKeys) {
        // clear key no longer existed
        // clear checkedKeys according to targetKeys
        const { dataSource, targetKeys = [] } = nextProps;

        const newSourceSelectedKeys = [];
        const newTargetSelectedKeys = [];
        dataSource.forEach(({ key }) => {
          if (sourceSelectedKeys.indexOf(key) >= 0 && !targetKeys.indexOf(key) >= 0) {
            newSourceSelectedKeys.push(key);
          }
          if (targetSelectedKeys.indexOf(key) >= 0 && targetKeys.indexOf(key) >= 0) {
            newTargetSelectedKeys.push(key);
          }
        });
        this.setState({
          sourceSelectedKeys: newSourceSelectedKeys,
          targetSelectedKeys: newTargetSelectedKeys,
        });
      }
    }

    if (nextProps.selectedKeys) {
      const targetKeys = nextProps.targetKeys || [];
      this.setState({
        sourceSelectedKeys: nextProps.selectedKeys.filter(key => !(targetKeys.indexOf(key) >= 0)),
        targetSelectedKeys: nextProps.selectedKeys.filter(key => targetKeys.indexOf(key) >= 0),
      });
    }
  }

  separateDataSource(props) {
    if (this.separatedDataSource) {
      return this.separatedDataSource;
    }

    const { dataSource, rowKey, targetKeys = [] } = props;

    const leftDataSource = [];
    const rightDataSource = new Array(targetKeys.length);
    dataSource.forEach(record => {
      if (rowKey) {
        record.key = rowKey(record);
      }

      // rightDataSource should be ordered by targetKeys
      // leftDataSource should be ordered by dataSource
      const indexOfKey = targetKeys.indexOf(record.key);
      if (indexOfKey !== -1) {
        rightDataSource[indexOfKey] = record;
      } else {
        leftDataSource.push(record);
      }
    });

    this.separatedDataSource = {
      leftDataSource,
      rightDataSource,
    };

    return this.separatedDataSource;
  }

  moveTo = (direction) => {
    const { targetKeys = [], dataSource = [], onChange } = this.props;
    const { sourceSelectedKeys, targetSelectedKeys } = this.state;
    const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys;
    // filter the disabled options
    const newMoveKeys = moveKeys.filter((key) =>
      !dataSource.some(data => !!(key === data.key && data.disabled)),
    );
    // move items to target box
    const newTargetKeys = direction === 'right'
      ? newMoveKeys.concat(targetKeys)
      : targetKeys.filter(targetKey => newMoveKeys.indexOf(targetKey) === -1);

    // empty checked keys
    const oppositeDirection = direction === 'right' ? 'left' : 'right';
    this.setState({
      [this.getSelectedKeysName(oppositeDirection)]: [],
    });
    this.handleSelectChange(oppositeDirection, []);

    if (onChange) {
      onChange(newTargetKeys, direction, newMoveKeys);
    }
  }

  moveToLeft = () => this.moveTo('left');
  moveToRight = () => this.moveTo('right');

  handleSelectChange(direction, holder) {
    const { sourceSelectedKeys, targetSelectedKeys } = this.state;
    const onSelectChange = this.props.onSelectChange;
    if (!onSelectChange) {
      return;
    }

    if (direction === 'left') {
      onSelectChange(holder, targetSelectedKeys);
    } else {
      onSelectChange(sourceSelectedKeys, holder);
    }
  }

  handleSelectAll = (direction, filteredDataSource, checkAll) => {
    const originalSelectedKeys = this.state[this.getSelectedKeysName(direction)] || [];
    const currentKeys = filteredDataSource.map(item => item.key);
    // Only operate current keys from original selected keys
    const newKeys1 = originalSelectedKeys.filter((key) => currentKeys.indexOf(key) === -1);
    const newKeys2 = [...originalSelectedKeys];
    currentKeys.forEach((key) => {
      if (newKeys2.indexOf(key) === -1) {
        newKeys2.push(key);
      }
    });
    const holder = checkAll ? newKeys1 : newKeys2;
    this.handleSelectChange(direction, holder);

    if (!this.props.selectedKeys) {
      this.setState({
        [this.getSelectedKeysName(direction)]: holder,
      });
    }
  }

  handleLeftSelectAll = (filteredDataSource, checkAll) => (
    this.handleSelectAll('left', filteredDataSource, checkAll)
  )
  handleRightSelectAll = (filteredDataSource, checkAll) => (
    this.handleSelectAll('right', filteredDataSource, checkAll)
  )

  handleFilter = (direction, e) => {
    this.setState({
      // add filter
      [`${direction}Filter`]: e.target.value,
    });
    if (this.props.onSearchChange) {
      this.props.onSearchChange(direction, e);
    }
  }

  handleLeftFilter = (e) => this.handleFilter('left', e);
  handleRightFilter = (e) => this.handleFilter('right', e);

  handleClear = (direction) => {
    this.setState({
      [`${direction}Filter`]: '',
    });
  }

  handleLeftClear = () => this.handleClear('left');
  handleRightClear = () => this.handleClear('right');

  handleSelect = (direction, selectedItem, checked) => {
    const { sourceSelectedKeys, targetSelectedKeys } = this.state;
    const holder = direction === 'left' ? [...sourceSelectedKeys] : [...targetSelectedKeys];
    const index = holder.indexOf(selectedItem.key);
    if (index > -1) {
      holder.splice(index, 1);
    }
    if (checked) {
      holder.push(selectedItem.key);
    }
    this.handleSelectChange(direction, holder);

    if (!this.props.selectedKeys) {
      this.setState({
        [this.getSelectedKeysName(direction)]: holder,
      });
    }
  }

  handleLeftSelect = (selectedItem, checked) => {
    return this.handleSelect('left', selectedItem, checked);
  }

  handleRightSelect = (selectedItem, checked) => {
    return this.handleSelect('right', selectedItem, checked);
  }

  handleScroll = (direction, e) => {
    const { onScroll } = this.props;
    if (onScroll) {
      onScroll(direction, e);
    }
  }

  handleLeftScroll = (e) => this.handleScroll('left', e);
  handleRightScroll = (e) => this.handleScroll('right', e);

  getTitles(transferLocale) {
    const { props } = this;
    if (props.titles) {
      return props.titles;
    }
    return transferLocale.titles;
  }

  getSelectedKeysName(direction) {
    return direction === 'left' ? 'sourceSelectedKeys' : 'targetSelectedKeys';
  }

  getLocale = (transferLocale) => {
    // Keep old locale props still working.
    const oldLocale = {};
    if ('notFoundContent' in this.props) {
      oldLocale.notFoundContent = this.props.notFoundContent;
    }
    if ('searchPlaceholder' in this.props) {
      oldLocale.searchPlaceholder = this.props.searchPlaceholder;
    }

    return ({ ...transferLocale, ...oldLocale, ...this.props.locale });
  }
  // 上下排序
  onLeftUpClick = (item, index) => {
    const { leftDataSource } = this.separateDataSource(this.props);
    leftDataSource.splice(index, 1)
    leftDataSource.splice(index - 1, 0, item)
    this.setState({flag: !this.state.flag})
  }
  onLeftDownClick = (item, index) => {
    const { leftDataSource } = this.separateDataSource(this.props);
    leftDataSource.splice(index, 1)
    leftDataSource.splice(index + 1, 0, item)
    this.setState({flag: !this.state.flag})
  }
  onRightUpClick = (item, index) => {
    const { rightDataSource } = this.separateDataSource(this.props);
    rightDataSource.splice(index, 1)
    rightDataSource.splice(index - 1, 0, item)
    this.setState({flag: !this.state.flag})
  }
  onRightDownClick = (item, index) => {
    const { rightDataSource } = this.separateDataSource(this.props);
    rightDataSource.splice(index, 1)
    rightDataSource.splice(index + 1, 0, item)
    this.setState({flag: !this.state.flag})
  }

  renderTransfer = (transferLocale) => {
    const {
      prefixCls = 'idoll-transfer',
      className,
      disabled,
      operations = [],
      showSearch,
      body,
      footer,
      style,
      listStyle,
      operationStyle,
      filterOption,
      render,
      lazy,
      sort,
    } = this.props;
    const locale = this.getLocale(transferLocale);
    const { leftFilter, rightFilter, sourceSelectedKeys, targetSelectedKeys } = this.state;

    const { leftDataSource, rightDataSource } = this.separateDataSource(this.props);
    const leftActive = targetSelectedKeys.length > 0;
    const rightActive = sourceSelectedKeys.length > 0;

    const cls = classNames(className, prefixCls, disabled && `${prefixCls}-disabled`);
    const operationCls = classNames(`${prefixCls}-operation`);

    const titles = this.getTitles(locale);
    return (
      <div className={cls} style={style}>
        <List
          prefixCls={`${prefixCls}-list`}
          titleText={titles[0]}
          flag={this.state.flag}
          sort={sort}
          dataSource={leftDataSource}
          filter={leftFilter}
          filterOption={filterOption}
          style={listStyle}
          checkedKeys={sourceSelectedKeys}
          handleFilter={this.handleLeftFilter}
          handleClear={this.handleLeftClear}
          handleSelect={this.handleLeftSelect}
          handleSelectAll={this.handleLeftSelectAll}
          render={render}
          showSearch={showSearch}
          onDownClick={this.onLeftDownClick}
          onUpClick={this.onLeftUpClick}
          body={body}
          footer={footer}
          lazy={lazy}
          onScroll={this.handleLeftScroll}
          disabled={disabled}
          {...locale}
        />
        <Operation
          className={operationCls}
          rightActive={rightActive}
          rightArrowText={operations[0]}
          moveToRight={this.moveToRight}
          leftActive={leftActive}
          leftArrowText={operations[1]}
          moveToLeft={this.moveToLeft}
          style={operationStyle}
          disabled={disabled}
        />
        <List
          prefixCls={`${prefixCls}-list`}
          titleText={titles[1]}
          flag={this.state.flag}
          sort={sort}
          dataSource={rightDataSource}
          filter={rightFilter}
          filterOption={filterOption}
          style={listStyle}
          checkedKeys={targetSelectedKeys}
          handleFilter={this.handleRightFilter}
          handleClear={this.handleRightClear}
          handleSelect={this.handleRightSelect}
          handleSelectAll={this.handleRightSelectAll}
          render={render}
          showSearch={showSearch}
          body={body}
          footer={footer}
          lazy={lazy}
          onScroll={this.handleRightScroll}
          disabled={disabled}
          onDownClick={this.onRightDownClick}
          onUpClick={this.onRightUpClick}
          {...locale}
        />
      </div>
    );
  }

  render() {
    return (
      <LocaleReceive
        componentName='Transfer'
        defaultLocale={defaultLocale.Transfer}
      >
        {this.renderTransfer}
      </LocaleReceive>
    );
  }
}

