/* @flow */

import React from 'react';
import ReactDOM from 'react-dom';
import Popper from '../../libs/utils/popper';
import { Component, PropTypes, Transition, View } from '../../libs';

type State = {
  showPopper: boolean
};

export default class Popover extends Component {
  state: State;

  static defaultProps = {
    visibleArrow: true,
    transition: 'fade-in-linear',
    trigger: 'click',
    placement: 'bottom',
    width: 150
  }

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

    this.state = {
      showPopper: false
    };
  }

  componentDidMount() {
    const { trigger, visible } = this.props, popper = this.refs.popper;

    this.element = ReactDOM.findDOMNode(this);
    this.reference = ReactDOM.findDOMNode(this.refs.reference);

    if (this.reference === null) return;
    
    if (trigger === 'click') {
      this.reference.addEventListener('click', () => {
        if (visible != null) {
          if (visible) {
            this.handleCancelPopover()
          } else {
            this.handleShowPopover()
          }
        } else {
          this.setState({
            showPopper: !this.state.showPopper
          });
        }
      });

      document.addEventListener('click', this.documentEvent);
    } else if (trigger === 'hover') {
      this.reference.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
      this.reference.addEventListener('mouseleave', this.handleMouseLeave.bind(this));

      popper.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
      popper.addEventListener('mouseleave', this.handleMouseLeave.bind(this));
    } else {
      if (this.reference.nodeName === 'INPUT' || this.reference.nodeName === 'TEXTAREA') {
        this.reference.addEventListener('focus', () => { this.handleShowPopover()});
        this.reference.addEventListener('blur', () => { this.handleCancelPopover()});
      } else {
        this.reference.addEventListener('mousedown', () => { this.handleShowPopover()});
        this.reference.addEventListener('mouseup', () => { this.handleCancelPopover()});
      }
    }
  }

  componentWillReceiveProps(props: Object) {
    if (props.visible != this.props.visible) {
      this.setState({
        showPopper: props.visible
      });
    }
  }

  componentWillUnmount(): void {
    this.reference.parentNode.replaceChild(this.reference.cloneNode(true), this.reference);
    document.removeEventListener('click', this.documentEvent);
  }

  documentEvent = (e: Event): void => {
    const popper = this.refs.popper;
    if (!this.element || this.element.contains(e.target) ||
        !this.reference || this.reference.contains(e.target) ||
        !popper || popper.contains(e.target)) return;
    if (this.props.visible != null) {
      if (!this.props.visible) return;
    } else {
      if (!this.state.showPopper) return;
    }

    this.handleCancelPopover()
  }

  handleMouseEnter(): void {
    clearTimeout(this.timer);

    this.handleShowPopover()
  }

  handleMouseLeave(): void {
    this.timer = setTimeout(() => {
      this.handleCancelPopover()
    }, 200);
  }

  handleShowPopover() {
    if (this.props.onShow != null) {
      this.props.onShow()
    } else {
      this.setState({
        showPopper: true
      });
    }
  }

  handleCancelPopover() {
    if (this.props.onCancel != null) {
      this.props.onCancel()
    } else {
      this.setState({
        showPopper: false
      });
    }
  }

  onEnter(): void {
    if (this.refs.arrow) {
      this.refs.arrow.setAttribute('x-arrow', '');
    }

    this.popperJS = new Popper(this.reference, this.refs.popper, {
      placement: this.props.placement,
      gpuAcceleration: false
    });
  }

  onAfterLeave(): void {
    this.popperJS.destroy();
  }

  render(): React.Element<any> {
    const { transition, popperClass, width, title, content, visibleArrow, visible } = this.props;

    return (
      <span>
        <Transition name={transition} onEnter={this.onEnter.bind(this)} onAfterLeave={this.onAfterLeave.bind(this)}>
          <View show={visible != null ? visible : this.state.showPopper}>
            <div ref="popper" className={this.className('el-popover', popperClass)} style={this.style({ width: Number(width) })}>
              { title && <div className="el-popover__title">{title}</div> }
              { content }
              { visibleArrow && <div ref="arrow" className="popper__arrow"></div>}
            </div>
          </View>
        </Transition>
        {React.cloneElement(React.Children.only(this.props.children), {
          ref: 'reference',
        })}
      </span>
    )
  }
}

Popover.propTypes = {
  width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  placement: PropTypes.oneOf(['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']),
  trigger: PropTypes.oneOf(['click', 'focus', 'hover']),
  title: PropTypes.string,
  content: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
  popperClass: PropTypes.string,
  transition: PropTypes.string,
  visible: PropTypes.bool,
  visibleArrow: PropTypes.bool,
  onShow: PropTypes.func,
  onCancel: PropTypes.func
}
