<template>
  <div
    :class="wrapperClasses"
    :style="wrapperStyle"
    @scroll="handleScroll">
    <!-- <div
      v-if="useVirtual"
      :style="virtualScrollSpaceStyle">
    </div> -->
    <table
      ref="scrollTable"
      cellpadding="0"
      cellspacing="0"
      border="0"
      :style="tableStyle">
      <colgroup>
        <col
          v-if="showSelectCol"
          key="checkbox-cell"
          :width="layout.selectColWidth" />
        <col
          v-for="(column, index) of flattenColumns"
          :key="index"
          :width="column.width || BIG_COL_WIDTH" />
      </colgroup>
      <ui-table-header
        v-if="!height && !maxHeight"
        :columns="columns"
        :fixed="fixed"
        :store="store"
        :row-selection="rowSelection"
        :show-select-column="showSelectCol"
        :sticky="sticky">
      </ui-table-header>
      <tbody ref="tbody">
        <template v-if="renderList">
          <ui-table-row
            v-for="(row, rowIndex) of renderList"
            :key="getRowKey(row, rowIndex)"
            :row-events="rowEvents"
            :row="row"
            :row-index="rowIndex"
            :fixed="fixed"
            :drag-handle="dragHandle"
            :row-height="rowHeight"
            :handle-dragstart="handleDragstart"
            :handle-dragend="handleDragend"
            :handle-dragenter="handleDragenter"
            :handle-drop="handleDrop"
            :sticky="sticky"
            @mouseenter.native="mouseenterHandler"
            @mouseleave.native="mouseleaveHandler">
            <ui-table-selection
              v-if="showSelectCol"
              key="selection"
              :sticky="sticky"
              :row="row"
              :row-index="rowIndex">
            </ui-table-selection>
            <ui-table-cell
              v-for="(column, colIndex) of flattenColumns"
              :key="colIndex"
              :column="column"
              :row="row"
              :row-index="rowIndex"
              :col-index="colIndex"
              :span-method="spanMethod"
              :on-click="handleClick">
            </ui-table-cell>
          </ui-table-row>
        </template>
        <tr v-else-if="!fixed">
          <td
            class="empty-placeholder"
            :colspan="flattenColumns.length + (showSelectCol ? 1 : 0)"></td>
        </tr>
      </tbody>
    </table>
    <div
      v-if="!loading && !fixed && !renderList.length"
      class="mm-empty-no-data">
      <slot name="empty">
        <ui-empty type="small" />
      </slot>
    </div>
  </div>
</template>
<script>
import debounce from "lodash/debounce"
import get from "lodash/get"
import Empty from "../empty"
import Header from "./header"
import Row from "./table-row"
import Cell from "./table-cell"
import Selection from "./table-selection"
import tableMixin from "./table-mixin"

// 初始化时尽量不让内容换行导致表格每行高度过高
const BIG_COL_WIDTH = 10086

