<template>
  <div
    ref="boxRef"
    class="w-full h-full"
    @click.stop="
      () => {
        curTdElement = {};
        emit('click');
      }
    "
  >
    <div
      v-if="element.options.showLabel"
      :style="{
        borderColor: element.options.borderColor,
        width: tableWidth + 'px',
        lineHeight: '40px',
        borderStyle: 'solid',
        borderWidth: '1px',
        paddingLeft: '16px',
        background: '#fff',
        borderRadius: '16px 16px 0 0',
      }"
      >{{ element.options.label }}</div
    >
    <div
      ref="conRef"
      :style="{ height: `calc(100% - 40px)`, overflowY: 'auto', overflowX: 'hidden' }"
      @scroll="handleScroll"
    >
      <table
        :border="element.options.borderWidth"
        cellspacing="0"
        cellpadding="0"
        :style="{
          borderColor: element.options.borderColor,
          width: tableWidth + 'px',
          tableLayout: 'fixed',
          height: '42px',
        }"
      >
        <thead>
          <tr style="background-color: #fff">
            <th
              v-for="(tdElement, tdIndex) of tdMax"
              :key="tdElement"
              style="border: 0"
              @click.stop="handleTh(tdIndex)"
            >
              <div
                :style="{
                  height: 0,
                  width: widthArr[tdIndex] + 'px' || '',
                }"
              ></div>
            </th>
          </tr>
        </thead>
        <tbody>
          <tr
            v-for="(row, rowIndex) of element.layout"
            :key="rowIndex"
            style="background-color: #fff"
          >
            <td
              v-for="(tdElement, tdIndex) of row.list"
              :key="element.key + '-' + rowIndex + '-' + tdIndex"
              :colspan="tdElement.colspan"
              :rowspan="tdElement.rowspan"
              :data-position="tdElement.position"
              :style="{
                borderColor: element.options.borderColor,
                borderWidth: element.options.borderWidth + 'px',
                borderStyle: 'solid',
                position: 'relative',
                backgroundColor:
                  curTdIndex == element.key + '-' + rowIndex + '-' + tdIndex ||
                  curThIndex == tdElement.position[1]
                    ? '#e6f7ff'
                    : '#fff',
                verticalAlign: 'top',
              }"
              @click.stop="handleTd(tdElement, element.key + '-' + rowIndex + '-' + tdIndex)"
            >
              <slot name="tdElement" :tdElement="tdElement" :key="tableKey"></slot>
              <a-dropdown
                v-if="curTdIndex == element.key + '-' + rowIndex + '-' + tdIndex && !preview"
                :trigger="['click']"
                @click="HandleSetting(element.layout, rowIndex, tdIndex, row.list)"
              >
                <SettingOutlined style="position: absolute; right: 10px; bottom: 10px" />
                <template #overlay>
                  <a-menu>
                    <a-menu-item>
                      <a
                        href="javascript:;"
                        @click="addLeftCol(element.layout, row.list, tdIndex, rowIndex)"
                        >左插入列</a
                      >
                    </a-menu-item>
                    <a-menu-item>
                      <a href="javascript:;" @click="addRightCol(element.layout, row.list, tdIndex)"
                        >右插入列</a
                      >
                    </a-menu-item>
                    <a-menu-item>
                      <a href="javascript:;" @click="addTopRow(element.layout, rowIndex, tdIndex)"
                        >上插入行</a
                      >
                    </a-menu-item>
                    <a-menu-item>
                      <a href="javascript:;" @click="addDownRow(element.layout, rowIndex)"
                        >下插入行</a
                      >
                    </a-menu-item>
                    <a-menu-divider />
                    <a-menu-item :disabled="disabledObj.right">
                      <a href="javascript:;" @click="mergeCol(row.list, tdIndex)">向右合并</a>
                    </a-menu-item>
                    <a-menu-item :disabled="disabledObj.down">
                      <a
                        href="javascript:;"
                        @click="mergeRow(element.layout, row.list, rowIndex, tdIndex)"
                        >向下合并</a
                      >
                    </a-menu-item>
                    <a-menu-divider />
                    <a-menu-item :disabled="!tdElement.colspan || tdElement.colspan < 2">
                      <a href="javascript:;" @click="splitCol(row.list, rowIndex, tdIndex)"
                        >拆分成列</a
                      >
                    </a-menu-item>
                    <a-menu-item :disabled="!tdElement.rowspan || tdElement.rowspan < 2">
                      <a
                        href="javascript:;"
                        @click="splitRow(element.layout, row.list, rowIndex, tdIndex)"
                        >拆分成行</a
                      >
                    </a-menu-item>
                    <a-menu-divider />
                    <a-menu-item :disabled="row.list.length == 1 || tdElement.colspan >= 2">
                      <a href="javascript:;" @click="deleteCol(element.layout, row.list, tdIndex)"
                        >删除当前列</a
                      >
                    </a-menu-item>
                    <a-menu-item :disabled="disabledObj.delRow">
                      <a href="javascript:;" @click="deleteRow(element.layout, row.list, rowIndex)"
                        >删除当前行</a
                      >
                    </a-menu-item>
                  </a-menu>
                </template>
              </a-dropdown>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>
