<template>
  <div class="layout-render-layout">
    <VueDraggable
      v-model="pageLayouts"
      :group="{ name: 'page' }"
      class="layout-render-wraper"
      handle=".draggable-handle"
    >
      <div
        v-for="(row, rowIndex) in pageLayouts"
        :key="rowIndex"
        :style="{
          ...getRowLayoutStyle(pageLayouts, rowIndex, row, 0),
          position: 'relative',
          zIndex: zIndex - rowIndex,
        }"
        @mouseenter.stop="() => handleMouseEnter(row)"
      >
        <VueDraggable
          :list="row.cols"
          :group="{ name: `row${rowIndex}` }"
          :gutter="gutter"
          tag="a-row"
          handle=".draggable-handle"
          style="height: 100%"
          type="flex"
        >
          <template v-if="row.cols instanceof Array && row.cols.length">
            <a-col
              v-for="(col, colIndex) in row.cols"
              :key="colIndex"
              :span="col.span"
              :style="{
                height: getColLayoutHeight(row, col),
                zIndex: zIndex - colIndex,
              }"
              @mouseenter.native.stop="() => handleMouseEnter(col)"
            >
              <template v-if="col.rows instanceof Array && col.rows.length">
                <DesignArea2
                  :layouts="col.rows"
                  :components="components"
                  :multiple="multiple"
                  :on-cp-inner-edit="onCpInnerEdit"
                  :on-hide-cp-edit="onHideCpEdit"
                ></DesignArea2>
              </template>
              <GridLayout
                v-else
                class="layout-render-content"
                :components="getComponentsByLayout(col)"
                @addFn="() => handleShowModal(col)"
                @layout-updated-event="(cps) => layoutUpdatedEvent(cps, col)"
                @selected-component="(cps) => handleSelectedComponent(cps, col)"
              />
              <!-- 操作区域 -->
              <div
                class="layout-render-op"
                :style="{
                  ...getLayoutRenderOpStyle(col),
                }"
              >
                <a-button
                  class="op-item"
                  type="primary"
                  icon="plus"
                  :title="$t('ADD')"
                  @click.stop="() => handleShowModal(col)"
                ></a-button>
              </div>
              <!-- 线 -->
              <div
                v-if="colIndex < row.cols.length - 1"
                class="layout-render-line"
                :style="{
                  width: '7px',
                  height: '100%',
                  top: '0',
                  left: 'calc(100% - 3px)',
                  cursor: 'e-resize',
                }"
                @mousedown.stop="
                  (event) =>
                    onMouseDown(event, row.cols, colIndex, 'horizontal')
                "
              >
                <div
                  class="vertical-line"
                  :style="{
                    width: '1px',
                    height: '100%',
                    marginLeft: '3px',
                  }"
                ></div>
              </div>
              <div class="layout-render-grid-area" />
            </a-col>
          </template>
        </VueDraggable>
        <!-- 操作区域 -->
        <div
          class="layout-render-op"
          :style="{
            ...getLayoutRenderOpStyle(row),
          }"
        >
          <a-button
            class="op-item"
            type="primary"
            icon="plus"
            :title="$t('ADD')"
            @click.stop="() => handleShowModal(row)"
          ></a-button>
        </div>
        <!-- 线 -->
        <div
          v-if="rowIndex < pageLayouts.length - 1"
          class="layout-render-line"
          :style="{
            width: '100%',
            height: '7px',
            top: 'calc(100% - 3px)',
            left: '0',
            cursor: 'n-resize',
          }"
          @mousedown.stop="
            (event) => onMouseDown(event, pageLayouts, rowIndex, 'vertical', 0)
          "
        >
          <div
            class="horizontal-line"
            :style="{
              width: '100%',
              height: '1px',
              marginTop: '3px',
            }"
          ></div>
        </div>
        <div class="layout-render-grid-area" />
      </div>
    </VueDraggable>

    <SelectCp
      :key="selectCpKey"
      :visible.sync="modalVisible"
      :multiple="multiple"
      @confirm="handleCpConfirm"
    />
  </div>