export default {
  name: "mm-tbody",
  components: {
    "ui-table-header": Header,
    "ui-table-row": Row,
    "ui-table-cell": Cell,
    "ui-table-selection": Selection,
    "ui-empty": Empty,
  },
  mixins: [tableMixin],
  props: {
    height: {
      type: Number,
      default: 0,
    },
    maxHeight: {
      type: Number,
    },
    loading: {
      type: Boolean,
    },
    draggable: {
      type: Boolean,
      default: false,
    },
    rowEvents: {
      type: Object,
      default: () => ({}),
    },
    spanMethod: Function,
    setTableLayout: Function,
    handleClick: Function,
    rowKey: String,
    rowClass: null,
    summary: Boolean,
    rowHeight: Number,
    remainRows: Number,
    // useVirtual: Boolean,
    sticky: Boolean,
    dragHandle: String,
  },
  data() {
    return {
      BIG_COL_WIDTH,
      startIndex: 0,
      visibleIndex: 0,
    }
  },
  computed: {
    state() {
      return this.rootTable.store.state
    },
    layout() {
      return this.rootTable.store.layout
    },
    viewPortHeight() {
      const {
        scrollBarWidth,
        canScrollX,
        headerHeight,
        pageHeight,
        footerHeight,
      } = this.layout

      const height = this.height || this.maxHeight
      return (
        height -
        headerHeight -
        footerHeight -
        pageHeight +
        (canScrollX ? 0 : scrollBarWidth)
      )
    },
    wrapperClasses() {
      return {
        "mm-tbody": true,
        // "mm-tbody__virtual-wrapper": this.useVirtual,
      }
    },
    wrapperStyle() {
      const {
        scrollBarWidth,
        canScrollX,
        canScrollY,
        headerHeight,
        pageHeight,
        footerHeight,
      } = this.layout

      const height = this.height || this.maxHeight
      const result = {
        marginBottom:
          this.fixed || this.summary || !canScrollX
            ? `-${scrollBarWidth}px`
            : 0,
        marginRight:
          (this.fixed && this.fixed === "left") || !canScrollY
            ? `-${scrollBarWidth}px`
            : 0,
      }
      const resultHeight = `${
        height -
        headerHeight -
        footerHeight -
        pageHeight +
        (canScrollX ? 0 : scrollBarWidth)
      }px`

      if (this.height) {
        return {
          ...result,
          height: resultHeight,
        }
      } else if (this.maxHeight) {
        return {
          ...result,
          maxHeight: resultHeight,
        }
      }

      return result
    },
    // virtualScrollSpaceStyle() {
    //   return { height: `${this.totalHeight}px`, float: "left", width: "0" }
    // },
    debounceResizeHandler() {
      return debounce(this.resizeHandler, 60)
    },
    totalCount() {
      return this.state.filteredRows.length
    },
    totalHeight() {
      return this.totalCount * this.rowHeight
    },
    visibleSize() {
      return Math.ceil(this.viewPortHeight / this.rowHeight)
    },
    renderSize() {
      return this.remainRows + this.visibleSize
    },
    renderList() {
      // if (!this.useVirtual) {
      return this.state.filteredRows
      // }
      // return this.state.filteredRows.slice(
      //   this.startIndex,
      //   this.startIndex + this.renderSize
      // )
    },
  },
  watch: {
    columns: {
      immediate: true,
      deep: true,
      handler() {
        this.$nextTick(() => {
          if (this.$isServer || !this.$el) {
            return
          }
          this.tableLayout()
          if (!this.fixed) {
            this.setRowHeightList()
          }

          this.$el.scrollTop = 0
        })
      },
    },
    "store.state.filteredRows"() {
      if (this.$isServer || !this.$el) {
        return
      }
      this.$nextTick(() => {
        this.tableLayout()
      })
      if (!this.fixed) {
        this.setRowHeightList()
      }
    },
    "store.state.hoverRowIndex"(newVal, oldVal) {
      requestAnimationFrame(() => {
        const rows = this.$el.querySelectorAll(".mm-tbody-tr")
        const oldRow = rows[oldVal]
        const newRow = rows[newVal]
        if (oldRow) {
          oldRow.classList.remove("mm-tbody-tr--active")
        }
        if (newRow) {
          newRow.classList.add("mm-tbody-tr--active")
        }
      })
    },
  },
  inject: {
    rootTable: {
      from: "mm-table",
      default: null,
    },
  },
  mounted() {
    const refKey = !this.fixed
      ? "tbody"
      : this.fixed === "right"
      ? "rightFixedTbody"
      : "fixedTbody"
    this.store.setRef(refKey, this.$el)
    if (!this.fixed) {
      window.addEventListener("resize", this.debounceResizeHandler)
    }
  },
  beforeDestroy() {
    if (!this.fixed) {
      window.removeEventListener("resize", this.debounceResizeHandler)
    }
  },
  methods: {
    getRowKey(row, rowIndex) {
      return this.rowKey ? get(row, this.rowKey) : rowIndex
    },
    resizeHandler() {
      this.setRowHeightList()
      this.tableLayout()
    },
    setRowHeightList() {
      const { tbody } = this.$refs
      this.$nextTick(() => {
        if (tbody) {
          this.store.setLayout(
            "rowHeightList",
            Array.from(tbody.children, (tr) => tr.offsetHeight)
          )
        }
      })
    },
    tableLayout() {
      const { tbody } = this.$refs
      if (tbody && this.setTableLayout && !this.fixed) {
        this.setTableLayout()
      }
    },
    mouseenterHandler: function(e) {
      requestAnimationFrame(() => {
        const { target } = e
        const index = target.rowIndex - (this.height || this.maxHeight ? 0 : 1)
        if (index !== this.store.state.hoverRowIndex) {
          this.store.setState("hoverRowIndex", index)
        }
      })
    },
    mouseleaveHandler: function() {
      requestAnimationFrame(() => {
        this.store.setState("hoverRowIndex", -1)
      })
    },
    handleDragstart(row) {
      if (this.draggable) {
        this.store.setDragRow(row)
      }
    },
    handleDragenter(row, rowIndex) {
      if (this.draggable && this.store.state.dragRow) {
        this.store.setDragoverIndex(row, rowIndex)
      }
    },
    handleDrop(row) {
      if (this.draggable && this.store.state.dragRow) {
        this.store.setDrop(row, (newIndex, oldIndex) => {
          this.$emit("drop", newIndex, oldIndex)
        })
      }
    },
    handleDragend() {
      if (this.draggable) {
        this.store.setDragleave()
      }
    },
    handleScroll(e) {
      window.requestAnimationFrame(() => {
        // sync position
        this.scrollHandler(e)
        // vertical virtual scroll
        // this.useVirtual && this.handleVirtualScrollY(e)
      })
    },
    // handleVirtualScrollY(e) {
    //   const scrollTop = e.target.scrollTop
    //   let startIndex = 0
    //   const { visibleIndex, visibleSize, rowHeight, totalCount, renderSize } = this
    //   const toVisibleIndex = Math.ceil(scrollTop / rowHeight)
    //   const marginSize = Math.min(Math.floor((renderSize - visibleSize) / 2), visibleSize)
    //   if (visibleIndex !== toVisibleIndex) {
    //     if (visibleIndex > toVisibleIndex) {
    //       // scroll up
    //       startIndex = Math.max(0, toVisibleIndex - Math.max(marginSize, renderSize - visibleSize))
    //     } else {
    //       // scroll down
    //       startIndex = Math.max(0, Math.min(toVisibleIndex - marginSize, totalCount - renderSize))
    //     }
    //     this.visibleIndex = toVisibleIndex
    //     this.updateStartIndex(startIndex)
    //     this.$refs.scrollTable.style.transform = `translateY(${this.startIndex * rowHeight}px)`
    //   }
    // },
    // updateStartIndex(startIndex) {
    //   if (this.startIndex === startIndex || (startIndex < this.totalCount - this.renderSize && startIndex % 2 !== 0)) {
    //     return
    //   }
    //   this.startIndex = startIndex
    // },
  },
}
</script>
