<script>
import Icon from "../icon"
import TabBar from "./tab-bar"
import { addListener, removeListener } from "resize-detector/dist"

function noop() {}
const firstUpperCase = str => {
  return str.toLowerCase().replace(/( |^)[a-z]/g, L => L.toUpperCase())
}

export default {
  name: "mm-tab-nav",
  components: {
    TabBar,
    Icon,
  },
  inject: {
    rootTabs: {
      from: "mm-tabs",
      default: null,
    },
  },
  props: {
    panes: Array,
    currentName: String,
    editable: Boolean,
    onTabClick: {
      type: Function,
      default: noop,
    },
    onTabRemove: {
      type: Function,
      default: noop,
    },
    type: String,
  },
  data() {
    return {
      scrollable: false,
      navOffset: 0,
      isFocus: false,
      focusable: true,
    }
  },
  computed: {
    navStyle() {
      const dir = ["top", "bottom"].includes(this.rootTabs.tabPosition) ? "X" : "Y"

      return {
        transform: `translate${dir}(-${this.navOffset}px)`,
      }
    },
    sizeName() {
      return ["top", "bottom"].includes(this.rootTabs.tabPosition) ? "width" : "height"
    },
  },
  updated() {
    this.update()
  },
  mounted() {
    addListener(this.$el, this.update)
    document.addEventListener("visibilitychange", this.visibilityChangeHandler)
    window.addEventListener("blur", this.windowBlurHandler)
    window.addEventListener("focus", this.windowFocusHandler)
  },
  beforeDestroy() {
    if (this.$el && this.update) {
      removeListener(this.$el, this.update)
    }
    document.removeEventListener("visibilitychange", this.visibilityChangeHandler)
    window.removeEventListener("blur", this.windowBlurHandler)
    window.removeEventListener("focus", this.windowFocusHandler)
  },
  methods: {
    scrollPrev() {
      const containerSize = this.$refs.navScroll[`offset${firstUpperCase(this.sizeName)}`]
      if (!this.navOffset) {
        return
      }

      this.navOffset = this.navOffset > containerSize ? this.navOffset - containerSize : 0
    },
    scrollNext() {
      const navSize = this.$refs.nav[`offset${firstUpperCase(this.sizeName)}`]
      const containerSize = this.$refs.navScroll[`offset${firstUpperCase(this.sizeName)}`]

      if (navSize - this.navOffset <= containerSize) {
        return
      }

      this.navOffset =
        navSize - this.navOffset > containerSize * 2
          ? this.navOffset + containerSize
          : navSize - containerSize
    },
    scrollToActiveTab() {
      if (!this.scrollable) {
        return
      }

      const nav = this.$refs.nav
      const activeTab = this.$el.querySelector(".mm-tabs-item--active")

      if (!activeTab) {
        return
      }

      const navScroll = this.$refs.navScroll
      const activeTabBounding = activeTab.getBoundingClientRect()
      const navScrollBounding = navScroll.getBoundingClientRect()
      const navBounding = nav.getBoundingClientRect()
      const currentOffset = this.navOffset
      let newOffset = currentOffset

      if (activeTabBounding.left < navScrollBounding.left) {
        newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left)
      }

      if (activeTabBounding.right > navScrollBounding.right) {
        newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right
      }

      if (navBounding.right < navScrollBounding.right) {
        newOffset = nav.offsetWidth - navScrollBounding.width
      }

      this.navOffset = Math.max(newOffset, 0)
    },
    update() {
      if (!this.$refs.nav) {
        return
      }

      const navSize = this.$refs.nav[`offset${firstUpperCase(this.sizeName)}`]
      const containerSize = this.$refs.navScroll[`offset${firstUpperCase(this.sizeName)}`]

      if (containerSize < navSize) {
        this.scrollable = this.scrollable || {}
        this.scrollable.prev = this.navOffset
        this.scrollable.next = this.navOffset + containerSize < navSize
        if (navSize - this.navOffset < containerSize) {
          this.navOffset = navSize - containerSize
        }
      } else {
        this.scrollable = false
        if (this.navOffset > 0) {
          this.navOffset = 0
        }
      }
    },
    changeTab(e) {
      const keyCode = e.keyCode
      let nextIndex
      let currentIndex, tabList

      if ([37, 38, 39, 40].indexOf(keyCode) !== -1) {
        // 左右上下键更换tab
        tabList = e.currentTarget.querySelectorAll("[role=tab]")
        currentIndex = Array.prototype.indexOf.call(tabList, e.target)
      } else {
        return
      }

      if (keyCode === 37 || keyCode === 38) {
        // left
        if (currentIndex === 0) {
          // first
          nextIndex = tabList.length - 1
        } else {
          nextIndex = currentIndex - 1
        }
      } else {
        // right
        if (currentIndex < tabList.length - 1) {
          // not last
          nextIndex = currentIndex + 1
        } else {
          nextIndex = 0
        }
      }

      tabList[nextIndex].focus() // 改变焦点元素
      tabList[nextIndex].click() // 选中下一个tab
      this.setFocus()
    },
    setFocus() {
      if (this.focusable) {
        this.isFocus = true
      }
    },
    removeFocus() {
      this.isFocus = false
    },
    visibilityChangeHandler() {
      const visibility = document.visibilityState
      if (visibility === "hidden") {
        this.focusable = false
      } else if (visibility === "visible") {
        setTimeout(() => {
          this.focusable = true
        }, 50)
      }
    },
    windowBlurHandler() {
      this.focusable = false
    },
    windowFocusHandler() {
      setTimeout(() => {
        this.focusable = true
      }, 50)
    },
  },
  render(h) {
    const {
      type,
      panes,
      editable,
      onTabClick,
      onTabRemove,
      navStyle,
      scrollable,
      scrollNext,
      scrollPrev,
      changeTab,
      setFocus,
      removeFocus,
    } = this
    const scrollBtn = scrollable
      ? [
        <span class={["mm-tabs-nav-prev", scrollable.prev ? "" : "mm-tabs-nav-prev--disabled"]} on-click={scrollPrev}>
          <icon
            name="chevronleft"
            size="0.75rem"
            fill="#909399"
          />
        </span>,
        <span class={["mm-tabs-nav-next", scrollable.next ? "" : "mm-tabs-nav-next--disabled"]} on-click={scrollNext}>
          <icon
            name="chevronright"
            size="0.75rem"
            fill="#909399"
          />
        </span>,
      ]
      : null
    const tabs = this._l(panes, (pane, index) => {
      let tabName = pane.name || pane.index || index
      const closable = pane.isClosable || editable
      const btnClose = closable ? (
        <icon
          class="mm-icon-close"
          name="close"
          size="0.75rem"
          fill="#909399"
          on-click={ev => {
            onTabRemove(pane, ev)
          }}
        />
      ) : null
      const tabLabelContent = pane.$slots.label || pane.label
      const tabindex = pane.active ? 0 : -1
      return (
        <div
          class={{
            "mm-tabs-item": true,
            "mm-tabs-item__card": type === "card",
            "mm-tabs-item__board-card": type === "border-card",
            [`mm-tabs-item__${this.rootTabs.tabPosition}`]: true,
            "mm-tabs-item--active": pane.active,
            "mm-tabs-item--disabled": pane.disabled,
            "mm-tabs-item__closable": closable,
            "mm-tabs-item--focus": this.isFocus,
          }}
          id={`tab-${tabName}`}
          aria-controls={`pane-${tabName}`}
          role="tab"
          aria-selected={pane.active}
          ref="tabs"
          tabindex={tabindex}
          refInFor
          on-focus={() => {
            setFocus()
          }}
          on-blur={() => {
            removeFocus()
          }}
          on-click={ev => {
            removeFocus()
            onTabClick(pane, tabName, ev)
          }}
          on-keydown={ev => {
            if (closable && (ev.keyCode === 46 || ev.keyCode === 8)) {
              onTabRemove(pane, ev)
            }
          }}
        >
          {tabLabelContent}
          {btnClose}
        </div>
      )
    })

    return (
      <div
        class={[
          "mm-tabs-nav-wrap",
          scrollable ? "mm-tabs-nav-wrap__scrollable" : "",
          `mm-tabs-nav-wrap__${this.rootTabs.tabPosition}`,
        ]}
      >
        {scrollBtn}
        <div class={["mm-tabs-nav-scroll"]} ref="navScroll">
          <div class="mm-tabs-nav" ref="nav" style={navStyle} role="tablist" on-keydown={changeTab}>
            {!type ? <tab-bar tabs={panes} /> : null}
            {tabs}
          </div>
        </div>
      </div>
    )
  },
}
</script>
