<template>
  <div class="mm-carousel">
    <div
      :style="{ height: height }"
      class="mm-carousel-container">
      <div
        :class="[ `mm-carousel-arrow-${arrow}` ]"
        class="mm-carousel-arrow">
        <div
          class="carousel-arrow-left"
          @click="prev">
          <ui-icon
            name="chevronleft"
            size="14px"
            fill="#fff"></ui-icon>
        </div>
        <div
          class="carousel-arrow-right"
          @click="next">
          <ui-icon
            name="chevronright"
            size="14px"
            fill="#fff"></ui-icon>
        </div>
      </div>

      <div
        v-show="positionReady"
        :style="{ height: height }"
        class="mm-carousel-content">
        <slot></slot>
      </div>
    </div>

    <slot name="pagination">
      <ul
        v-if="indicator"
        :class="[ `mm-carousel-indicators-${indicatorPosition}` ]"
        class="mm-carousel-indicators">
        <li
          v-for="(item, index) of itemList"
          :key="index"
          :class="{ 'active': index === current }"
          class="carousel-indicator"
          @click="indicatorHandler('click', index)"
          @mouseenter="indicatorHandler('hover', index)"></li>
      </ul>
    </slot>
  </div>
</template>

<script>
import throttle from "lodash/throttle"
import debounce from "lodash/debounce"
import Icon from "../icon"
import config from "../../managers/config"

config.defaults({
  "Carousel.trigger": "hover",
  "Carousel.autoplay": true,
  "Carousel.interval": 5000,
  "Carousel.indicatorPosition": undefined,
  "Carousel.indicator": true,
  "Carousel.arrow": "hover",
  "Carousel.type": "normal",
  "Carousel.easing": "ease-in-out",
  "Carousel.easingTime": 0.4,
  "Carousel.loopPlay": true,
  "Carousel.adaptiveWidth": false,
})

const RESET_AUTO_PLAY_INTERVAL = 1500

export default {
  name: "mm-carousel",
  components: {
    "ui-icon": Icon,
  },
  props: {
    initialIndex: {
      type: Number,
      default: 0,
    },
    height: String,
    trigger: {
      type: String,
      default: () => config.get("Carousel.trigger"),
      validator(value) {
        return ["click", "hover"].includes(value)
      },
    },
    autoplay: {
      type: Boolean,
      default: () => config.get("Carousel.autoplay"),
    },
    interval: {
      type: Number,
      default: () => config.get("Carousel.interval"),
    },
    indicatorPosition: {
      type: String,
      default: () => config.get("Carousel.indicatorPosition"),
    },
    indicator: {
      type: Boolean,
      default: () => config.get("Carousel.indicator"),
    },
    arrow: {
      type: String,
      default: () => config.get("Carousel.arrow"),
      validator(value) {
        return ["always", "hover", "never"].includes(value)
      },
    },
    type: {
      type: String,
      default: () => config.get("Carousel.type"),
      validator(value) {
        return ["card", "normal"].includes(value)
      },
    },
    easing: {
      type: String,
      default: () => config.get("Carousel.easing"),
    },
    easingTime: {
      type: Number,
      default: () => config.get("Carousel.easingTime"),
    },
    loopPlay: {
      type: Boolean,
      default: () => config.get("Carousel.loopPlay"),
    },
    adaptiveWidth: {
      type: Boolean,
      default: () => config.get("Carousel.adaptiveWidth"),
    },
  },
  provide() {
    return {
      "mm-carousel": this,
    }
  },
  data() {
    return {
      positionReady: false,
      itemList: [],
      last: 0,
      current: this.initialIndex,
      setCurrent: null,
      timer: null,
    }
  },
  computed: {
    total() {
      return this.itemList.length
    },
    resetAutoPlay() {
      return debounce(() => {
        this.setAutoPlay()
      }, RESET_AUTO_PLAY_INTERVAL)
    },
  },
  watch: {
    current(newVal, oldVal) {
      this.$emit("change", newVal, oldVal)
    },
    itemList() {
      if (this.timer) {
        clearTimeout(this.timer)
        this.timer = null
      }
      this.setAutoPlay()
    },
  },
  mounted() {
    this.setHandler()
    this.setAutoPlay()
    this.positionReady = true
    if (this.adaptiveWidth) {
      window.addEventListener("resize", this.resetWidth)
    }
  },
  beforeDestroy() {
    if (this.adaptiveWidth) {
      window.removeEventListener("resize", this.resetWidth)
    }
  },
  methods: {
    register(vm) {
      this.itemList.push(vm)
    },
    unregister(vm) {
      const index = this.itemList.indexOf(vm)
      if (index > -1) {
        this.itemList.slice(index, 1)
      }
    },
    resetWidth() {
      this.$nextTick(() => {
        this.itemList.forEach(item => {
          item.computedWidth()
        })
      })
    },
    setHandler() {
      this.setCurrent = throttle(function(index) {
        this.last = this.current
        this.current = index
      }, this.easingTime * 1000)
    },
    setAutoPlay() {
      if (!this.autoplay || this.timer) {
        return false
      }

      if (this.current === this.total - 1 && !this.loopPlay) {
        return false
      }

      this.timer = setTimeout(() => {
        this.next()
        this.timer = null
        this.setAutoPlay()
      }, this.interval)
    },
    getNextIndex(direction) {
      const { current, total, loopPlay } = this
      const max = total - 1
      let res = current + direction

      if (res > max) {
        res = loopPlay ? 0 : max
      } else if (res < 0) {
        res = loopPlay ? max : 0
      }

      return res
    },
    prev(e) {
      if (e) {
        this.clearAutoPlay()
      }
      this.setCurrent(this.getNextIndex(-1))
    },
    next(e) {
      if (e) {
        this.clearAutoPlay()
      }
      this.setCurrent(this.getNextIndex(1))
    },
    indicatorHandler(type, index) {
      if (type === this.trigger) {
        this.clearAutoPlay()
        this.setCurrent(index)
      }
    },
    clearAutoPlay() {
      if (this.timer) {
        clearTimeout(this.timer)
        this.timer = null
      }

      this.resetAutoPlay()
    },
  },
}
</script>
