import PropTypes from '../../_util/vue-types';
import Trigger from '../../vc-trigger';
import placements from './placements';
import { hasProp, getEvents, getOptionProps } from '../../_util/props-util';
import BaseMixin from '../../_util/BaseMixin';
import { cloneElement } from '../../_util/vnode';

export default {
  mixins: [BaseMixin],
  props: {
    minOverlayWidthMatchTrigger: PropTypes.bool,
    prefixCls: PropTypes.string.def('rc-dropdown'),
    transitionName: PropTypes.string,
    overlayClassName: PropTypes.string.def(''),
    openClassName: PropTypes.string,
    animation: PropTypes.any,
    align: PropTypes.object,
    overlayStyle: PropTypes.object.def(() => ({})),
    placement: PropTypes.string.def('bottomLeft'),
    overlay: PropTypes.any,
    trigger: PropTypes.array.def(['hover']),
    alignPoint: PropTypes.bool,
    showAction: PropTypes.array.def([]),
    hideAction: PropTypes.array.def([]),
    getPopupContainer: PropTypes.func,
    visible: PropTypes.bool,
    defaultVisible: PropTypes.bool.def(false),
    mouseEnterDelay: PropTypes.number.def(0.15),
    mouseLeaveDelay: PropTypes.number.def(0.1),
  },
  data() {
    let sVisible = this.defaultVisible;
    if (hasProp(this, 'visible')) {
      sVisible = this.visible;
    }
    return {
      sVisible,
    };
  },
  watch: {
    visible(val) {
      if (val !== undefined) {
        this.setState({
          sVisible: val,
        });
      }
    },
  },
  methods: {
    onClick(e) {
      // do no call onVisibleChange, if you need click to hide, use onClick and control visible
      if (!hasProp(this, 'visible')) {
        this.setState({
          sVisible: false,
        });
      }
      this.$emit('overlayClick', e);
      if (this.childOriginEvents.click) {
        this.childOriginEvents.click(e);
      }
    },

    onVisibleChange(visible) {
      if (!hasProp(this, 'visible')) {
        this.setState({
          sVisible: visible,
        });
      }
      this.__emit('visibleChange', visible);
    },

    getMinOverlayWidthMatchTrigger() {
      const props = getOptionProps(this);
      const { minOverlayWidthMatchTrigger, alignPoint } = props;
      if ('minOverlayWidthMatchTrigger' in props) {
        return minOverlayWidthMatchTrigger;
      }

      return !alignPoint;
    },

    getOverlayElement() {
      const overlay = this.overlay || this.$slots.overlay || this.$scopedSlots.overlay;
      let overlayElement;
      if (typeof overlay === 'function') {
        overlayElement = overlay();
      } else {
        overlayElement = overlay;
      }
      return overlayElement;
    },

    getMenuElement() {
      const { onClick, prefixCls, $slots } = this;
      this.childOriginEvents = getEvents($slots.overlay[0]);
      const overlayElement = this.getOverlayElement();
      const extraOverlayProps = {
        props: {
          prefixCls: `${prefixCls}-menu`,
          getPopupContainer: () => this.getPopupDomNode(),
        },
        on: {
          click: onClick,
        },
      };
      if (typeof overlayElement.type === 'string') {
        delete extraOverlayProps.props.prefixCls;
      }
      return cloneElement($slots.overlay[0], extraOverlayProps);
    },

    getMenuElementOrLambda() {
      const overlay = this.overlay || this.$slots.overlay || this.$scopedSlots.overlay;
      if (typeof overlay === 'function') {
        return this.getMenuElement;
      }
      return this.getMenuElement();
    },

    getPopupDomNode() {
      return this.$refs.trigger.getPopupDomNode();
    },

    getOpenClassName() {
      const { openClassName, prefixCls } = this.$props;
      if (openClassName !== undefined) {
        return openClassName;
      }
      return `${prefixCls}-open`;
    },

    afterVisibleChange(visible) {
      if (visible && this.getMinOverlayWidthMatchTrigger()) {
        const overlayNode = this.getPopupDomNode();
        const rootNode = this.$el;
        if (rootNode && overlayNode && rootNode.offsetWidth > overlayNode.offsetWidth) {
          overlayNode.style.minWidth = `${rootNode.offsetWidth}px`;
          if (
            this.$refs.trigger &&
            this.$refs.trigger._component &&
            this.$refs.trigger._component.$refs &&
            this.$refs.trigger._component.$refs.alignInstance
          ) {
            this.$refs.trigger._component.$refs.alignInstance.forceAlign();
          }
        }
      }
    },

    renderChildren() {
      const children = this.$slots.default && this.$slots.default[0];
      const { sVisible } = this;
      return sVisible && children
        ? cloneElement(children, { class: this.getOpenClassName() })
        : children;
    },
  },

  render() {
    const {
      prefixCls,
      transitionName,
      animation,
      align,
      placement,
      getPopupContainer,
      showAction,
      hideAction,
      overlayClassName,
      overlayStyle,
      trigger,
      ...otherProps
    } = this.$props;
    let triggerHideAction = hideAction;
    if (!triggerHideAction && trigger.indexOf('contextmenu') !== -1) {
      triggerHideAction = ['click'];
    }

    const triggerProps = {
      props: {
        ...otherProps,
        prefixCls,
        popupClassName: overlayClassName,
        popupStyle: overlayStyle,
        builtinPlacements: placements,
        action: trigger,
        showAction,
        hideAction: triggerHideAction || [],
        popupPlacement: placement,
        popupAlign: align,
        popupTransitionName: transitionName,
        popupAnimation: animation,
        popupVisible: this.sVisible,
        afterPopupVisibleChange: this.afterVisibleChange,
        getPopupContainer,
      },
      on: {
        popupVisibleChange: this.onVisibleChange,
      },
      ref: 'trigger',
    };
    return (
      <Trigger {...triggerProps}>
        {this.renderChildren()}
        <template slot="popup">{this.$slots.overlay && this.getMenuElement()}</template>
      </Trigger>
    );
  },
};
