<template>
  <div class="v-table" id="v-table">
    <table
      :class="[{ tableBorder: border }]"
      ref="cTable"
      :style="{ 'max-height': maxHeight + 'px' }"
      @scroll="handleScroll"
    >
      <thead>
        <v-thead-tr
          :check="check"
          :index="index"
          :border="border"
          :height="height"
          :indeterminate="indeterminate"
          :scrollShadow="scrollShadow"
        >
          <slot></slot>
        </v-thead-tr>
      </thead>

      <tbody>
        <v-checkbox-group
          v-model="toggleRowSelectionList"
          @change="handleCheckedCitiesChange"
          class="radio-group"
        >
          <v-tbody-tr
            v-for="(row, index) in proxyData"
            v-show="row.rendering || row.rendering === undefined"
            :key="row"
            :row="row"
            :index="index"
            :border="border"
            :height="height"
            :rowClassName="rowClassName"
            :scrollShadow="scrollShadow"
            :highlightCurrentRow="highlightCurrentRow"
            :setHighlightCurrentRowList="setHighlightCurrentRowList"
            :stripe="stripe && index % 2 !== 0"
          >
            <slot></slot>
          </v-tbody-tr>
        </v-checkbox-group>

        <template v-if="showSummary">
          <v-tbody-tr
            :showSummary="showSummary"
            :row="showSummaryObj"
            :sumText="sumText"
            :border="border"
            :scrollShadow="scrollShadow"
          >
            <slot></slot>
          </v-tbody-tr>
        </template>
      </tbody>
    </table>
  </div>
</template>

