<template>
  <div class="fbg-popper-box" :class="{[popperClass]: true, hide: !visible}" ref="mainRef" @click="handleClick">
    <div :style="markStyle" v-if="visibleArrow" :class="{[positionDirectionClass]: true,[positionVerticalClass]: true}"></div>
    <slot />
  </div>
</template>
<script>
import util from '../../utils';
export default {
  props: {
    popupStyle: {
      type: String,
      defualt: ''
    },
    visible: {
      type: Boolean,
      default: false
    },
    visibleArrow: {
      type: Boolean,
      default: true
    },
    autoWidth: {
      type: Boolean,
      default: false
    },

    options: {
      type: Object,
      default() {
        return {
          boundariesElement: 'body'
        };
      }
    },
    placement: {
      type: String,
      default: 'top-center'
    },
    popperClass: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      el: null,
      markStyle: {},
      bindScrollList: [],
      timer: null,
      zIndex: 0,
      positionDirectionClass: '',
      positionVerticalClass: '',
      resizeFun: null,
      elAttrs: {}
    };
  },
  inject: {
    getMainPopperElement: {
      default: null
    }
  },
  watch: {
    visible: {
      immediate: true,
      handler(value) {
        if (value) {
          if (this.el && !this.zIndex) {
            this.zIndex = util.getMaxIndex(this.el) + 100;
          }
          const el = typeof this.getMainPopperElement === 'function' ? this.getMainPopperElement(): this.el;
          this.bindScroll(el, () => {
            this.el && this.updatePositoin(this.el);;
          })
          this.el && this.updatePositoin(this.el);
        } else {
          this.clearScroll();
        }
      }
    },
    popupStyle: {
      immediate: true,
      handler() {
        this.el && this.updatePositoin(this.el);
      }
    }
  },
  destroyed() {
    if (typeof this.resizeFun === 'function') {
      window.removeEventListener('resize', this.resizeFun);
    }
  },
  methods: {
    resizeAutoUpdate(el) {
      this.resizeFun = () => {
        if (this.visible) {
          this.updatePositoin(el);
        }
      };
      window.addEventListener('resize', this.resizeFun, false);
    },
    init(el) {
      this.el = el;
      this.resizeAutoUpdate(el);
      // this.updatePositoin(el);
    },
    updatePositoin(el) {
      this.timer = setTimeout(() => {
        if (this.visible) {
          this.elAttrs = this.getBaseAttrs(el);
          if (!this.autoWidth) {
            this.$refs.mainRef.style.minWidth = this.elAttrs.width + 'px';
          }
          const placement = this.getAutoPositionType(el, this.placement);
          this.elPosition(el, placement);
        }
        clearTimeout(this.timer);
        this.timer = null;
      }, 180);
    },
    bindScroll(e, callback) {
        if (util.isDom(e) || e === window ) {
            const overflow = e.className ? util.getStyle(e, 'overflow') : 'auto';
            if (['auto', 'scroll'].indexOf(overflow) > -1 || e === window) {
                const cb = () => {
                    typeof callback === 'function' && callback();
                };
                e.addEventListener('scroll', cb, false);
                this.bindScrollList.push({
                  el: e,
                  callback: cb
                });
            }
            if (e.parentNode) {
                this.bindScroll(e.parentNode, callback);
            }
        }
    },
    clearScroll() {
      this.bindScrollList.forEach(item => {
        item.el.removeEventListener('scroll', item.callback);
      });
      this.bindScrollList = [];
    },
    getBaseAttrs(el) {
      const boundariesElement = this.options.boundariesElement;
      const boundariesElementEl = util.getBoundariesElement(el, boundariesElement) || document.body;
      if (['absolute', 'relative', 'fixed'].indexOf(util.getStyle(boundariesElementEl, 'position')) === -1) {
        boundariesElementEl.style.position = 'relative';
      }
      const pos = el.getBoundingClientRect();
      const top = pos.top;
      const left = pos.left;
      const width = pos.width;
      const height = pos.height;
      const boxWidth = util.getDocumentWidth();
      const boxHeight = util.getDocumentHeight();
      const popperWidth = util.getElWidth(this.$refs.mainRef);
      const popperHeight = util.getElHeight(this.$refs.mainRef);
      return {
        boundariesElement,
        boundariesElementEl,
        top,
        left,
        width,
        height,
        boxWidth,
        boxHeight,
        popperWidth,
        popperHeight
      }
    },
    getAutoPositionType(el, type) {
      const {
        boundariesElement,
        boundariesElementEl,
        top,
        left,
        width,
        height,
        boxWidth,
        boxHeight,
        popperWidth,
        popperHeight
      } = this.elAttrs;
      const arr = type.split('-');
      let verticalType = '';
      let horizontalType = '';
      if (this.isVerticalWay(arr[0])) {
        verticalType = arr[0];
        horizontalType = arr[1];
        const right = (boxWidth - left - width);
        const isLeft = (boxWidth - left) > popperWidth + 20;
        const isRight = left + width  > popperWidth + 20;
        const supportVerticalTypes = [];
        const supportHorizontalTypes = [];
        const overflowMargin = (popperWidth - width) / 2;
        if (boxHeight - top - height > popperHeight + 20) {
          supportVerticalTypes.push('bottom');
        }
        if (top > popperHeight) {
          supportVerticalTypes.push('top');
        }

        if (left > overflowMargin && right > overflowMargin) {
          supportHorizontalTypes.push('center');
        }
        if (isLeft) {
          supportHorizontalTypes.push('left');
        }
        if (isRight) {
          supportHorizontalTypes.push('right');
        }
        verticalType = this.getSupportType(supportVerticalTypes, verticalType);
        horizontalType = this.getSupportType(supportHorizontalTypes, horizontalType);
        return [verticalType, horizontalType].join('-');
      } else {
        verticalType = arr[1];
        horizontalType = arr[0];
        const supportVerticalTypes = [];
        const supportHorizontalTypes = [];
        if (left > popperWidth + 20) {
          supportHorizontalTypes.push('left');
        } 
        if (boxWidth - left - width > popperWidth){
          supportHorizontalTypes.push('right');
        }
        const isTop = boxHeight - top - height > popperHeight;
        const isBottom = boxHeight - top > popperHeight;
        if (isTop && isBottom) {
          supportVerticalTypes.push('center');
        }
        if (isTop) {
          supportVerticalTypes.push('top');
        }
        if (isBottom) {
          supportVerticalTypes.push('bottom');
        }
        verticalType = this.getSupportType(supportVerticalTypes, verticalType);
        horizontalType = this.getSupportType(supportHorizontalTypes, horizontalType);
        return [horizontalType, verticalType].join('-');
      }
    },
    getSupportType(list, type) {
        const index = list.indexOf(type);
        if (index === -1) {
          return list[0]||type;
        } else {
          return list[index];
        }
    },
    isVerticalWay(type) {
      return ['top', 'bottom'].indexOf(type) > -1;
    },
    elPosition(el, type) {
      const {
        boundariesElement,
        boundariesElementEl,
        top,
        left,
        width,
        height,
        boxWidth,
        boxHeight,
        popperWidth,
        popperHeight
      } = this.getBaseAttrs(el);
      const margin = this.visibleArrow ? 10 : 0;
      const topPositionValue = boxHeight - top;
      const bottomPositionValue = top + height;
      const centerWidthPositionValue = left - (popperWidth/2 - width/2);
      const centerHeightPositionValue = top - (popperHeight/2 - height/2);
      const leftPositionValue = boxWidth - left - width;
      const rightPositionValue = left;
      this.positionDirectionClass = this.getDirectionClass(type);
      this.positionVerticalClass = this.getAlignClass(type);
      let cssText = `z-index: ${this.zIndex};`
      cssText += this.autoWidth ? '' : `min-width: ${width}px;` ;
      if (this.popupStyle) {
        cssText += this.popupStyle;
      }
      let direction = type.split('-')[0];
      let align = type.split('-')[1];
      switch(direction) {
        case 'top':
          cssText += `margin-bottom: ${margin}px;`;
          break;
        case 'bottom':
          cssText += `margin-top: ${margin}px;`;
          break;
        case 'left':
          cssText += `margin-right: ${margin}px;`;
          break;
        case 'right':
          cssText += `margin-left: ${margin}px;`;
          break;
      }
      let hm = (height - 10) /2;
      let wm = (width - 10) /2;
      let cssKey = {
        left: 'right',
        right: 'left',
        top: 'bottom',
        bottom: 'top'
      };
      this.markStyle = {
        [cssKey[align]]: `${this.isVerticalWay(align)?hm : wm}px`
      };
      switch(type) {
        case 'top-center':
          cssText += `bottom: ${topPositionValue}px; left: ${centerWidthPositionValue}px`;
          break;
        case 'top-right':
          cssText += `bottom: ${topPositionValue}px; right: ${leftPositionValue}px`;
          break;
        case 'top-left':
          cssText += `bottom: ${topPositionValue}px; left: ${rightPositionValue}px`;
          break;
        case 'bottom-center':
          cssText += `top: ${bottomPositionValue}px; left: ${centerWidthPositionValue}px`;
          break;
        case 'bottom-right':
          cssText += `top: ${bottomPositionValue}px; right: ${leftPositionValue}px`;
          break;
        case 'bottom-left':
          cssText += `top: ${bottomPositionValue}px; left: ${rightPositionValue}px`;
          break;
        case 'left-center':
          cssText += `top: ${centerHeightPositionValue}px; right: ${leftPositionValue + width}px`;
          break;
        case 'left-top':
          cssText += `bottom: ${topPositionValue - height}px; right: ${leftPositionValue + width}px`;
          break;
        case 'left-bottom':
          cssText += `top: ${bottomPositionValue - height}px; right: ${leftPositionValue + width}px`;
          break;
        case 'right-center': 
          cssText += `top: ${centerHeightPositionValue}px; left: ${rightPositionValue + width}px`;
          break;
        case 'right-top': 
          cssText += `bottom: ${topPositionValue - height}px; left: ${rightPositionValue + width}px`;
          break;
        case 'right-bottom': 
          cssText += `top: ${bottomPositionValue - height}px; left: ${rightPositionValue + width}px`;
          break;
      }
      this.$refs.mainRef.style.cssText = cssText;
    },
    getDirectionClass(type) {
      return 'fbg-popper-' + type.split('-')[0] + '-mark';
    },
    getAlignClass(type) {
      
      const arr = type.split('-');
      let t = 'vertical'
      if (this.isVerticalWay(arr[0])) {
        t = 'horizontal'
      }
      return 'fbg-popper-' + t + '-mark-' + arr[1];
    },
    handleClick(e) {
      this.$emit('onClick');
    }
  }
}
</script>
<style lang="scss" scoped>
.fbg-popper {
  &-box {
    position: fixed;
    z-index: 999;
    // display: none;
    transition: left, right, top, bottom, width, height, visibility 0.2s ease-in-out;
    box-shadow: 0px 0px 5px $themeShadowColor;
    border: 1px solid #eee;
    background-color: #fff;
  }
  &-box.hide {
    display: none;
  }

  &-vertical-mark {
    &-center {
      top: 50%;
      transform: translateY(-50%);
    }
    &-top {
      bottom: 0px;
    }
    &-bottom {
      top: 0px;
    }
  }
  &-top-mark {
    position: absolute;
    top: 100%;
    width: 10px;
    &::after {
      position: absolute;
      content: '';
      width: 1px;
      left: 1px;
      border-left: 7px solid transparent;
      border-right: 7px solid transparent;
      border-top: 8px solid #fff;
      border-bottom: 0px;
    }
    &::before {
      position: absolute;
      content: '';
      width: 1px;
      border-left: 8px solid transparent;
      border-right: 8px solid transparent;
      border-top: 8px solid #ddd;
    }
  }
  &-bottom-mark {
    position: absolute;
    bottom: 100%;
    height: 8px;
    width: 10px;
    &::after {
      position: absolute;
      content: '';
      width: 1px;
      left: 1px;
      border-left: 7px solid transparent;
      border-right: 7px solid transparent;
      border-bottom: 8px solid #fff;
      border-top: 0px;
    }
    &::before {
      position: absolute;
      content: '';
      width: 1px;
      border-left: 8px solid transparent;
      border-right: 8px solid transparent;
      border-bottom: 7px solid #ddd;
    }
  }
  &-right-mark {
    position: absolute;
    left: 100%;
    height: 10px;
    width: 8px;
    &::after {
      position: absolute;
      content: '';
      height: 0px;
      border-top: 8px solid transparent;
      border-bottom: 8px solid transparent;
      border-left: 8px solid #fff;
      border-right: 8px;
    }
    &::before {
      position: absolute;
      content: '';
      height: 2px;
      border-top: 7px solid transparent;
      border-bottom: 8px solid transparent;
      border-left: 8px solid #ddd;
    }
  }
  &-left-mark {
    position: absolute;
    right: 100%;
    height: 10px;
    width: 8px;
    &::after {
      position: absolute;
      content: '';
      height: 0px;
      border-top: 8px solid transparent;
      border-bottom: 8px solid transparent;
      border-right: 8px solid #fff;
      border-left: 0px;
    }
    &::before {
      position: absolute;
      content: '';
      height: 2px;
      border-top: 7px solid transparent;
      border-bottom: 8px solid transparent;
      border-right: 8px solid #ddd;
    }
  }
  &-horizontal-mark {
    &-center {
      left: 50%;
      transform: translateX(-50%);
    }
    &-left {
      left: 10px;
    }
    &-right {
      right: 10px;
    }
  }
}

</style>