'use strict';

/** @jsx createElement */
import { Component, createElement, PropTypes, cloneElement, render } from 'rax';
import View from 'nuke-view';
import Text from 'nuke-text';
import Mask from 'nuke-mask';
import { textKeys } from 'nuke-helper';
import ThemeProvider from 'nuke-theme-provider';
// import Slip from 'nuke-slip';
import Column from './column';
import { labelMap, valueMap, keyMap } from './util';
import stylesProvider from '../styles';

const noop = () => { };

const { connectStyle } = ThemeProvider;
class Picker extends Component {
  static propTypes = {
    title: PropTypes.node,
    value: PropTypes.array,
    visible: PropTypes.bool,
    trigger: PropTypes.element,

    selectedKey: PropTypes.array,
    defaultVisible: PropTypes.bool,

    toolbar: PropTypes.node,
    hasToolbar: PropTypes.bool,
    dataSource: PropTypes.array,

    onDone: PropTypes.func,
    onCancel: PropTypes.func,
    onClose: PropTypes.func,

    onChange: PropTypes.func,
    afterOpen: PropTypes.func,
    afterClose: PropTypes.func,
    onVisibleChange: PropTypes.func,

    labelMap: PropTypes.func,
    valueMap: PropTypes.func,
    childrenMap: PropTypes.func,
    keyMap: PropTypes.func,
  };

  static defaultProps = {
    dataSource: [],
    selectedKey: '',
    defaultVisible: false,

    hasToolbar: true,

    title: '',
    trigger: null,

    onChange: noop,
    onDone: noop,
    onCancel: noop,
    onClose: noop,
    afterOpen: noop,
    afterClose: noop,
    onVisibleChange: noop,

    labelMap,
    valueMap,
    childrenMap,
    keyMap,
  };

  static childContextTypes = {
    onUpdate: PropTypes.func,
    onChange: PropTypes.func,
    __picker__: PropTypes.bool,
    selectedValue: PropTypes.array,
  };

  constructor(props) {
    super(props);

    let visible; let
      value;

    if ('visible' in props) {
      visible = props.visible;
    } else {
      visible = props.defaultVisible;
    }

    if ('value' in props) {
      value = props.value;
    } else {
      value = this.handleSelectedKey();
    }

    this.state = {
      value,
      visible,
    };

    this.items = [];
    this.columns = null; // calculate at some time

    [
      'onDone',
      'onCancel',
      'onClose',
      'onChange',
      'onUpdate',
      'onVisibleChange',
      'afterOpen',
      'maskPress',
      'afterClose',
    ].forEach((m) => {
      this[m] = this[m].bind(this);
    });
  }
  handleSelectedKey() {
    const { selectedKey, dataSource } = this.props;
    let val;
    if (!selectedKey) {
      if (dataSource[0].children) {
        val = [dataSource[0].key, dataSource[0].children[0].key];
      } else {
        val = dataSource[0].key;
      }
    } else {
      val = selectedKey;
    }
    if (!val) {
      if (dataSource[0].children) {
        val = [dataSource[0].key, dataSource[0].children[0].key];
      } else {
        val = [dataSource[0].key];
      }
    }
    if (typeof val === 'string') {
      val = [val];
    }
    return val;
  }
  getChildContext() {
    return {
      __picker__: true,
      onUpdate: this.onUpdate,
      onChange: this.onChange,
      selectedValue: this.state.value,
    };
  }

  componentWillReceiveProps(nextProps) {
    // controlled component
    // sync state from props
    if ('visible' in nextProps) {
      this.setState({
        visible: nextProps.visible,
      });
    }

    if ('value' in nextProps) {
      this.setState({
        value: nextProps.value,
      });
    }
  }

  componentWillUpdate(nextProps, nextState) {
    if (
      nextState.value !== this.state.value ||
      nextProps.dataSource !== this.props.dataSource ||
      JSON.stringify(nextState.value) !== JSON.stringify(this.state.value) ||
      JSON.stringify(nextProps.dataSource) !==
      JSON.stringify(this.props.dataSource)
    ) {
      this.changed = true;
    }
  }

  componentDidUpdate() {
    this.changed = false;
  }

  getColumns(value) {
    // debugger;
    const { dataSource } = this.props;

    let columns = [];

    value = value || this.state.value;

    // normal data
    if ('children' in dataSource[0]) {
      // cascade data
      let cols = dataSource;


      let flag = false;

      if (cols.length > 0) {
        if (value === '') {
          value = [];
        }
        columns.push(cols);
      }

      for (let i = 0; ; i++) {
        for (let j = 0, l = cols.length; j < l; j++) {
          const col = cols[j];
          const colValue = value[i];

          if (keyMap(col) === colValue) {
            cols = childrenMap(col);
            flag = true;
            break;
          }
        }

        if (!flag) {
          if (cols.length > 0) {
            value[i] = keyMap(cols[0]);
            cols = childrenMap(cols[0]);
          } else {
            cols = null;
            value[i] = null;
          }
        }

        if (!cols || !cols.length) {
          break;
        } else {
          columns.push(cols);
          flag = false;
        }
      }
    } else {
      columns = [dataSource];
    }

    // column change -> value change
    // dont setState() re-render
    if (value instanceof Array) {
      value.splice(columns.length);
    }

    this.columns = columns;

    return columns;
  }

  show() {
    this.setState({
      visible: true,
    });
    this.picker && this.picker.show();
  }

  hide() {
    this.picker && this.picker.hide();
    this.setState({
      visible: false,
    });
  }

