<template>
  <div
    ref="containerRef"
    :class="{ 'workplace-container': true, 'static-workplace-container': isStatic }"
  >
    <h-button @click="onClick">点我</h-button>
    <div class="workplace-panel-cards-container">
      <template v-for="card in cardList" :key="card.uid">
        <Teleport :to="`.${card.selector}`">
          <workplaceCard
            :config="card.config"
            @maximize="maximize"
            @normalize="normalize"
            @minimize="minimize"
            @close="close"
          ></workplaceCard>
        </Teleport>
      </template>
    </div>

    <div :class="['workplace-instance', `workplace-instance-${workplaceId}`]"></div>

    <div class="mini-cardView-container" v-if="miniCardList.length">
      <div class="mini-cardView-list">
        <div v-for="card in miniCardList" class="mini-cardView-sign" @click="recover(card.config)">
          <h-tooltip>
            <template #title>{{ card.config.name }}</template>
            <HIcon name="hoteam-icon-index-table" size="20"></HIcon>
          </h-tooltip>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted, onBeforeUnmount } from 'vue';
import { GridStack, GridStackEngine } from 'gridstack';
import type { GridStackNode, GridItemHTMLElement } from 'gridstack';
import 'gridstack/dist/gridstack.min.css';
import { merge, throttle, xor } from 'lodash-es';
import bus from '@/utils/bus';
import './workplaceUnit.less';
import workplaceCard from './workplaceCard.vue';
import { message } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';

