<template>
  <div
    :class="_class"
    ref="sliderDom"
    @click.stop="onClick"
  >
    <div :class="_bgClass" />
    <div :class="_containerClass">
      <div
        :class="_leftBarClass"
        @mousedown.stop="onMouseDown(1, $event)"
        :style="{'width': barLeftWidth + '%'}"
        v-if="range"
      >
        <ToolTip
          :message="leftToolTipMsg.toString()"
          :show="showToolTip === true ? showToolTip : leftTooltipShow"
          trigger-type="manual"
          dark
        >
          <div
            :class="_leftIndicator"
            @mouseenter="onMouseEnter(1)"
            @mouseleave="onMouseLeave"
          />
        </ToolTip>
      </div>
      <div
        :class="_rightBarClass"
        @mousedown.stop="onMouseDown(2, $event)"
        :style="{'width': barRightWidth + '%'}"
      >
        <ToolTip
          :message="rightToolTipMsg.toString()"
          :show="showToolTip === true ? showToolTip : rightTooltipShow"
          trigger-type="manual"
          dark
        >
          <div
            :class="_rightIndicator"
            @mouseenter="onMouseEnter(2)"
            @mouseleave="onMouseLeave"
          />
        </ToolTip>
      </div>
      <div :class="_markClass">
        <template v-if="marks">
          <span
            v-for="(item, i) in marks"
            class="hive-slider__mark_i"
            :class="{'hive-slider__mark_i--focus': (marksDistance * i) >= barLeftWidth && (marksDistance * i) <= (100 - barRightWidth)}"
            :key="i"
            :style="{ left: (marksDistance * i) + '%' }"
          >{{ item }}</span>
        </template>
      </div>
    </div>
  </div>
</template>

<script>
import ToolTip from '../tooltip';

const name = 'hive-slider';