  setVisible(visible, cb, ...args) {
    if (this.state.visible === visible) {
      return;
    }

    const { [`${cb}`]: callback } = this.props;

    // if (!('visible' in this.props)) {
    if (callback && callback(...args) !== false) {
      if (visible) {
        this.show();
      } else {
        this.hide();
      }
      this.setState({ visible });
    }
    // } else {
    //   callback.apply(null, args);
    // }
  }

  onDone(e) {
    this.setVisible(false, 'onDone', this.items, e);
  }

  onCancel(e) {
    this.setVisible(false, 'onCancel', e);
  }

  onClose(e) {
    this.setVisible(false, 'onClose', e);
  }

  onUpdate(item, index) {
    // debugger;
    this.items[index] = item;
    // console.log(this.items);
  }

  onChange(v, item, i) {
    const items = this.items;
    const { value } = this.state;
    let newValue; let
      newItems;

    const { dataSource, onChange } = this.props;

    newValue = [...value];
    newValue[i] = v;

    newItems = [...items];
    newItems[i] = item;

    if (!('value' in this.props)) {
      this.setState(
        {
          value: newValue,
        },
        () => {
          // use callback beacuse of dynamic data
          if (onChange && onChange(newItems) === false) {
            // reset
            this.setState({
              value,
            });
          }
        }
      );
    } else {
      // cascade data normalize value
      if (!(this.props.dataSource[0] instanceof Array)) {
        this.getColumns(value);
      }
      onChange(newItems);
    }
  }

  onVisibleChange(visible) {
    this.setVisible(visible, 'onVisibleChange', visible);
  }

  afterOpen() {
    // debugger;
    this.props.afterOpen && this.props.afterOpen(this);
  }

  afterClose() {
    this.props.afterClose();
  }

  getPickerEl() {
    const {
      hasToolbar,
      hasToolbarButton,
      hasBottomButton,
      buttonStyle,
      title,
      childrenMap,
      style = {},
      locale,
    } = this.props;
    const styles = this.props.themeStyle;
    let { children } = this.props;
    let columns = this.columns;

    const toolbar = (
      <View style={styles['picker-toolbar']}>
        {hasToolbarButton ? (
          <View style={styles['picker-cancel']} onClick={this.onCancel}>
            {locale.cancel}
          </View>
        ) : null}
        <View style={styles['picker-title']}>{title}</View>
        {hasToolbarButton ? (
          <View style={styles['picker-done']} onClick={this.onDone}>
            {locale.confirm}
          </View>
        ) : null}
      </View>
    );

    if (!children) {
      if (!columns || this.changed) {
        columns = this.getColumns();
      }

      children = columns.map((col, index) => (
        <Column
          key={index}
          index={index}
          dataSource={col}
          labelMap={labelMap}
          valueMap={valueMap}
          keyMap={keyMap}
        />
      ));
    } else if (children.map) {
      children = children.map((col, index) => cloneElement(col, { index }));
    } else {
      const index = 0;
      children = cloneElement(children, { index });
    }
    const bottomButtonStyle = Object.assign(
      {},
      styles['picker-bottom-button'],
      buttonStyle
    );
    const bottomButtonTextStyle = Object.assign(
      {},
      styles['picker-bottom-button-text']
    );
    textKeys.forEach((item) => {
      if (bottomButtonStyle[item]) {
        bottomButtonTextStyle[item] = bottomButtonStyle[item];
      }
      // 也加上伪类继承
      if (bottomButtonStyle[`${item}:active`]) {
        bottomButtonTextStyle[`${item}:active`] =
          bottomButtonStyle[`${item}:active`];
      }
    });
    const pickerWrapStyle = Object.assign({}, styles.picker, style);
    return (
      <View x="picker-wrap" style={pickerWrapStyle}>
        {hasToolbar ? toolbar : null}
        <View x="picker-content" style={styles['picker-content']}>
          {children}
        </View>
        {hasBottomButton ? (
          <View style={bottomButtonStyle} onClick={this.onDone}>
            <Text style={bottomButtonTextStyle}>{locale.confirm}</Text>
          </View>
        ) : null}
      </View>
    );
  }
  maskPress() {
    const { onCancel } = this.props;
    onCancel && onCancel();
  }
  touchMove(e) {
    e.preventDefault();
  }
  render() {
    const { visible } = this.state;
    const { trigger, renderMask, style = {}, maskClosable } = this.props;
    const styles = this.props.themeStyle;
    let picker = null;

    picker = this.getPickerEl();

    return renderMask ? (
      <Mask
        style={styles.mask}
        defaultVisible={false}
        maskClosable={maskClosable}
        animate
        onShow={this.afterOpen}
        onHide={this.afterClose}
        onMaskPress={this.maskPress}
        onTouchmove={this.touchMove}
        ref={(n) => {
          this.picker = n;
        }}
      >
        {picker}
      </Mask>
    ) : (
      picker
    );
  }
}

// for cascade data，default key: children
function childrenMap(item) {
  if (typeof item === 'object') {
    return item.children;
  }
}
Picker.defaultProps = {
  locale: {
    cancel: 'Cancel',
    confirm: 'Confirm',
  },
  renderMask: true,
};
Picker.propTypes = {
  locale: PropTypes.obj,
  renderMask: PropTypes.boolean,
};
Picker.displayName = 'Picker';
const StyledPicker = connectStyle(stylesProvider)(Picker);

export default StyledPicker;
