<template>
  <div class="preview">
    <template v-for="(row, i) in rowList">
      <!-- 行 -->
      <div :key="i" :class="['row', `row-${i}`]" :style="getMultipleColStyle(i)">
        <!-- 列 -->
        <template v-for="(col, j) in row.col_list">
          <div :key="j" :class="['col', `col-${i}-${j}`]" :style="computedColStyle(col)">
            <!-- 网格 -->
            <div
              v-for="(grid, k) in col.grid_list"
              :key="`grid-${i}-${j}-${k}`"
              :style="{ gridArea: grid.grid_area, height: grid.height }"
              :class="[!noMoveComponent.includes(grid.type) ? 'grid' : 'no-grid']"
            >
              <template v-for="{ type, cursor, lineClass } in moveLineList">
                <span
                  v-if="!noMoveComponent.includes(grid.type)"
                  :key="`${type}-${i}-${j}-${k}`"
                  class="drag-line"
                  :class="[type, ...lineClass]"
                  :style="{ gridArea: type }"
                  :data-type="type"
                  @mousedown="dragStart($event, { cursor, type: type, i, j, k, id: grid.id })"
                ></span>
              </template>
              <component
                :is="previewComponentList[grid.type]"
                :id="grid.id"
                ref="preview"
                :key="`preview-${grid.id}`"
                :courseware-id="coursewareId"
                :class="[grid.id]"
                :style="{
                  gridArea: 'preview',
                  height: grid.height,
                  overflow: 'hidden',
                }"
              />
            </div>
          </div>
        </template>
      </div>
    </template>
  </div>
</template>

<script>
import { previewComponentList } from '@/views/book/courseware/data/bookType';