<script lang="ts" setup>
  import { SettingOutlined } from '@ant-design/icons-vue';
  import { cloneDeep, sortBy } from 'lodash-es';

  import { inject, onMounted, ref, watch, onUnmounted, Ref, nextTick } from 'vue';
  import { TableCell, TableTh } from '/@/components/Designer/src/types';

  const props = defineProps({
    element: {
      type: Object,
      required: true,
    },
    level: Number,
  });
  const emit = defineEmits(['click', 'conScroll']);
  const current = inject<Ref<number>>('current');
  let preview = inject('preview') as boolean;
  const disabledObj = ref({
    right: false,
    down: false,
    delRow: false,
  });
  const tdMax = ref(0);
  const boxRef = ref();
  let curTdElement = inject<Ref<TableCell>>('tableCell', ref({}));
  let curThElement = inject<Ref<TableTh>>('tableTh', ref({}));
  const widthArr = ref<number[]>([]);
  const tableWidth = ref(0);
  const curThIndex = ref();
  const curTdIndex = inject<Ref<string>>('tdIndex', ref(''));
  const tableKey = ref(0);
  const conRef = ref();
  onMounted(() => {
    tdMax.value = 0;
    props.element.layout[0].list.forEach((o) => {
      if (o.colspan >= 2) {
        tdMax.value = tdMax.value + o.colspan;
      } else {
        tdMax.value += 1;
      }
    });
    compulateWidth();
    window.onresize = () => {
      compulateWidth();
    };
    setTimeout(() => {
      tableKey.value++;
    }, 20);
  });
  onUnmounted(() => {
    window.onresize = null;
  });
  watch(
    () => current?.value,
    (val) => {
      if (val === 1) {
        compulateWidth();
      }
    },
  );
  watch(
    () => props.element.layout,
    async (val) => {
      tdMax.value = 0;
      val[0].list.forEach((o) => {
        if (o.colspan >= 2) {
          tdMax.value = tdMax.value + o.colspan;
        } else {
          tdMax.value += 1;
        }
      });

      compulateWidth();
    },
    { deep: true },
  );
  function compulateWidth() {
    nextTick(() => {
      let oldWidth = 0;
      let thCount = 0;
      let newwidth = 0;
      let hasWidth = {};

      for (let j = 0; j < tdMax.value; j++) {
        hasWidth[j] = null;
      }
      for (let i = 0; i < props.element.layout.length; i++) {
        for (let k = 0; k < props.element.layout[i].list.length; k++) {
          let obj = props.element.layout[i].list[k];
          if (Object.keys(hasWidth).includes(String(obj.position[1])) && obj.width) {
            hasWidth[obj.position[1]] = obj;
            oldWidth += Number(obj.width);
          }
        }
      }
      for (let i in hasWidth) {
        if (hasWidth[i]) {
          thCount += 1;
        }
      }

      if (tdMax.value - thCount > 0) {
        tableWidth.value = boxRef.value.offsetWidth;
        newwidth = Math.floor(
          (boxRef.value.offsetWidth - oldWidth - tdMax.value * 2) / (tdMax.value - thCount),
        );
      } else {
        tableWidth.value = oldWidth;
      }
      widthArr.value = [];
      for (let i in hasWidth) {
        if (hasWidth[i]) {
          widthArr.value.push(hasWidth[i].width);
        } else {
          widthArr.value.push(newwidth);
        }
      }
    });
  }
  function handleTh(tdIndex) {
    if (preview) return;
    for (let i = 0; i < props.element.layout.length; i++) {
      curThElement.value = {};
      for (let j = 0; j < props.element.layout[i].list.length; j++) {
        let obj = props.element.layout[i].list[j];
        if (obj.position[1] == tdIndex) {
          curThElement.value = obj;
          curThIndex.value = tdIndex;
          break;
        }
      }
      if (curThElement.value && Object.keys(curThElement.value).length > 0) break;
    }
    curTdElement.value = {};
    curTdIndex.value = '';
    emit('click');
  }
  function handleTd(el, tdIndex) {
    if (preview) return;
    curTdElement.value = el;
    curThElement.value = {};
    curThIndex.value = null;
    curTdIndex.value = tdIndex;
    emit('click');
  }

  function addLeftCol(layout, list, idx, rowIndex) {
    if (idx == 0) {
      layout.forEach((o, i) => {
        o.list.splice(0, 0, {
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          position: [i, 0],
        });
        for (let j = 1; j < o.list.length; j++) {
          o.list[j].position[1] += 1;
        }
      });
    } else {
      addRightCol(layout, list, idx - 1);
    }
    curTdIndex.value = props.element.key + '' + rowIndex + '-' + (idx + 1);
  }
  function addRightCol(layout, list, idx) {
    let curRowBox = 0;
    if (list[idx].colspan >= 2) {
      curRowBox = list[idx].position[1] + list[idx].colspan;
    } else {
      curRowBox = list[idx].position[1] + 1;
    } //当前格子之前的格子数

    let colObj: any = {}; //其他行格子数之和最接近curRowBox的格子所在索引
    let colArr: number[] = [];
    layout.forEach((o, i) => {
      let span = 0;
      for (let j = 0; j < o.list.length; j++) {
        if (o.list[j].colspan >= 2) {
          span = o.list[j].position[1] + o.list[j].colspan;
        } else {
          span = o.list[j].position[1] + 1;
        }

        if (span == curRowBox) {
          if (o.list[j].colspan >= 2) {
            span = o.list[j].position[1] + o.list[j].colspan;
          }
          colObj[i] = { index: j, num: span };
          break;
        } else if (span > curRowBox) {
          if (j - 1 < 0) {
            colObj[i] = { index: j, num: span };
          } else {
            if (o.list[j - 1].colspan >= 2) {
              span = o.list[j - 1].position[1] + o.list[j - 1].colspan;
            } else {
              span = o.list[j].position[1] > curRowBox ? curRowBox : o.list[j].position[1];
            }
            colObj[i] = { index: j - 1, num: span };
          }
          break;
        }
      }
      colArr.push(span);
    });

    let lastObj = colObj;

    layout.forEach((o, i) => {
      let index = 0;
      let pcol = 0;

      index = lastObj[i].index + 1;
      pcol = lastObj[i].num;

      o.list.splice(index, 0, {
        width: '',
        height: '',
        class: 'tableLayoutTd',
        children: [],
        position: [i, pcol],
      });
      o.list.forEach((k, j) => {
        if (j > index) {
          k.position[1] += 1;
        }
      });
    });
  }

  function addTopRow(layout, idx, tdIndex) {
    if (idx == 0) {
      let lastRow: any = [];
      for (let j = 0; j < tdMax.value; j++) {
        lastRow.push({
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          position: [0, j],
        });
      }
      layout.splice(0, 0, { list: lastRow });
      layout.forEach((o, i) => {
        o.list.forEach((k) => {
          k.position[0] = i;
        });
      });
    } else {
      addDownRow(layout, idx - 1);
    }
    curTdIndex.value =
      props.element.key + '' + idx + 1 + '-' + layout[idx].list[tdIndex].position[1];
  }
  function addDownRow(layout, idx) {
    if (layout.length == idx + 1) {
      let lastRow: any = [];
      for (let j = 0; j < tdMax.value; j++) {
        lastRow.push({
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          position: [layout.length, j],
        });
      }
      layout.push({ list: lastRow });
    } else if (layout.length > idx + 1) {
      let newCol = {};
      let newRow = {};
      let arr: number[] = [];
      for (let i = 0; i < tdMax.value; i++) {
        let obj = layout[idx].list.find((o) => {
          return o.position[1] == i;
        });
        if (obj) {
          arr.push(i);
          let item = obj;
          if (item.rowspan >= 2) {
            if (!newRow[idx + item.rowspan]) {
              newRow[idx + item.rowspan] = { list: {} };
            }
            if (item.colspan >= 2) {
              for (let j = i; j < i + item.colspan; j++) {
                arr.push(j);
                newRow[idx + item.rowspan].list[j] = {
                  width: '',
                  height: '',
                  class: 'tableLayoutTd',
                  children: [],
                  position: [idx + item.rowspan, j],
                };
              }
            } else {
              newRow[idx + item.rowspan].list[i] = {
                width: '',
                height: '',
                class: 'tableLayoutTd',
                children: [],
                position: [idx + item.rowspan, i],
              };
            }
          } else {
            if (!newCol[idx + 1]) {
              newCol[idx + 1] = { list: [] };
            }
            if (item.colspan >= 2) {
              for (let j = i; j < i + item.colspan; j++) {
                arr.push(j);
                newCol[idx + 1].list.push({
                  width: '',
                  height: '',
                  class: 'tableLayoutTd',
                  children: [],
                  position: [idx + 1, j],
                });
              }
            } else {
              newCol[idx + 1].list.push({
                width: '',
                height: '',
                class: 'tableLayoutTd',
                children: [],
                position: [idx + 1, i],
              });
            }
          }
        } else if (!arr.includes(i)) {
          //往上找rowspan
          findRowSpan(layout, idx - 1, i, newRow, arr);
        }
      }
      layout.splice(idx + 1, 0, { list: newCol[idx + 1].list });
      for (let j = idx + 2; j < layout.length; j++) {
        for (let k = 0; k < layout[j].list.length; k++) {
          layout[j].list[k].position[0] += 1;
        }
      }

      for (let i in newRow) {
        for (let k in newRow[i].list) {
          layout[i].list.splice(k, 0, newRow[i].list[k]);
          layout[i].list = sortBy(layout[i].list, function (o) {
            return o.position[1];
          });
        }
      }
    }
  }
  function findRowSpan(layout, idx, i, newRow, arr) {
    if (idx < 0) return;
    let obj = layout[idx].list.find((o) => {
      return o.position[1] == i;
    });
    if (obj) {
      arr.push(i);
      let item = obj;
      if (item.rowspan >= 2) {
        if (!newRow[idx + item.rowspan]) {
          newRow[idx + item.rowspan] = { list: {} };
        }
        if (item.colspan >= 2) {
          for (let j = i; j < i + item.colspan; j++) {
            arr.push(j);
            newRow[idx + item.rowspan].list[j] = {
              width: '',
              height: '',
              class: 'tableLayoutTd',
              children: [],
              position: [idx + item.rowspan, j],
            };
          }
        } else {
          newRow[idx + item.rowspan].list[i] = {
            width: '',
            height: '',
            class: 'tableLayoutTd',
            children: [],
            position: [idx + item.rowspan, i],
          };
        }
      }
    } else {
      findRowSpan(layout, idx - 1, i, newRow, arr);
    }
  }

  function mergeCol(list, idx) {
    let colspan =
      list[idx].colspan >= 2
        ? list[idx + 1].colspan >= 2
          ? list[idx].colspan + list[idx + 1].colspan
          : list[idx].colspan + 1
        : list[idx + 1].colspan >= 2
        ? 1 + list[idx + 1].colspan
        : 2;
    list[idx].children = list[idx].children.concat(list[idx + 1].children);
    list[idx].colspan = colspan;
    list.splice(idx + 1, 1);
  }

  function mergeRow(layout, list, rowIndex, tdIndex) {
    let rowx = list[tdIndex].rowspan >= 2 ? list[tdIndex].rowspan : 1;
    let tdx = 0;
    layout[rowIndex + rowx].list.forEach((o, i) => {
      if (o.position[1] == list[tdIndex].position[1]) {
        tdx = i;
        list[tdIndex].children = list[tdIndex].children.concat(
          layout[rowIndex + rowx].list[i].children,
        );
        list[tdIndex].rowspan = rowx + (o.rowspan ? o.rowspan : 1);
      }
    });

    layout[rowIndex + rowx].list.splice(tdx, 1);
  }
  function deleteCol(layout, list, tdIndex) {
    let pcol = list[tdIndex].position[1];

    let colObj = findPcol(layout, pcol);

    for (let i in colObj) {
      let colspan = colObj[i].list[colObj[i].tdIdx].colspan;
      if (colspan >= 2) {
        let Tlist = colObj[i].list;
        let TtdIndex = colObj[i].tdIdx;
        let TrowIndex = Number(i);

        Tlist.splice(TtdIndex + 1, 0, {
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          rowspan: Tlist[TtdIndex].rowspan,
          position: [TrowIndex, pcol],
        });

        Tlist[TtdIndex].colspan = Tlist[TtdIndex].colspan - 1;
      }
    }

    for (let i = 0; i < layout.length; i++) {
      let tdidx = layout[i].list.findIndex((o) => {
        return o.position[1] == pcol;
      });

      if (tdidx >= 0) {
        layout[i].list.splice(tdidx, 1);
      }
      for (let j = 0; j < layout[i].list.length; j++) {
        if (layout[i].list[j].position[1] > pcol) {
          let clone = cloneDeep(layout[i].list[j]);
          clone.position[1] -= 1;
          layout[i].list.splice(j, 1, clone);
        }
      }
    }
  }
  function findPcol(layout, pcol) {
    let hasPcol = {}; //最接近pcol的格子
    for (let i = 0; i < layout.length; i++) {
      let arr: number[] = [];
      for (let j = 0; j < layout[i].list.length; j++) {
        if (layout[i].list[j].position[1] <= pcol) {
          arr.push(j);
        }
      }
      if (arr.length > 0) {
        hasPcol[i] = { tdIdx: arr[arr.length - 1], list: layout[i].list };
      }
    }
    return hasPcol;
  }

  function deleteRow(layout, list, rowIndex) {
    let arr: number[] = [];
    for (let i = 0; i < list.length; i++) {
      let item = list[i];
      if (item.rowspan >= 2) {
        layout[rowIndex + 1].list.splice(i, 0, {
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          rowspan: item.rowspan - 1,
          colspan: item.colspan,
          position: [rowIndex, item.position[1]],
        });
        if (item.colspan >= 2) {
          for (let j = 0; j < item.colspan; j++) {
            arr.push(item.position[1] + j);
          }
        } else {
          arr.push(item.position[1]);
        }
      } else if (item.colspan >= 2) {
        for (let j = 0; j < item.colspan; j++) {
          arr.push(item.position[1] + j);
        }
      } else {
        for (let i = 0; i < tdMax.value; i++) {
          if (item.position[1] == i) {
            arr.push(item.position[1]);
          }
        }
      }
    }

    for (let i = 0; i < tdMax.value; i++) {
      if (!arr.includes(i)) {
        //往上找rowspan
        findTopRow(layout, rowIndex - 1, i, arr);
      }
    }

    layout.splice(rowIndex, 1);
    layout.forEach((o, i) => {
      o.list.forEach((k) => {
        k.position[0] = i;
      });
    });
    layout[rowIndex].list = sortBy(layout[rowIndex].list, function (o) {
      return o.position[1];
    });
  }
  function findTopRow(layout, idx, i, arr) {
    if (idx < 0) return;
    let index = layout[idx].list.findIndex((o) => {
      return o.position[1] == i;
    });

    if (index >= 0) {
      let item = layout[idx].list[index];
      if (item.rowspan >= 2) {
        item.rowspan -= 1;
        layout[idx].list.splice(index, 1, item);
        if (item.colspan >= 2) {
          for (let j = 0; j < item.colspan; j++) {
            arr.push(item.position[1] + j);
          }
        } else {
          arr.push(item.position[1]);
        }
      }
    } else {
      findTopRow(layout, idx - 1, i, arr);
    }
  }
  function splitCol(list, rowIndex, tdIndex) {
    if (list[tdIndex].colspan >= 2) {
      for (let j = tdIndex + 1; j < list[tdIndex].colspan + tdIndex; j++) {
        list.splice(j, 0, {
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          rowspan: list[tdIndex].rowspan,
          position: [rowIndex, j],
        });
      }
      list[tdIndex].colspan = '';
    }
  }
  function splitRow(layout, list, rowIndex, tdIndex) {
    if (list[tdIndex].rowspan >= 2) {
      for (let j = rowIndex + 1; j < list[tdIndex].rowspan + rowIndex; j++) {
        let index = layout[j].list.findIndex((k) => {
          return k.position[1] > list[tdIndex].position[1];
        });

        layout[j].list.splice(index, 0, {
          width: '',
          height: '',
          class: 'tableLayoutTd',
          children: [],
          colspan: list[tdIndex].colspan,
          position: [j, list[tdIndex].position[1]],
        });
      }
      list[tdIndex].rowspan = '';
    }
  }
  function mergeDownDisabled(layout, rowIndex, tdIndex, list) {
    let flag = false;
    let rowx = list[tdIndex].rowspan >= 2 ? list[tdIndex].rowspan : 1;
    // 下一行合并列相同的就可以合并
    if (layout[rowIndex + rowx]) {
      let obj = layout[rowIndex + rowx].list.find((o) => {
        return o.position[1] == list[tdIndex].position[1];
      });
      if (obj) {
        if (list[tdIndex].colspan && list[tdIndex].colspan == obj.colspan) {
          flag = false;
        } else if (
          (!list[tdIndex].colspan || list[tdIndex].colspan < 2) &&
          (!obj.colspan || obj.colspan < 2)
        ) {
          flag = false;
        } else {
          flag = true;
        }
      } else {
        flag = true;
      }
    } else {
      flag = true;
    }

    return flag;
  }

  function mergeRightDisabled(tdIndex, list) {
    let flag = false;
    if (tdIndex == list.length - 1) {
      flag = true;
    } else if (list[tdIndex].rowspan >= 2) {
      if (list[tdIndex + 1]?.rowspan >= 2) {
        if (list[tdIndex + 1].rowspan == list[tdIndex].rowspan) {
          flag = false;
        } else {
          flag = true;
        }
      } else {
        flag = true;
      }
    } else if (!list[tdIndex].rowspan || list[tdIndex].rowspan < 2) {
      if (list[tdIndex + 1]?.rowspan >= 2) {
        flag = true;
      } else {
        flag = false;
        if (list[tdIndex + 1].position[1] > list[tdIndex].position[1] + 1) {
          flag = true;
          if (list[tdIndex].colspan >= 2) {
            flag = false;
          }
        }
      }
    } else {
      flag = false;
    }
    return flag;
  }

  function delRowDiasbled(layout) {
    let count = 0;
    layout.forEach((o) => {
      if (o.list.length > 0) {
        count += 1;
      }
    });
    return count == 1 ? true : false;
  }

  function HandleSetting(layout, rowIndex, tdIndex, list) {
    disabledObj.value.down = mergeDownDisabled(layout, rowIndex, tdIndex, list);
    disabledObj.value.right = mergeRightDisabled(tdIndex, list);
    disabledObj.value.delRow = delRowDiasbled(layout);
  }
  function handleScroll() {
    if (conRef.value) {
      const { scrollTop, scrollHeight, clientHeight } = conRef.value;
      if (scrollTop + clientHeight >= scrollHeight) {
        // 已经滚动到底部

        emit('conScroll', 'bottom');
      } else if (scrollTop === 0) {
        emit('conScroll', 'top');
      } else if (scrollTop + clientHeight < scrollHeight) {
        emit('conScroll', 'center');
      }
    }
  }
</script>