export default {
  name,
  components: {
    ToolTip
  },
  props: {
    /**
     * 滑块是否禁用
     */
    disabled: {
      type: Boolean,
      default: false
    },
    /**
     * 步长 , 必须大于 0
     */
    step: {
      type: Number,
      default: 1
    },
    /**
     * 双滑块模式
     */
    range: {
      type: Boolean,
      default: false
    },
    /**
     * 刻度标记
     */
    marks: Array,
    /**
     * 初始值, 当 range 为 false 时，defaultValue 默认为 0 ；当 range 为 true 时，defaultValue 默认为 [0, 100]
     */
    defaultValue: {
      type: [Number, Array],
      default() {
        const returnVal = this.range ? [0, 100] : 0;
        return returnVal;
      }
    },
    /**
     * 设置当前取值。当 range 为 false 时，使用 number，否则用 [number, number]
     */
    isValue: [Number, Array],
    /**
     * 是否始终显示 ToolTip , 当值为 true 时 , ToolTip 始终显示, 当值为 false 时 , ToolTip 始终隐藏
     */
    showToolTip: Boolean,
    /**
     * 自定义 ToolTip 内容 , renderTip , 当 renderTip 为 null 时则隐藏 ToolTip
     */
    renderTip: Function,
    /**
     * 当 Slider 发生改变时，触发 onChange 事件，并返回 Slider 当前值，当 range 为 true 是，返回值为 [number, number]
     */
    onChange: Function
  },
  data() {
    return {
      sliderDomWidth: 0, // 最外层宽度
      sliderDomLeft: 0, // 最外层左距离
      calculated: 0, // 鼠标点击与元素的距离
      barLeftWidth: 0,
      barRightWidth: 100,
      canMoveLeft: false,
      canMoveRight: false,
      effectiveLeave: true,
      leftTooltipShow: false,
      rightTooltipShow: false,
      marksDistance: 0,
      clickChange: true,
      leftToolTipMsg: 0,
      rightToolTipMsg: 0
    };
  },
  computed: {
    _class() {
      return [
        `${name}`,
        {
          [`${name}--disabled`]: this.disabled
        }
      ];
    },
    _bgClass() {
      return [
        `${name}__background`
      ];
    },
    _containerClass() {
      return [
        `${name}__container`
      ];
    },
    _markClass() {
      return [
        `${name}__mark`
      ];
    },
    _leftBarClass() {
      return [
        `${name}__bar`,
        `${name}__leftbar`
      ];
    },
    _rightBarClass() {
      return [
        `${name}__bar`,
        `${name}__rightbar`
      ];
    },
    _leftIndicator() {
      return [
        `${name}__indicator`,
        `${name}__leftindicator`,
        {
          [`${name}__indicator--toplevel`]: this.showToolTip || this.leftTooltipShow
        }
      ];
    },
    _rightIndicator() {
      return [
        `${name}__indicator`,
        `${name}__rightindicator`,
        {
          [`${name}__indicator--toplevel`]: this.showToolTip || this.rightTooltipShow
        }
      ];
    }
  },
  watch: {
    isValue() {
      this.setBarWidth(this.isValue);
    },
    barLeftWidth() {
      this.barWidthChange();
    },
    barRightWidth() {
      this.barWidthChange();
    }
  },
  mounted() {
    if (this.marks) {
      this.marksDistance = 100 / (this.marks.length - 1);
    }
    setTimeout(() => {
      this.sliderDomLeft = this.$refs.sliderDom.offsetLeft;
      this.sliderDomWidth = this.$refs.sliderDom.offsetWidth;
    }, 200);

    this.isValue && this.isValue.length > 0 ? this.setBarWidth(this.isValue) : this.setBarWidth(this.defaultValue);
    window.addEventListener('resize', this.onResize, false);
  },
  beforeDestroy() {
    window.removeEventListener('mousemove', this.onMouseMove);
    window.removeEventListener('mouseup', this.onMouseUp);
    window.removeEventListener('resize', this.onResize);
  },
  methods: {
    onClick(e) {
      if (!this.disabled) {
        this.calculated = ((e.clientX - this.sliderDomLeft) / this.sliderDomWidth) * 100;

        let moveDistance;
        if (this.calculated < 0) {
          moveDistance = 0;
        } else if (this.calculated > 100) {
          moveDistance = 100;
        } else {
          moveDistance = this.getStep(this.calculated);
        };

        const leftDistance = this.calculated - this.barLeftWidth;
        const rightDistance = (100 - this.barRightWidth) - this.calculated;
        if (this.range) {
          if (leftDistance <= 0 || rightDistance > leftDistance) {
            this.barLeftWidth = moveDistance;
          } else {
            this.barRightWidth = (100 - moveDistance);
          }
        } else {
          this.barRightWidth = (100 - moveDistance);
        }

        this.clickChange && this.onHandleChange();
      }
    },
    onMouseDown(value, e) {
      if (!this.disabled) {
        this.effectiveLeave = false;

        if (value === 1) {
          this.canMoveLeft = true;
          this.leftTooltipShow = true;
        } else {
          this.canMoveRight = true;
          this.rightTooltipShow = true;
        };

        if (Math.round(100 - this.barRightWidth) - Math.round(this.barLeftWidth) === 0) {
          this.canMoveLeft = true;
          this.canMoveRight = true;
        };

        this.clickChange = false;
        window.addEventListener('mousemove', this.onMouseMove, false);
        window.addEventListener('mouseup', this.onMouseUp, false);
      };
    },
    onMouseMove(e) {
      this.calculated = ((e.clientX - this.sliderDomLeft) / this.sliderDomWidth) * 100;

      let moveDistance;
      if (this.calculated < 0) {
        moveDistance = 0;
      } else if (this.calculated > 100) {
        moveDistance = 100;
      } else {
        moveDistance = this.getStep(this.calculated);
      };

      // 判断移动哪个点
      if (this.canMoveLeft) {
        if (moveDistance >= (100 - this.barRightWidth)) {
          this.canMoveLeft = false;
          this.canMoveRight = true;
          this.leftTooltipShow = true;
          this.rightTooltipShow = false;
          this.barLeftWidth = (100 - this.barRightWidth);
        } else {
          this.barLeftWidth = moveDistance;
        }
      }
      if (this.canMoveRight) {
        if (moveDistance <= this.barLeftWidth) {
          this.canMoveLeft = true;
          this.canMoveRight = false;
          this.leftTooltipShow = false;
          this.rightTooltipShow = true;
          this.barRightWidth = (100 - this.barLeftWidth);
        } else {
          this.barRightWidth = (100 - moveDistance);
        }
      }
    },
    onMouseUp(e) {
      this.canMoveLeft = false;
      this.canMoveRight = false;
      this.leftTooltipShow = false;
      this.rightTooltipShow = false;
      this.effectiveLeave = true;
      window.removeEventListener('mousemove', this.onMouseMove);
      window.removeEventListener('mouseup', this.onMouseUp);

      !this.clickChange && this.onHandleChange();
    },
    onHandleChange() {
      this.$nextTick(() => {
        const returnVal = this.range ? [Math.round(this.barLeftWidth), Math.round(100 - this.barRightWidth)] : Math.round(100 - this.barRightWidth);
        this.$emit('onChange', returnVal);
        !this.clickChange && setTimeout(() => {
          this.clickChange = true;
        }, 100);
      });
    },
    onMouseEnter(val) {
      val === 1 ? this.leftTooltipShow = true : this.rightTooltipShow = true;
    },
    onMouseLeave() {
      if (this.effectiveLeave) {
        this.leftTooltipShow = false;
        this.rightTooltipShow = false;
      }
    },
    onResize() {
      this.sliderDomWidth = this.$refs.sliderDom.offsetWidth;
      this.sliderDomLeft = this.$refs.sliderDom.offsetLeft;
    },

    setBarWidth(val) {
      const stepNum = this.step <= 0 ? 1 : this.step;
      let newDefaultValue;
      let multiple1;
      let multiple2;
      if (this.range) {
        if (Array.isArray(val)) {
          val.length = 2;
          multiple1 = Math.round(Math.min.apply(null, val) / stepNum);
          multiple2 = Math.round(Math.max.apply(null, val) / stepNum);
          newDefaultValue = [(stepNum * multiple1), (stepNum * multiple2)];
        } else {
          newDefaultValue = [0, 100];
        }
        this.barLeftWidth = newDefaultValue[0];
        this.barRightWidth = (100 - newDefaultValue[1]);
      } else {
        if (Array.isArray(val)) {
          multiple1 = Math.round(val[0] / stepNum);
        } else {
          multiple1 = Math.round(val / stepNum);
        }
        newDefaultValue = stepNum * multiple1;
        this.barRightWidth = (100 - newDefaultValue);
      }
    },
    getStep(num) {
      const stepNum = this.step <= 0 ? 1 : this.step;

      if (this.step === 1) return num;

      const multiple = Math.round(num / stepNum);

      return (multiple * stepNum);
    },

    barWidthChange() {
      if (this.renderTip) {
        this.leftToolTipMsg = this.renderTip(Math.round(this.barLeftWidth));
        this.rightToolTipMsg = this.renderTip(Math.round(100 - this.barRightWidth));
      } else {
        this.leftToolTipMsg = Math.round(this.barLeftWidth);
        this.rightToolTipMsg = Math.round(100 - this.barRightWidth);
      }
    }

  }
};
</script>