export default {
  name: 'PreviewEdit',
  provide() {
    return {
      getDragStatus: () => this.drag.dragging,
    };
  },
  props: {
    rowList: {
      type: Array,
      required: true,
    },
    coursewareId: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      previewComponentList,
      drag: {
        dragging: false,
        i: -1,
        j: -1,
        k: -1,
        startX: 0,
        startY: 0,
        type: '',
        id: '',
      },
      moveLineList: [
        {
          type: 'top',
          cursor: 'ns-resize',
          lineClass: ['drag-line'],
        },
        {
          type: 'left',
          cursor: 'ew-resize',
          lineClass: ['drag-vertical-line'],
        },
        {
          type: 'right',
          cursor: 'ew-resize',
          lineClass: ['drag-vertical-line'],
        },
        {
          type: 'bottom',
          cursor: 'ns-resize',
          lineClass: ['drag-line'],
        },
      ],
      // 不需要移动的组件
      noMoveComponent: ['divider', 'spacing'],
    };
  },
  created() {
    document.addEventListener('mousemove', this.dragMove);
    document.addEventListener('mouseup', this.dragEnd);
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.dragMove);
    document.removeEventListener('mouseup', this.dragEnd);
  },
  methods: {
    getMultipleColStyle(i) {
      let row = this.rowList[i];
      let col = row.col_list;
      if (col.length <= 1) {
        return {
          gridTemplateColumns: '100fr',
        };
      }
      let gridTemplateColumns = row.width_list.join(' ');

      return {
        gridAutoFlow: 'column',
        gridTemplateColumns,
        gridTemplateRows: 'auto',
      };
    },
    /**
     * 分割整数为多个 1的倍数
     * @param {number} num
     * @param {number} parts
     */
    splitInteger(num, parts) {
      let base = Math.floor(num / parts);
      let arr = Array(parts).fill(base);
      let remainder = num - base * parts;
      for (let i = 0; remainder > 0; i = (i + 1) % parts) {
        arr[i] += 1;
        remainder -= 1;
      }
      return arr;
    },
    computedColStyle(col) {
      const grid = col.grid_list;

      let maxCol = 0; // 最大列数
      let rowList = new Map();
      grid.forEach(({ row }) => {
        rowList.set(row, (rowList.get(row) || 0) + 1);
      });
      let curMaxRow = 0; // 当前数量最大 row 的值
      rowList.forEach((value, key) => {
        if (value > maxCol) {
          maxCol = value;
          curMaxRow = key;
        }
      });
      // 计算 grid_template_areas
      let gridTemplateAreas = '';
      let gridArr = [];
      grid.forEach(({ grid_area, row }) => {
        if (!gridArr[row - 1]) {
          gridArr[row - 1] = [];
        }
        if (curMaxRow === row) {
          gridArr[row - 1].push(`${grid_area}`);
        } else {
          let filter = grid.filter((item) => item.row === row);
          let find = filter.findIndex((item) => item.grid_area === grid_area);
          let needNum = maxCol - filter.length; // 需要的数量

          let str = '';
          if (filter.length === 1) {
            str = ` ${grid_area} `.repeat(needNum + 1);
          } else {
            let arr = this.splitInteger(needNum, filter.length);
            str = arr[find] === 0 ? ` ${grid_area} ` : ` ${grid_area} `.repeat(arr[find] + 1);
          }
          gridArr[row - 1].push(`${str}`);
        }
      });
      gridArr.forEach((item) => {
        gridTemplateAreas += `'${item.join(' ')}' `;
      });

      // 计算 grid_template_columns
      let gridTemplateColumns = '';
      let max = { row: 0, num: 0 };
      grid.forEach(({ row }) => {
        // 计算出 row 的哪个值最多
        let len = grid.filter((item) => item.row === row).length;
        if (max.num < len) {
          max.num = len;
          max.row = row;
        }
      });
      grid.forEach((item) => {
        if (item.row === max.row) {
          gridTemplateColumns += `${item.width} `;
        }
      });

      // 计算 grid_template_rows
      let gridTemplateRows = '';
      // 将 grid 按照 row 分组
      let gridMap = new Map();
      grid.forEach((item) => {
        if (!gridMap.has(item.row)) {
          gridMap.set(item.row, []);
        }
        gridMap.get(item.row).push(item.height);
      });
      gridMap.forEach((value) => {
        if (value.length === 1) {
          gridTemplateRows += `${value[0]} `;
        } else {
          let isAllAuto = value.every((item) => item === 'auto'); // 是否全是 auto
          gridTemplateRows += isAllAuto ? 'auto ' : `max(${value.join(', ')}) `;
        }
      });

      return {
        width: col.width,
        gridTemplateAreas,
        gridTemplateColumns,
        gridTemplateRows,
      };
    },
    /**
     * 拖拽开始
     * @param {MouseEvent} event
     * @param {string} cursor
     * @param {string} type
     * @param {string} id
     * @param {number} i
     * @param {number} j
     * @param {number} k
     */
    dragStart(event, { cursor, type, id, i, j, k }) {
      const { clientX, clientY } = event;
      this.drag = {
        dragging: true,
        startX: clientX,
        startY: clientY,
        type,
        id,
        i,
        j,
        k,
      };

      this.bgColor = '#272727';
      document.body.style.cursor = cursor;
    },
    /**
     * 拖拽移动
     * @param {MouseEvent} event
     */
    dragMove(event) {
      if (!this.drag.dragging) return;

      const { clientX, clientY } = event;
      const { i, j, k, id, startX, startY, type } = this.drag;
      const offsetX = clientX - startX;
      const offsetY = clientY - startY;

      let el = this.findChildComponentByKey(`preview-${id}`);
      let { min_height, min_width } = el.data;

      // 获取行的宽度
      const row_width = document.getElementsByClassName(`row-${i}`)[0].getBoundingClientRect().width;
      this.$emit('computedMoveData', {
        i,
        j,
        k,
        offsetX,
        offsetY,
        type,
        id,
        min_width,
        min_height,
        row_width,
      });

      this.drag.startX = clientX;
      this.drag.startY = clientY;

      this.$forceUpdate();
    },
    /**
     * 拖拽结束
     */
    dragEnd() {
      this.drag = {
        dragging: false,
        startX: 0,
        startY: 0,
        type: '',
      };

      this.bgColor = '#ebebeb';
      document.body.style.cursor = 'auto';
    },
    // 获取子组件
    findChildComponentByKey(key) {
      return this.$children.find((child) => child.$vnode.key === key);
    },
  },
};
</script>

<style lang="scss" scoped>
.preview {
  display: flex;
  flex-direction: column;
  row-gap: 12px;

  .row {
    display: grid;
    gap: 16px;

    .col {
      display: grid;
      gap: 16px;
      align-items: flex-start;

      .grid {
        display: grid;
        grid-template:
          'top top top' 3px
          'left preview right' 1fr
          'bottom bottom bottom' 3px / 3px 1fr 3px;
      }

      .drag-line {
        z-index: 2;
        width: 100%;
        height: 6px;
        cursor: ns-resize;
        background: linear-gradient(to bottom, transparent, transparent 40%, #e5e6eb 40%, #e5e6eb 60%, transparent 60%);

        &.bottom {
          background: linear-gradient(
            to bottom,
            transparent,
            transparent 20%,
            #e5e6eb 20%,
            #e5e6eb 50%,
            transparent 50%
          );
        }
      }

      .drag-vertical-line {
        z-index: 2;
        width: 6px;
        height: 100%;
        cursor: ew-resize;
        background: linear-gradient(to right, transparent, transparent 40%, #e5e6eb 40%, #e5e6eb 60%, transparent 60%);

        &.left {
          background: linear-gradient(to left, transparent, transparent 60%, #e5e6eb 60%, #e5e6eb);
        }
      }
    }
  }
}
</style>
