<script setup lang="ts">
import {
  ref,
  onMounted,
  InjectionKey,
  Ref,
  provide,
  inject,
  nextTick,
  markRaw,
} from "vue";
import {
  HJMethodName,
  HJNodeData,
  HJNodeType,
  nodeTypes,
  edgeTypes,
  HJMethodProvideName,
  CurrentHeaderOperationProvideName,
  CurrentSelectedEdgeProvideName,
  HJFlowProps,
} from "../types/comTypes";
import Panel from "../panel/index.vue";
import CommonEdge from "../edges/commonEdge.vue";
import DropzoneBackground from "../background/DropzoneBackground.vue";
import { useVueFlow, VueFlow, Edge } from "@vue-flow/core";
import useDragAndDrop from "../hooks/useDnD";
import { sortNodesByPosition, useLayout } from "../utils/useLayout";
import { useSnakeLayoutHook } from "../hooks/snakeHooks";
import { ElMessage } from "element-plus";

defineOptions({
  name: "HJFlow",
});

const { onConnect, addEdges, fitView, updateNode, findNode } = useVueFlow();
const { onDragOver, onDrop, onDragLeave, isDragOver } = useDragAndDrop();

onConnect(addEdges);

// ========== 外面传进来的参数  ===========
const nodes = defineModel<HJNodeData[]>("nodesData");
const edges = defineModel("edgesData");
const props = defineProps<HJFlowProps>();

// ==========vueflow 本身的方法操作 ===========
const doubleClick = ({ node }) => {
  // 如果通过双击弹出额外信息编辑是可以拿到jsonData的，但是如果在自定义node中是拿不到的，非要在自定义中拿就得把额外的信息放到data中。
  // 还要看 是否要根据 dragData中的数据生成具体 avue页面。 之后可以尝试通过useVueFlow的find能否找到
  console.log("doubleClick", node);
};

const currentClickEdgeData = ref<Edge | null>(null);
provide(CurrentSelectedEdgeProvideName, currentClickEdgeData);
const onClickEdge = ({ edge }) => {
  currentClickEdgeData.value = JSON.parse(JSON.stringify(edge));
};

// ======= Emits =======
const emits = defineEmits<{
  hjMethod: [name: HJMethodName, node: HJNodeData];
}>();

// 提供一个孙子组件里面（比如自定义node里面) 调用本组件的方法
// ======= Node操作 =======
const currentHeaderOperationNodeData = ref<HJNodeData | null>(null); // 当前选中的header操作节点
provide(CurrentHeaderOperationProvideName, currentHeaderOperationNodeData);
provide(HJMethodProvideName, (name: HJMethodName, node: HJNodeData) => {
  currentHeaderOperationNodeData.value = JSON.parse(JSON.stringify(node)); // 复制一份数据
  console.log("provide", name, node);
  emits("hjMethod", name, node);
});

///// === 数据历史纪录 用于回退或者重置按钮 === /////
let originData: any = null;
let historyList: any[] = [];

///// === 所有的node 和edge change 调用的地方 === /////
const onFlowNodesChange = (data: any) => {
  if (
    data.length > 0 &&
    data[0].type &&
    (data[0].type === "add" || data[0].type === "remove")
  ) {
    historyList.unshift(
      JSON.stringify({
        nodes: markRaw(nodes.value),
        edges: markRaw(edges.value),
      })
    );
    console.log("onFlowNodesChange historyList", historyList);
  }
};
const onFlowEdgesChange = (data: any) => {
  if (
    data.length > 0 &&
    data[0].type &&
    (data[0].type === "add" || data[0].type === "remove")
  ) {
    historyList.unshift(
      JSON.stringify({
        nodes: markRaw(nodes.value),
        edges: markRaw(edges.value),
      })
    );
  }
};
///// === 所有的node 和edge change 调用的地方 === /////

///// === 初始化   === /////
const initFlow = (nodes: HJNodeData[], edges: any[]) => {
  originData = JSON.stringify({ nodes, edges }); // 复制一份数据
  historyList = []; // 初始化历史纪录
};

// 为父组件提供更新node中的data数据的操作 一般只更新information就行了 更新一个node的值
const updateNodeData = (node: HJNodeData) => {
  console.log("updateNodeData", node);

  if (node.id) {
    const findNodeData = findNode(node.id) as HJNodeData;
    findNodeData.data.information = node.data.information;
    console.log("findNodeData", findNodeData);
    // updateNode(node.id, node)
  }
};
// 清空状态 比如选中某一个节点的状态
const clearStatus = () => {
  currentHeaderOperationNodeData.value = null;
  currentClickEdgeData.value = null;
  historyList = [];
};

// 获取是否是正在编辑的状态 目前只判断节点的新增和删除  外面保存之后要清空状态
const getChangedStatus = () => {
  return historyList.length > 0;
};

defineExpose({
  updateNodeData,
  initFlow,
  clearStatus,
  getChangedStatus,
});

// ======= Panel =======
// 测试layout  点击切换布局时候的操作
const { layout } = useLayout();

async function layoutGraph(direction: "LR" | "TB") {
  nodes.value = layout(nodes.value, edges.value, direction);

  console.log("排序完", nodes.value);

  nextTick(() => {
    fitView();
    nodes.value = sortNodesByPosition(nodes.value);
  });
}

const pannelOperations = {
  addNode: (nodeType: HJNodeType) => {
    console.log("addNode", nodeType);
  },
  layout: (direction: "LR" | "TB") => {
    layoutGraph(direction);
  },
  toSnake: () => {
    console.log("toSnake");
    nodes.value = useSnakeLayoutHook(nodes.value, edges.value);
    nextTick(() => {
      fitView();
    });
  },
  reset: () => {
    console.log("reset");
    if (originData && JSON.parse(originData)) {
      let data = JSON.parse(originData);
      nodes.value = data.nodes;
      edges.value = data.edges;
      historyList = [];
    }
  },
  back: () => {
    console.log("back");
    let firstHistory = historyList.shift();
    if (firstHistory) {
      let historyData = JSON.parse(firstHistory);
      nodes.value = historyData.nodes;
      edges.value = historyData.edges;
    } else {
      ElMessage.warning("已是初始线路");
    }
  },
  saveTemplate: () => {
    emits("hjMethod", HJMethodName.SaveTemplate, nodes.value[0]);
  },
};
// ======= Panel ======= ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
</script>

<template>
  <VueFlow
    v-model:nodes="nodes"
    v-model:edges="edges"
    :node-types="nodeTypes"
    :edgeTypes="edgeTypes"
    @dragover="onDragOver"
    @dragleave="onDragLeave"
    @drop="onDrop"
    @nodeDoubleClick="doubleClick"
    @edge-click="onClickEdge"
    @nodes-change="onFlowNodesChange"
    @edges-change="onFlowEdgesChange"
  >
    <DropzoneBackground
      :style="{
        backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
        transition: 'background-color 0.2s ease',
      }"
    >
      <!--      <p v-if="isDragOver">Drop here</p>-->
    </DropzoneBackground>

    <!--        右面上工具栏操作 -->
    <Panel
      v-if="showPanel"
      v-on="pannelOperations"
      :fun-names="panelBtns"
    ></Panel>
  </VueFlow>
</template>

<style scoped lang="less"></style>