</template>

<script>
import { uniq } from "lodash";
import GridLayout from "./DesignGrid.vue";
import SelectCp from "@/components/SelectCp";

export default {
  name: "DesignArea2",
  components: {
    GridLayout,
    SelectCp,
  },
  provide() {
    return {
      handleDelCp: this.handleDelCp,
      handleEditCp: this.handleEditCp,
      multiple: this.multiple,
    };
  },
  props: {
    layouts: {
      type: Array,
      default: () => [],
    },
    components: {
      type: Array,
      default: () => [],
    },
    gutter: {
      type: Number,
      default: 1,
    },
    zIndex: {
      type: Number,
      default: 100,
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    onCpInnerEdit: Function,
    onHideCpEdit: Function,
  },
  data() {
    return {
      pageLayouts: [],
      componentsMap: null,
      // 调整布局大小
      moveLayouts: {
        data: [],
        index: undefined,
        parentNode: null,
        lineNode: null,
        blockNode: null,
      },

      selectCpKey: undefined,

      throughLayoutKey: undefined, // 鼠标滑过的布局key
      modalVisible: false,
    };
  },
  watch: {
    layouts: {
      handler(val) {
        this.pageLayouts = [].concat(val);
      },
      immediate: true,
      deep: true,
    },
    components: {
      handler(val) {
        this.componentsMap = new Map();
        val?.forEach((c) => {
          this.componentsMap.set(
            c.layoutKey,
            c.components.map((e, i) => ({
              x: e.x || 0,
              y: e.y || 0,
              w: e.w || 24,
              h: e.h || 10,
              i: e.id,
              id: e.id,
              url: e.url,
              attrs: e.attrs,
            }))
          );
        });
      },
      immediate: true,
      deep: true,
    },
  },
  methods: {
    getPageData() {
      const tmp = [];
      [...this.componentsMap.entries()].forEach(([key, value]) => {
        value instanceof Array &&
          value.length &&
          tmp.push({
            layoutKey: key,
            components: value,
          });
      });
      return tmp;
    },
    // 设置选中组件 或者 更新布局中的组件位置信息
    handleSelectedComponent(cps, layout) {
      // const cpsList = cps.map((e, i) => ({
      //   x: e.x || 0,
      //   y: e.y || 0,
      //   w: e.w || 24,
      //   h: e.h || 10,
      //   i: e.id,
      //   url: e.url,
      // }));
      // // cps 组件位置信息初始化
      // const tmp = this.components.find((item) => item.layoutKey === layout.key);
      // if (tmp) {
      //   this.$set(tmp, "components", cpsList);
      // } else {
      //   this.components.push({
      //     layoutKey: layout.key,
      //     components: cpsList,
      //   });
      // }
    },
    // 布局下选择组件
    handleCpConfirm(cpKeys, cps) {
      const components = this.componentsMap.get(this.throughLayoutKey) || [];

      const cpsList = cps.map((e, i) => {
        const cp = components.find((item) => item.id === e.id);
        return (
          cp || {
            x: e.x || 0,
            y: e.y || 0,
            w: e.w || 24,
            h: e.h || 10,
            i: e.id,
            id: e.id,
            url: e.componentPath,
            attrs: e.attrs,
          }
        );
      });

      // cps 组件位置信息初始化
      const tmp = this.components.find(
        (item) => item.layoutKey === this.throughLayoutKey
      );
      if (tmp) {
        this.$set(tmp, "components", cpsList);
      } else {
        this.$set(this.components, this.components.length, {
          layoutKey: this.throughLayoutKey,
          components: cpsList,
        });
        // this.components.push({
        //   layoutKey: this.throughLayoutKey,
        //   components: cpsList,
        // });
      }
    },
    // 组件内部操作 删除
    handleDelCp(cp) {
      this.$confirm({
        title: this.$t("DELETE"),
        content: this.$t("DELETE_INFO"),
        okType: "warning",
        okText: this.$t("CONFIRM"),
        cancelText: this.$t("CANCEL"),
        onOk: () => {
          const cpsList = this.componentsMap.get(this.throughLayoutKey) || [];
          const index = cpsList.findIndex((c) => c.id === cp.id);
          index > -1 && cpsList.splice(index, 1);
          // cps 组件位置信息初始化
          const tmp = this.components.find(
            (item) => item.layoutKey === this.throughLayoutKey
          );
          if (tmp) {
            this.$set(tmp, "components", cpsList);
          }
          // 隐藏组件编辑
          this.onHideCpEdit instanceof Function && this.onHideCpEdit(cp);
        },
      });
    },
    // 组件内部操作 编辑
    handleEditCp(cp) {
      if (this.onCpInnerEdit instanceof Function) {
        this.onCpInnerEdit(cp, this.throughLayoutKey);
      }
    },
    // 更新布局中的组件位置信息
    layoutUpdatedEvent(cps, layout) {
      this.componentsMap.set(layout.key, cps);
    },
    // 获取布局下的组件集合
    getComponentsByLayout(layout) {
      return this.componentsMap.get(layout.key) || [];
    },
    // 绑定事件
    bindEvents() {
      document.addEventListener("mousemove", this.onMouseMove, {
        passive: false,
      });
      document.addEventListener("mouseup", this.onMouseUp);
    },
    // 卸载事件
    unbindEvents() {
      document.removeEventListener("mousemove", this.onMouseMove, {
        passive: false,
      });
      document.removeEventListener("mouseup", this.onMouseUp);
    },
    // 鼠标按下事件
    onMouseDown(event, items, index, direction, level) {
      // 父元素
      let parentNode;

      if (event.target?.classList.contains("layout-render-line")) {
        parentNode = event.target?.parentNode?.parentNode;
      } else {
        parentNode = event.target?.parentNode?.parentNode?.parentNode;
      }
      // 相关布局元素
      const currNode = parentNode?.childNodes?.[index];
      const nextNode = parentNode?.childNodes?.[index + 1];
      // 相关布局元素大小
      const currClientRect = currNode?.getBoundingClientRect();
      const nextClientRect = nextNode?.getBoundingClientRect();
      // 线元素
      const lineNode = event.target;
      // 块元素
      const currNodeChildrenLength = currNode?.childNodes?.length;
      const blockNode =
        currNodeChildrenLength &&
        currNode?.childNodes?.[currNodeChildrenLength - 1];

      if (
        parentNode &&
        currNode &&
        nextNode &&
        currClientRect &&
        nextClientRect &&
        lineNode &&
        blockNode
      ) {
        // 设置数据拖拽移动数据
        const currItem = items[index];
        const nextItem = items[index + 1];
        const data = [currItem, nextItem];

        let affectedLayoutSpan = 0;
        let affectedLayoutHeightPercent = 0;
        if (direction === "horizontal") {
          affectedLayoutSpan = currItem.span + nextItem.span;
        } else if (direction === "vertical") {
          if (currItem?.cols instanceof Array && currItem.cols.length) {
            const currHeightPercent = this.getRowLayoutHeight(currItem, level);
            const nextHeightPercent = this.getRowLayoutHeight(nextItem, level);

            affectedLayoutHeightPercent =
              currHeightPercent.includes("%") && nextHeightPercent.includes("%")
                ? parseInt(currHeightPercent, 10) +
                  parseInt(nextHeightPercent, 10)
                : 0;
          } else {
            affectedLayoutHeightPercent =
              parseInt(currItem.height, 10) + parseInt(nextItem.height, 10);
          }
        }

        this.$set(this, "moveLayouts", {
          data, // 布局数据
          direction, // 拖拽方向
          parentNode, // 父元素
          currNode, // 当前布局元素
          nextNode, // 下一个布局元素
          currClientRect, // 当前布局元素
          nextClientRect, // 下一个布局元素
          affectedLayoutSpan,
          affectedLayoutHeightPercent, // 影响布局高度
          lineNode, // 线元素
          blockNode, // 块(分块)元素
        });

        // 按下功能
        blockNode.style.display = "block";
        this.bindEvents();
      }
    },
    // 拖动
    onMouseMove(event) {
      event.preventDefault();
      const {
        data, // 布局数据
        direction, // 拖拽方向
        parentNode, // 父元素
        currNode, // 当前布局元素
        nextNode, // 下一个布局元素
        currClientRect, // 当前布局元素
        nextClientRect, // 下一个布局元素
        affectedLayoutSpan,
        affectedLayoutHeightPercent,
        lineNode,
        blockNode,
      } = this.moveLayouts;
      const currItem = data[0];
      const nextItem = data[1];
      // 拖拽受影响布局的总宽度和高度
      const totalLayoutSpan = Math.round(
        nextItem.layoutSpan + currItem.layoutSpan
      );

      // 布局元素位置和大小
      const {
        left: currX,
        top: currY,
        width: currNodeWidth,
        height: currNodeHeight,
      } = currClientRect;
      const { width: nextNodeWidth, height: nextNodeHeight } = nextClientRect;
      // 相关布局宽高和
      const totalWidth = currNodeWidth + nextNodeWidth;
      const totalHeight = currNodeHeight + nextNodeHeight;

      // 父元素宽高
      const { offsetHeight: parentNodeHeight, offsetWidth: parentNodeWidth } =
        parentNode;

      // 修改布局宽高
      if (direction === "horizontal") {
        nextNode.style.display = "block";
        // 水平移动
        // console.log(
        //   "%c 🍌 水平移动: ",
        //   "font-size:20px;background-color: #F5CE50;color:#fff;"
        // );

        let currWidth = event.pageX - currX;
        currWidth = currWidth < 0 ? 0 : currWidth;
        currWidth = currWidth > totalWidth ? totalWidth : currWidth;
        const nextWidth = totalWidth - currWidth;
        // 布局宽高
        currNode.style.width = `${currWidth}px`;
        nextNode.style.width = `${nextWidth}px`;
        // 拖拽区域线位置
        lineNode.style.left = `${currWidth - 3}px`;

        // 设置分块内容宽高和布局占比
        const blockWidthSpan = Math.round((currWidth / parentNodeWidth) * 24);
        blockNode.style.width = `${(blockWidthSpan / 24) * parentNodeWidth}px`;
        blockNode.style.height = "100%";
        // 设置布局中的 moveLayoutSpan 数据
        this.$set(currItem, "moveSpan", blockWidthSpan);
        this.$set(nextItem, "moveSpan", affectedLayoutSpan - blockWidthSpan);
      } else if (direction === "vertical") {
        // 垂直移动
        // console.log(
        //   "%c 🦐 垂直移动: ",
        //   "font-size:20px;background-color: #6EC1C2;color:#fff;"
        // );

        let currHeight = event.pageY - currY;
        currHeight = currHeight < 0 ? 0 : currHeight; // 判断最小
        currHeight = currHeight > totalHeight ? totalHeight : currHeight; // 判断最大
        const nextHeight = totalHeight - currHeight;
        // 布局宽高
        const currCols = currItem?.cols;
        const nextCols = nextItem?.cols;
        if (currCols instanceof Array && currCols.length) {
          currCols.forEach((colTmp) => {
            this.$set(colTmp, "moveHeight", `${currHeight}px`);
          });
        } else {
          this.$set(currItem, "moveHeight", `${currHeight}px`);
        }
        if (nextCols instanceof Array && nextCols.length) {
          nextCols.forEach((colTmp) => {
            this.$set(colTmp, "moveHeight", `${nextHeight}px`);
          });
        } else {
          this.$set(nextItem, "moveHeight", `${nextHeight}px`);
        }
        // 拖拽区域线位置
        lineNode.style.top = `${currHeight - 3}px`;
        // 设置分块内容宽高和布局占比
        const blockHeightPercent = Math.round(
          (currHeight / parentNodeHeight) * 100
        );
        const nextHeightPercent = Math.round(
          affectedLayoutHeightPercent - blockHeightPercent
        );

        blockNode.style.width = "100%";
        blockNode.style.height = `${
          (blockHeightPercent / 100) * parentNodeHeight
        }px`;
        // 设置布局中的 moveLayoutHeight 数据
        if (currCols instanceof Array && currCols.length) {
          currCols.forEach((colTmp) => {
            this.$set(colTmp, "moveHeight", `${blockHeightPercent}%`);
          });
        } else {
          this.$set(currItem, "moveHeight", `${blockHeightPercent}%`);
        }
        if (nextCols instanceof Array && nextCols.length) {
          nextCols.forEach((colTmp) => {
            this.$set(colTmp, "moveHeight", `${nextHeightPercent}%`);
          });
        } else {
          this.$set(nextItem, "moveHeight", `${nextHeightPercent}%`);
        }
      }
    },
    // 放开鼠标
    onMouseUp() {
      const { data, direction, currNode, nextNode, lineNode, blockNode } =
        this.moveLayouts;
      if (direction === "vertical") {
        data.forEach((item) => {
          if (item?.cols instanceof Array && item.cols.length) {
            item.cols.forEach((colTmp) => {
              // eslint-disable-next-line no-param-reassign
              colTmp.height = colTmp.moveHeight;
              // eslint-disable-next-line no-param-reassign
              colTmp.moveHeight = undefined;
            });
          } else {
            // eslint-disable-next-line no-param-reassign
            item.height = item.moveHeight;
            // eslint-disable-next-line no-param-reassign
            item.moveHeight = undefined;
          }
        });
      } else if (direction === "horizontal") {
        currNode.style.width = "";
        nextNode.style.width = "";
        lineNode.style.left = "calc(100% - 3px)";
        data.forEach((item) => {
          // eslint-disable-next-line no-param-reassign
          item.span = item.moveSpan;
          // eslint-disable-next-line no-param-reassign
          item.moveSpan = undefined;
        });
      } else {
        // 将move的布局数据转给普通数据
        data.forEach((item, index) => {
          if (item.moveLayoutHeight !== undefined) {
            this.$set(
              this.moveLayouts.data[index],
              "layoutHeight",
              item.moveLayoutHeight
            );
            lineNode.style.top = "calc(100% - 3px)";
          }
          if (item.moveLayoutSpan !== undefined) {
            this.$set(
              this.moveLayouts.data[index],
              "layoutSpan",
              item.moveLayoutSpan
            );
            currNode.style.width = "";
            nextNode.style.width = "";
            lineNode.style.left = "calc(100% - 3px)";
          }
        });
      }

      blockNode.style.display = "none";
      this.unbindEvents();
      setTimeout(() => {
        this.unbindEvents();
      }, 100);
    },
    getColHeight(col) {
      if (col.moveHeight) {
        return col.moveHeight;
      }
      if (col.height) {
        return col.height;
      }
      if (col.rows instanceof Array && col.rows.length) {
        const height = col.rows.reduce((itemPre, item) => {
          const itemHeight = item.cols.reduce((ePre, e) => {
            return ePre + parseInt(e.height, 10);
          }, 0);
          return itemPre + itemHeight;
        }, 0);

        return `${height}%`;
      }
      return "100%";
    },
    getRowLayoutHeight(row, level) {
      if (row.cols instanceof Array && row.cols.length) {
        let isPercent = true;
        const colTotalHeight = row.cols.reduce((prev, item, index) => {
          // 判断是百分比还是像素
          const height = this.getColHeight(item);
          if (index === 0 && !height.includes("%")) {
            isPercent = false;
          }
          return prev + parseInt(height, 10);
        }, 0);

        if (isPercent) {
          return level === 0
            ? `${colTotalHeight / row.cols.length}%`
            : `${colTotalHeight}%`;
        }
        return `${colTotalHeight / row.cols.length}px`;
      }
      return "100%";
    },
    getRowLayoutStyle(rows, index, row, level) {
      const fn = (len, i, height, gutter) => {
        const halfGutter = gutter / 2;
        const tmp = {};
        if (len > 1) {
          if (i === 0) {
            tmp.height = `calc(${height} - ${halfGutter}px)`;
          } else if (i === len - 1) {
            tmp.height = `calc(${height} - ${halfGutter}px)`;
            tmp.marginTop = `${gutter}px`;
          } else {
            tmp.marginTop = `${gutter}px`;
            tmp.height = `calc(${height} - ${gutter}px)`;
          }
        } else {
          tmp.height = height;
        }
        return tmp;
      };

      let rowHeight = "100%";
      if (row.cols instanceof Array && row.cols.length) {
        let isPercent = true;
        const colTotalHeight = row.cols.reduce((prev, item, i) => {
          // 判断是百分比还是像素
          const height = this.getColHeight(item);
          if (i === 0 && !height.includes("%")) {
            isPercent = false;
          }
          return prev + parseInt(height, 10);
        }, 0);

        if (isPercent) {
          if (level === 0) {
            rowHeight = `${colTotalHeight / row.cols.length}%`;
          } else {
            rowHeight = `${colTotalHeight}%`;
          }
        } else {
          rowHeight = `${colTotalHeight / row.cols.length}px`;
        }
      }

      return fn(rows.length, index, rowHeight, this.gutter);
    },
    getColLayoutHeight(row, col) {
      const tmp = uniq(row.cols.map((item) => this.getColHeight(item)));

      if (row.cols.length === 1) {
        return "100%";
      }

      if (tmp.length === row.cols.length) {
        return col.height;
      }

      return "100%";
    },
    getLayoutRenderOpStyle(item) {
      const tmp = {};
      let key;
      if (item.cols?.length === 1) {
        key = item.cols[0].key;
      } else {
        key = item.key;
      }
      if (this.throughLayoutKey && this.throughLayoutKey === key) {
        tmp.opacity = "1";
      }
      return tmp;
    },
    handleMouseEnter(item) {
      let key;
      if (item.cols?.length === 1) {
        key = item.cols[0].key;
      } else {
        key = item.key;
      }
      if (this.throughLayoutKey !== key) {
        this.throughLayoutKey = key;
      }
    },
    handleShowModal() {
      this.modalVisible = true;
      if (this.selectCpKey !== this.throughLayoutKey) {
        this.selectCpKey = this.throughLayoutKey;
      }
      this.onHideCpEdit instanceof Function && this.onHideCpEdit();
    },
    handleSelectComponent() {
      this.modalVisible = false;
    },
  },
};
</script>

<style lang="less" scoped>
.layout-render {
  &-layout,
  &-wraper,
  &-content {
    position: relative;
    width: 100%;
    height: 100%;
  }
  &-line {
    position: absolute;
    z-index: 20;
    .vertical-line {
      background-image: linear-gradient(
        to bottom,
        #a4a4a4 0%,
        #a4a4a4 50%,
        transparent 50%
      );
      background-size: 1px 12px;
      background-repeat: repeat-y;
    }
    .horizontal-line {
      background-image: linear-gradient(
        to right,
        #a4a4a4 0%,
        #a4a4a4 50%,
        transparent 50%
      );
      background-size: 12px 1px;
      background-repeat: repeat-x;
    }
  }
  &-grid-area {
    position: absolute;
    left: 0;
    top: 0;
    background: red;
    opacity: 0.12;
    display: none;
    transition-duration: 0.1s;
  }
  &-op {
    position: absolute;
    top: 1px;
    left: 1.5px;
    z-index: 10;
    background: #fff;
    opacity: 0;
    transition: all 0.5s;
    .op-item + .op-item {
      margin-left: 4px;
    }
    /deep/ .op-item:first-of-type {
      border-top-left-radius: 0;
      border-top-right-radius: 0;
      border-bottom-left-radius: 0;
    }
  }
}
</style>