export default defineComponent({
  name: 'workplaceUnit',
  components: {
    workplaceCard,
  },
  props: {
    workplaceConfig: {
      type: Object,
      default: () => {},
    },
  },

  setup(props) {
    const containerRef = ref();
    const workplaceId = ref(props.workplaceConfig?.key || Math.floor(Math.random() * (10000 - 10)));

    // gridstack实例，使用响应式定义的化会改变内部数据结构，影响之后的判断
    let gridGs;
    const cardList = ref([]);

    const initGsEngine = () => {
      class CustomEngine extends GridStackEngine {
        /**
         * 看板加载卡片时初始化
         * @param {Object} node
         */
        prepareNode(node) {
          // 存在_id说明是gridstack初始化时默认存在的，直接渲染
          if (node._id) {
            mountWidget(node);
          }
          // 不存在_id说明未初始化，需要处理数据结构，在后续dropped钩子进行mounted
          else {
            let x, y, w, h, config;
            if (node?.config?.isRecover) {
              delete node.config.isRecover;
              x = node.x;
              y = node.y;
              w = node.w;
              h = node.h;
            } else {
              w = 30;
              h = 20;
              config = currentAppConfig.value || {};
              currentAppConfig.value = {};
            }
            const options = {
              x,
              y,
              w,
              h,
              // gridstack除了自带的属性会清除_attr格式的属性，自定义属性名前不能带_
              config,
            };
            Object.assign(node, options);
          }
          return super.prepareNode(node);
        }
      }
      GridStack.registerEngine(CustomEngine);
    };

    const initGsInstance = () => {
      const options = {
        styleInHead: true,
        column: 100,
        disableOneColumnMode: true,
        margin: 5,
        float: false,
        // row: 48,
        // minRow: 24,
        //// 高度限制是否应该用媒体查询做 最小高度是要给的
        maxRow: 100,
        staticGrid: false,
        dragOut: false,
        disableDrag: true,
        disableResize: false,
        resizable: { autoHide: true, handles: 'se, sw, s, e, w' },
        removable: true,
        handleClass: 'grid-stack-item',
        acceptWidgets: () => true,
      };
      gridGs = GridStack.init(options, `.workplace-instance-${workplaceId.value}`);
    };

    const mountWidget = node => {
      const el: HTMLElement | null = Array.prototype.find.call(node.el.children, node =>
        node.classList.contains('grid-stack-item-content'),
      );
      // 容器要显示card内容，清除原来已有的
      let firstChild = el.firstChild;
      while (firstChild) {
        el.removeChild(firstChild);
        firstChild = el.firstChild;
      }

      // 标记uid和选择器，方便teleport匹配到对应的卡片，使用cardid而不是appId防止一个页面出现多个相同应用
      const uid = node._id;
      // 初始化的card此时已经生成了_id，需要记录
      node.config._id = uid;
      const selector = `grid-stack-item-content-instance-${uid}`;
      el.classList.add(selector);
      cardList.value = cardList.value.concat({
        uid,
        selector,
        config: node.info || node.config,
      });
    };

    const currentAppConfig = ref({});
    const clone = event => {
      // 不能设置可拖拽事件的dom还是会有问题

      // 目前通过data-key的形式查找对应应用配置
      const appId = event.target.getAttribute('data-appId');
      bus.emit('homePanel:getCurrentAppConfig', {
        appId,
        cb: config => {
          currentAppConfig.value = config;
        },
      });
      return event.target.cloneNode(true);
    };

    const initGsDragInOption = () => {
      GridStack.setupDragIn(
        '.grid-stack-item',
        merge(
          {
            revert: 'invalid',
            scroll: false,
            appendTo: 'body',
            helper: clone,
          },
          {},
        ),
      );
    };

    const removeCard = node => {
      // cardList.value = cardList.value.filter(card => card.config._id !== node.config._id);
      gridGs.removeWidget(node.el);
      node.el?.remove();
      node.el = null;
      node = null;
    };

    const initGsEvent = () => {
      // 外侧卡片拖拽进入放入看板回调
      gridGs.on(
        'dropped',
        (_e: Event, _previousNode: GridStackNode | GridItemHTMLElement | GridStackNode[], node) => {
          // 存在config.id说明是右侧的应用
          if (!node.config.id) {
            removeCard(node);
            return;
          }

          let sameCount = 0;
          cardList.value.forEach(_node => {
            let id = _node.config.id;
            id === node.config.id && sameCount++;
          });
          // 经过prepareNode后，拖拽进来的卡片已经被算进gridstack里了，所以id相同的node至少有一个
          if (sameCount > 0) {
            removeCard(node);
            message.warning('不能展示相同的应用');
            return;
          }

          mountWidget(node);
        },
      );

      gridGs.on('removed', (_e: Event, nodes: GridStackNode[]) => {
        // 实际dom已删除，将被删除的dom关联的card数据也删掉
        nodes.forEach(node => {
          let uid = (node as any)._id;
          cardList.value = cardList.value.filter(card => uid !== card.uid);
        });
      });

      initGsDragInOption();
    };

    const resetApplicationPanel = tabid => {
      console.log(gridGs);
      // gridGs.destroy();
    };

    bus.on('homePanel:initGsDragInOption', initGsDragInOption);
    bus.on('homePanel:resetApplicationPanel', resetApplicationPanel);

    onMounted(() => {
      initGsInstance();
      initGsEvent();
    });

    onBeforeUnmount(() => {
      bus.off('homePanel:initGsDragInOption', initGsDragInOption);
      bus.off('homePanel:resetApplicationPanel', resetApplicationPanel);
    });

    const getNodes = () => {
      return gridGs?.engine.nodes || [];
    };

    const isStatic = ref(false);
    const cacheNodes = ref([]);
    const miniCardList = ref([]);

    const recurseUpdateNode = (_nodes, cb) => {
      const length = _nodes.length;
      const nodes = cloneDeep(_nodes);

      const exec = index => {
        setTimeout(() => {
          if (index < length) {
            cb(nodes[index]);
            exec(++index);
          }
        });
      };

      exec(0);
    };

    const maximize = cardConfig => {
      // 保存当前副本
      cacheNodes.value = gridGs.save();
      const nodes = getNodes();

      const cb = node => {
        console.log(`node:`, node);
        console.log(`cardConfig:`, cardConfig);
        console.log(`node._id:`, node._id);
        console.log(`cardConfig._id:`, cardConfig._id);
        if (node._id === cardConfig._id) {
          const _el = node.el;
          const style = window.getComputedStyle(containerRef.value);
          const width = parseInt(style.width.substring(0, style.width.length - 2));
          const height = parseInt(style.height.substring(0, style.height.length - 2));
          const h = Math.floor((height / width) * gridGs.getColumn());
          gridGs.update(_el, { x: 0, y: 0, w: gridGs.getColumn(), h });
          isStatic.value = true;
        } else {
          gridGs.update(node.el, { w: 0, h: 0 });
        }
      };
      recurseUpdateNode(nodes, cb);
    };

    const normalize = _cardConfig => {
      const nodes = getNodes();
      const _cacheNodes = cacheNodes.value;
      const cb = node => {
        const config = _cacheNodes.find(cnode => cnode.config._id === node.config._id);
        if (config) {
          gridGs.update(node.el, {
            x: config.x,
            y: config.y,
            w: config.w,
            h: config.h,
          });
        }
      };
      recurseUpdateNode(nodes, cb);
      cacheNodes.value = [];
      isStatic.value = false;
    };

    const minimize = cardConfig => {
      const node = getNodes().find(node => node._id === cardConfig._id);
      if (node) {
        miniCardList.value = miniCardList.value.concat({
          w: node.w,
          h: node.h,
          x: node.x,
          y: node.y,
          config: node.config,
          cacheEl: node.el,
          // cacheEl: node.el.querySelector('.grid-stack-item-content')?.querySelector('.workplace-card'),
        });

        // close(cardConfig);

        // update修改w\h只能传number类型的值，且最小的结果为1，执行update触发动画
        gridGs.update(node.el, { w: 0, h: 0 });
        // 通过setAttribute修改属性尺寸
        gridGs._writePosAttr(node.el, { w: '0', h: '0' });
        // 手动修改w\h属性确保不会影响到以后的布局
        node.w = node.h = 0;
        // 再执行一次批量更新操作，为了消除w\h改为0时的影响
        // gridGs.batchUpdate();

        // gridGs.setStatic(true);
      }
    };

    const recover = cardConfig => {
      const index = miniCardList.value.findIndex(node => node.config._id === cardConfig._id);
      const target = miniCardList.value[index];
      target.config.isRecover = true;

      if (target) {
        // 对应缩小时使用close的方案
        // gridGs.addWidget(target.cacheEl, {
        //   x: target.x,
        //   y: target.y,
        //   w: target.w,
        //   h: target.h,
        //   config: target.config,
        // })

        const node = getNodes().find(node => node._id === cardConfig._id);
        if (node) {
          gridGs.update(node.el, { w: target.w, h: target.h });
        }

        miniCardList.value.splice(index, 1);

        // if (!miniCardList.value.length) {
        //   gridGs.setStatic(false);
        // }
      }
    };

    const close = cardConfig => {
      const node = getNodes().find(node => node._id === cardConfig._id);
      node && gridGs.removeWidget(node.el);
    };
    initGsEngine();
    initGsDragInOption();

    const onClick = () => {
      let serializedData = [
        {
          x: 3,
          y: 1,
          h: 2,
          id: 1,
          info: {
            name: '无极1',
            id: 1,
            _id: 1,
            url: 'https://wujicode.cn/xy/app/prod/official/index',
          },
        },
        {
          x: 4,
          y: 1,
          h: 2,
          id: 2,
          info: {
            name: '无极2',
            id: 2,
            _id: 2,
            url: 'https://wujicode.cn/xy/app/prod/official/index',
          },
        },
        {
          x: 5,
          y: 1,
          h: 2,
          id: 3,
          info: {
            name: '无极3',
            id: 3,
            _id: 3,
            url: 'https://wujicode.cn/xy/app/prod/official/index',
          },
        },
        {
          x: 6,
          y: 1,
          h: 2,
          id: 4,
          info: {
            name: '无极4',
            id: 4,
            _id: 4,
            url: 'https://wujicode.cn/xy/app/prod/official/index',
          },
        },
      ];
      gridGs.load(serializedData, true);
    };

    return {
      containerRef,
      workplaceId,
      cardList,
      isStatic,
      maximize,
      normalize,
      miniCardList,
      minimize,
      recover,
      close,
      onClick,
    };
  },
});
</script>

<style scoped lang="less">
.workplace-container {
  width: 100%;
  height: 100%;
  max-height: calc(100vh - 162px);
  overflow: auto;
  // overflow-y: hidden;
  display: flex;
  flex-direction: column;
}
.static-workplace-container {
  overflow-y: hidden;
}
.workplace-instance {
  width: 100%;
  flex: 1;
}

.mini-cardView-container {
  height: 40px;
  padding: 6px 12px;
  background: #fff;

  .mini-cardView-list {
    height: 100%;
    display: flex;
    justify-content: flex-start;
    align-items: center;
  }

  .mini-cardView-sign {
    margin-right: 8px;
    cursor: pointer;
  }
}
</style>