<script>
import VairTable from "../../types/table";
import calculate from "../../hooks/calculate";
import { ref, watch, watchEffect, onMounted, provide, reactive } from "vue";
import vTheadTr from "./components/Thead-tr";
import vTbodyTr from "./components/Tbody-tr";
export default {
  name: "table",
  components: {
    vTheadTr,
    vTbodyTr,
  },
  props: VairTable,
  setup(props, ctx) {
    const { accAdd } = calculate.setup();
    const toggleRowExpansionObj = reactive({});
    const cTable = ref(null);
    const scrollShadow = ref("right");
    const setHighlightCurrentRowList = ref([]);
    const toggleRowSelectionList = ref([]);
    const check = ref(false);
    const indeterminate = ref(false);
    const proxyData = ref(props.data);
    const clearTableFilter = ref([]);
    const showSummaryObj = ref({});
    const proxyDefaultSort = reactive(
      JSON.parse(JSON.stringify(props.defaultSort))
    );

    onMounted(() => {
      watchEffect(() => {
        if (!isNaN(props.height)) {
          cTable.value.style.height = `${props.height}px`;
        }
      });
    });

    watch(
      () => [...props.data],
      () => {
        proxyData.value = [];
        props.data.forEach((item) => proxyData.value.push(item));
        handleCheckedCitiesChange(toggleRowSelectionList.value);
      }
    );

    const cumulativeProp = (prop) => {
      return proxyData.value.reduce((num, item) => {
        if (item.rendering || item.rendering === undefined) {
          return accAdd(+item[prop] || 0, num);
        } else {
          return num;
        }
      }, 0);
    };

    const computedProp = () => {
      var column = [];
      proxyData.value.forEach((item) => {
        for (let prop in item) {
          column.includes(prop) || column.push(prop);
        }
      });
      return column;
    };

    watchEffect(() => {
      if (props.showSummary) {
        const column = computedProp();
        if (props.summaryMethod) {
          const type = Object.prototype.toString.call(props.summaryMethod);
          var data = [];
          props.data.forEach((item) => {
            if (item.rendering || item.rendering === undefined) {
              data.push(item);
            }
          });
          type === "[object Function]" &&
            (showSummaryObj.value = props.summaryMethod({ column, data }));
        } else {
          column.forEach((prop) => {
            const bool = proxyData.value.some(
              (item) => isNaN(item[prop]) && item[prop] !== undefined
            );
            showSummaryObj.value[prop] = bool ? "" : cumulativeProp(prop);
          });
        }
      }
    });

    const setCurrentRow = (row, bool = true) => {
      var index = props.data.findIndex((item) => item === row);
      if (index !== -1) {
        index = setHighlightCurrentRowList.value.findIndex(
          (item) => item === row
        );
        if (bool) {
          if (index === -1) {
            setHighlightCurrentRowList.value.push(row);
          }
        } else {
          if (index !== -1) {
            setHighlightCurrentRowList.value.splice(index, 1);
          }
        }
      }
    };

    const handleCheckedCitiesChange = (value) => {
      let checkedCount = value.length;
      check.value = checkedCount === proxyData.value.length;
      indeterminate.value =
        checkedCount > 0 && checkedCount < proxyData.value.length;
    };

    const toggleRowSelection = (row, bool = "check") => {
      var index = proxyData.value.findIndex((item) => item === row);
      if (index !== -1) {
        index = toggleRowSelectionList.value.findIndex((item) => item === row);
        if (bool === "check") {
          if (index !== -1) {
            toggleRowSelectionList.value.splice(index, 1);
          } else {
            toggleRowSelectionList.value.push(row);
          }
        } else if (bool) {
          if (index === -1) {
            toggleRowSelectionList.value.push(row);
          }
        } else {
          if (index !== -1) {
            toggleRowSelectionList.value.splice(index, 1);
          }
        }
      }
      handleCheckedCitiesChange(toggleRowSelectionList.value);
    };

    const toggleAllSelection = () => {
      proxyData.value.forEach((row) => {
        const index = toggleRowSelectionList.value.findIndex(
          (item) => item === row
        );
        if (index !== -1) {
          toggleRowSelectionList.value.splice(index, 1);
        } else {
          toggleRowSelectionList.value.push(row);
        }
      });
      handleCheckedCitiesChange(toggleRowSelectionList.value);
    };

    const clearSelection = () => {
      toggleRowSelectionList.value = [];
      handleCheckedCitiesChange(toggleRowSelectionList.value);
    };

    const clearCurrentRow = () => {
      setHighlightCurrentRowList.value = [];
    };

    const handleScroll = () => {
      const scrollWidth = cTable.value.scrollWidth;
      const scrollLeft = cTable.value.scrollLeft;
      const clientWidth = cTable.value.clientWidth;
      if (scrollLeft === 0) {
        scrollShadow.value = "right";
      } else if (scrollWidth === scrollLeft + clientWidth) {
        scrollShadow.value = "left";
      } else {
        scrollShadow.value = "all";
      }
    };

    const checkAll = (val) => {
      clearSelection();
      if (val) {
        proxyData.value.forEach((item) => {
          toggleRowSelectionList.value.push(item);
        });
      }
      check.value = val;
      indeterminate.value = false;
      ctx.emit("select-all", toggleRowSelectionList.value);
      ctx.emit("select-change", toggleRowSelectionList.value);
    };

    const checkBox = (row) => {
      ctx.emit("select", toggleRowSelectionList.value, row);
      ctx.emit("select-change", toggleRowSelectionList.value);
    };

    const setOrder = (column, order) => {
      console.log(column, order);
      proxyDefaultSort.prop = column.prop;
      if (order === null) {
        if (props.sortOrders) {
          const index = props.sortOrders.findIndex(
            (item) => item === proxyDefaultSort.order
          );
          if (index !== -1) {
            if (index === props.sortOrders.length - 1) {
              proxyDefaultSort.order = props.sortOrders[0];
            } else {
              proxyDefaultSort.order = props.sortOrders[index + 1];
            }
          }
        }
      } else {
        proxyDefaultSort.order = order;
      }
      sort(column);
      ctx.emit("sort-change", column, column.prop, proxyDefaultSort.order);
    };

    const clearSort = () => {
      proxyDefaultSort.prop = null;
      proxyDefaultSort.order = null;
      proxyData.value = [];
      props.data.forEach((row) => {
        proxyData.value.push(row);
      });
    };

    const sort = (column) => {
      if (column.sortable !== "custom") {
        proxyData.value = [];
        props.data.forEach((row) => proxyData.value.push(row));
        if (proxyDefaultSort.order !== null) {
          proxyData.value.sort((a, b) => {
            if (proxyDefaultSort.order === "ascending") {
              return a[proxyDefaultSort.prop] > b[proxyDefaultSort.prop]
                ? 1
                : -1;
            } else if (proxyDefaultSort.order === "descending") {
              return a[proxyDefaultSort.prop] > b[proxyDefaultSort.prop]
                ? -1
                : 1;
            }
          });
        }
      }
    };

    const filterMethod = (prop, value, method) => {
      if (value.length === 0) {
        handleOrClearFilter(prop);
      } else {
        proxyData.value.forEach((item) => {
          item[`${prop}Filter`] = false;
        });
        value.forEach((condition) => {
          proxyData.value.forEach((item) => {
            if (!item[`${prop}Filter`]) {
              item[`${prop}Filter`] = method(item, prop, condition)
                ? true
                : false;
            }
          });
        });
        handleOrClearFilter();
      }
    };

    const clearFilter = (prop) => {
      if (!prop) {
        proxyData.value.forEach((item) => {
          const keys = Object.keys(item);
          keys.forEach((key) => {
            if (key.indexOf("Filter") !== -1) {
              item[key] = true;
            }
          });
          item.rendering = true;
        });
        clearTableFilter.value = [];
      } else if (Array.isArray(prop)) {
        prop.forEach((attr) => {
          handleOrClearFilter(attr);
        });
        clearTableFilter.value = prop;
      } else if (typeof prop === "string") {
        handleOrClearFilter(prop);
        clearTableFilter.value = [];
        clearTableFilter.value.push(prop);
      }
    };

    const handleOrClearFilter = (prop) => {
      proxyData.value.forEach((item) => {
        prop && (item[`${prop}Filter`] = true);
        const list = [];
        const keys = Object.keys(item);
        keys.forEach((key) => {
          if (key.indexOf("Filter") !== -1) {
            list.push(item[key]);
          }
        });
        item.rendering = !list.some((item) => !item && item !== undefined);
      });
    };

    const toggleRowExpansion = (row, expanded = "check") => {
      toggleRowExpansionObj.row = row;
      toggleRowExpansionObj.expanded = expanded;
      toggleRowExpansionObj.reset = !toggleRowExpansionObj.reset;
    };

    const cellEvent = (row, column, cell, event) => {
      switch (event.type) {
        case "mouseenter":
          ctx.emit("cell-mouse-enter", row, column, cell, event);
          break;
        case "mouseleave":
          ctx.emit("cell-mouse-leave", row, column, cell, event);
          break;
        case "click":
          ctx.emit("cell-click", row, column, cell, event);
          break;
        case "dblclick":
          ctx.emit("cell-dblclick", row, column, cell, event);
          break;
        case "contextmenu":
          ctx.emit("cell-contextmenu", row, column, cell, event);
          break;
      }
    };

    const rowEvent = (row, column, cell, event) => {
      switch (event.type) {
        case "mouseenter":
          ctx.emit("row-mouse-enter", row, column, cell, event);
          break;
        case "mouseleave":
          ctx.emit("row-mouse-leave", row, column, cell, event);
          break;
        case "contextmenu":
          ctx.emit("row-contextmenu", row, column, cell, event);
          break;
        case "click":
          ctx.emit("row-click", row, column, cell, event);
          break;
        case "dblclick":
          ctx.emit("row-dblclick", row, column, cell, event);
          break;
      }
    };

    const headerEvent = (column, cell, event) => {
      switch (event.type) {
        case "mouseenter":
          ctx.emit("header-mouse-enter", column, cell, event);
          break;
        case "mouseleave":
          ctx.emit("header-mouse-leave", column, cell, event);
          break;
        case "contextmenu":
          ctx.emit("header-contextmenu", column, cell, event);
          break;
        case "click":
          ctx.emit("header-click", column, cell, event);
          break;
        case "dblclick":
          ctx.emit("header-dblclick", column, cell, event);
          break;
      }
    };

    const filterChange = (filters, prop) => {
      ctx.emit("filter-change", filters, prop);
    };

    const expandChange = (expanded, row) => {
      ctx.emit("expand-change", expanded, row);
    };

    provide("checkAll", checkAll);
    provide("checkBox", checkBox);
    provide("setOrder", setOrder);
    provide("defaultSort", proxyDefaultSort);
    provide("filterMethod", filterMethod);
    provide("clearTableFilter", clearTableFilter);
    provide("cellEvent", cellEvent);
    provide("rowEvent", rowEvent);
    provide("headerEvent", headerEvent);
    provide("filterChange", filterChange);
    provide("expandChange", expandChange);
    provide("toggleRowExpansionObj", toggleRowExpansionObj);

    return {
      clearSort,
      clearFilter,
      proxyData,
      handleScroll,
      cTable,
      scrollShadow,
      setCurrentRow,
      toggleRowSelection,
      toggleAllSelection,
      toggleRowSelectionList,
      setHighlightCurrentRowList,
      handleCheckedCitiesChange,
      toggleRowExpansion,
      clearSelection,
      clearCurrentRow,
      proxyDefaultSort,
      showSummaryObj,
      check,
      indeterminate,
    };
  },
};
</script>

<style lang='less' scoped>
// @import url('../../assets/css/scrollbar.css');
.v-table {
  width: 100%;
  table {
    display: flex;
    flex-direction: column;
    background-color: #fff;
    overflow: auto;
    .radio-group {
      display: block;
    }
    thead {
      position: sticky;
      top: 0;
      z-index: 999;
      background-color: #fff;
    }
  }
  .tableBorder {
    border: 1px solid #ebeef5;
  }
}
</style>