import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Icon } from '@alifd/next';
import CascaderMenu from './CascaderMenu';
import { AddressNode, NodeIDStrict, SelectionChain } from './Types';
import {
  nodeChainFromNode,
  selectionToId,
  getLabelIntl,
  getNodeLevelInTree,
} from './Common';
import { AddressStore } from './AddressStore';

interface AddressEntryProps {
  node: AddressNode;
  onSelect: (node: AddressNode) => void;
  active: boolean;
  selected: boolean;
  maxLevel?: number;
}

class AddressEntry extends React.Component<AddressEntryProps, {}> {
  static propTypes = {
    node: PropTypes.object,
    onSelect: PropTypes.func,
    active: PropTypes.bool,
    selected: PropTypes.bool,
    maxLevel: PropTypes.number,
  };

  constructor(props: AddressEntryProps) {
    super(props);

    this.handleMenuClick = this.handleMenuClick.bind(this);
  }

  handleMenuClick() {
    typeof this.props.onSelect == 'function' &&
      this.props.onSelect(this.props.node);
  }

  showArrow(props = this.props) {
    if (props.maxLevel && getNodeLevelInTree(props.node) >= props.maxLevel) {
      return false;
    }
    return !props.node.isLeaf;
  }

  render() {
    const { node, onSelect, active, selected, maxLevel, ...rest } = this.props;
    return (
      <CascaderMenu.Item
        key={node.id.toString()}
        className={classNames('cn-addr-casitem', {
          'cn-addr-casactive': this.props.active,
        })}
        onClick={this.handleMenuClick}
        {...rest}
      >
        {selected ? (
          <Icon className="cn-addr-caselect" type="select" size="xs" />
        ) : null}
        <span className="cn-addr-castext">{getLabelIntl(node)}</span>
        {!this.showArrow() ? null : (
          <Icon className="cn-addr-casexpand" type="arrow-right" size="xs" />
        )}
      </CascaderMenu.Item>
    );
  }
}

interface AddressIntlCascaderProps {
  value: SelectionChain;
  onChange: (value: NodeIDStrict) => any;
  rootNodeId: NodeIDStrict;
  maxLevel?: number;

  addressStore: AddressStore;
}

export default class AddressIntlCascader extends React.Component<
  AddressIntlCascaderProps,
  {}
> {
  static propTypes = {
    value: PropTypes.arrayOf(PropTypes.string),
    onChange: PropTypes.func,
    rootNodeId: PropTypes.string,
    maxLevel: PropTypes.number,

    addressStore: PropTypes.object,
  };

  static defaultProps = { value: [] };

  activeChainSet: Set<NodeIDStrict> = new Set();

  constructor(props: AddressIntlCascaderProps) {
    super(props);

    this.renderMenu = this.renderMenu.bind(this);
    this.handleSelect = this.handleSelect.bind(this);
  }

  handleSelect(node: AddressNode) {
    typeof this.props.onChange == 'function' && this.props.onChange(node.id);
  }

  renderMenu(
    node: AddressNode,
    zIndex: number,
    activeChainSet: Set<NodeIDStrict>
  ) {
    if (node.isLeaf) {
      return null;
    } else if (!node.children) {
      return null;
    }
    const selectedNodeId = selectionToId(this.props.value);
    return (
      <CascaderMenu
        className="cn-addr-caslist"
        key={node.id.toString()}
        style={{ zIndex }}
      >
        {node.children!.map(child => {
          return (
            <AddressEntry
              key={child.id.toString()}
              node={child}
              onSelect={this.handleSelect}
              active={activeChainSet.has(child.id)}
              selected={child.id == selectedNodeId}
              maxLevel={this.props.maxLevel}
            />
          );
        })}
      </CascaderMenu>
    );
  }

  render() {
    const nodeId = selectionToId(this.props.value);
    const node = this.props.addressStore.impl.getNodeByIdSync(nodeId)!;
    let nodeChain = nodeChainFromNode(node);
    const rootPos = nodeChain.findIndex(n => n.id == this.props.rootNodeId);
    if (rootPos != -1) {
      nodeChain = nodeChain.slice(rootPos);
    }

    this.activeChainSet = new Set();
    nodeChain.forEach(v => this.activeChainSet.add(v.id));

    const menus = nodeChain
      .filter(
        node =>
          !this.props.maxLevel || getNodeLevelInTree(node) < this.props.maxLevel
      )
      .map((node, idx) =>
        this.renderMenu(node, nodeChain.length - idx + 12, this.activeChainSet)
      );

    return <span className="cn-addr-casmenu cn-addr-casmenu-iu">{menus}</span>;
  }
}
