<template>
  <Panel title="大纲树" class="vtj-outline" back @back="handleBack">
    <ElTree
      :data="tree"
      node-key="id"
      default-expand-all
      draggable
      empty-text="请在文档管理器新建或打开文件"
      :expand-on-click-node="false"
      :allow-drop="allowDrop"
      :allow-drag="allowDrag"
      @current-change="handleCurrentChange"
      @node-drop="handleDrop"
      @mouseover="handleMouseOver">
      <template #default="{ data }">
        <div class="vtj-outline__item">
          <span>
            {{ data.label }}:
            <i :class="`is-${data.nodeType}`">{{ data.nodeType }}</i>
          </span>
          <span class="vtj-outline__actions">
            <i
              v-if="!data.node.lock"
              @click="setLock(data.node, true)"
              class="vtj-icon-lock"
              title="锁定"></i>
            <i
              v-if="data.node.lock"
              @click="setLock(data.node, false)"
              class="vtj-icon-unlock"
              title="解锁"></i>
            <i
              v-if="data.node.visible && !data.node.lock"
              @click="setVisible(data.node, false)"
              class="vtj-icon-invisible"
              title="设置隐藏"></i>
            <i
              v-if="!data.node.visible && !data.node.lock"
              @click="setVisible(data.node, true)"
              class="vtj-icon-visible"
              title="设置显示"></i>
            <i
              v-if="data.node.root !== data.node && !data.node.lock"
              @click="handleCopy(data.node)"
              class="vtj-icon-copy"
              title="复制"></i>
            <i
              v-if="data.node.root !== data.node && !data.node.lock"
              @click="handleRemove(data.node)"
              class="vtj-icon-remove"
              title="删除"></i>
          </span>
          <span class="vtj-outline__status">
            <i
              v-if="!data.node.visible"
              class="vtj-icon-invisible"
              title="不可见"></i>
            <i v-if="data.node.lock" class="vtj-icon-lock" title="已锁定"></i>
          </span>
        </div>
      </template>
    </ElTree>
  </Panel>
</template>
<script lang="ts" setup>
import Panel from '../parts/Panel.vue';
import { ElTree } from 'element-plus';
import useCore from '../hooks/useCore';
import { ElMessageBox } from 'element-plus';
import { computed } from 'vue';
import {
  INode,
  PAGE_NAME,
  BLOCK_NAME,
  TEMPLATE_NAME,
  EVENT_OUTLINE_CHANGE,
  EVENT_NODE_CHANGE,
  EVENT_OUTLINE_HOVER,
  EVENT_OUTLINE_SELECTED
} from '@vtj/engine';
import { doDrop } from '../hooks/useDesigner';

const { project, emitter, engine } = useCore();

interface ITreeItem {
  id: string;
  label: string;
  nodeType: string;
  node: INode;
  children: ITreeItem[];
}

const emit = defineEmits(['back']);

function getNodeType(node: INode) {
  if (node.slotName) {
    return `Slot[${node.slotName}]`;
  }
  return [PAGE_NAME, BLOCK_NAME].includes(node.name) ? node.name : 'Node';
}

function toTree(nodes: INode[]) {
  const tree: ITreeItem[] = [];

  nodes.forEach((node) => {
    const item: ITreeItem = {
      id: node.id,
      label: node.name,
      nodeType: getNodeType(node),
      node,
      children: []
    };
    if (Array.isArray(node.children)) {
      item.children = toTree(node.children);
    }

    tree.push(item);
  });

  return tree;
}

const tree = computed(() => {
  if (project.current) {
    return toTree([project.current] as INode[]);
  }
  return [];
});

const handleDrop = (dragging: any, drop: any, type: any, ev: any) => {
  const item: ITreeItem = dragging.data;
  const target: ITreeItem = drop.data;
  const targetNode = type === 'inner' ? target.node : target.node.parent;
  doDrop(engine.assets, targetNode, (slot) => {
    item.node.update({ slotName: slot?.name || '' } as any);
    project.current?.move(item.node, target.node, type);
    emitter.emit(EVENT_OUTLINE_CHANGE);
  });
};

const allowDrag = (node: any) => {
  const item: ITreeItem = node.data;
  // 锁定的节点不能更改
  if (item.node.lock) return false;
  // 插槽模板和页面不能拖拽
  return ![TEMPLATE_NAME, PAGE_NAME].includes(item.node.name);
};
const allowDrop = (dragging: any, drop: any, type: any) => {
  const dragItem: ITreeItem = dragging.data;
  const dropItem: ITreeItem = drop.data;
  // 锁定的节点不能更改
  if (dropItem.node.lock) return false;
  // 不能放到和根结点同级
  if (
    [PAGE_NAME, BLOCK_NAME].includes(dropItem.nodeType) &&
    ['prev', 'next'].includes(type)
  ) {
    return false;
  }
  const root = dragItem.node.root;
  const desc = root.componentMap[dragItem.node.name];
  if (!desc) return true;
  return (
    (type === 'inner' && dropItem.node.allowDrop(desc)) ||
    (type !== 'inner' && dropItem.node.parent?.allowDrop(desc))
  );
};

const setVisible = (node: INode, visible: boolean) => {
  node.visible = visible;
  emitter.emit(EVENT_NODE_CHANGE, node);
  emitter.emit(EVENT_OUTLINE_CHANGE);
};

const setLock = (node: INode, lock: boolean) => {
  node.lock = lock;
  emitter.emit(EVENT_NODE_CHANGE, node);
  emitter.emit(EVENT_OUTLINE_CHANGE);
};

const handleCurrentChange = (current: any) => {
  emitter.emit(
    EVENT_OUTLINE_SELECTED,
    current.node.visible ? current.node : null
  );
};

const handleMouseOver = (e: any) => {
  const vm = e.target?.__vueParentComponent;
  if (!vm) return;
  const item: ITreeItem = vm.ctx?.node?.data;
  if (!item) return;
  emitter.emit(EVENT_OUTLINE_HOVER, item.node.visible ? item.node : null);
};

const handleCopy = (node: INode) => {
  node.copy();
  emitter.emit(EVENT_OUTLINE_CHANGE);
};

const handleRemove = (node: INode) => {
  ElMessageBox.confirm('确定删除吗？', '提示', {
    type: 'warning'
  }).then(() => {
    node.dispose();
    emitter.emit(EVENT_OUTLINE_CHANGE);
  });
};

const handleBack = () => {
  emit('back');
};
</script>
