<template>
  <div @drop="onDrop" class="rule-flow-box123456">
    <VueFlow
      :nodes="state.nodes"
      :edges="state.edges"
      :class="{ dark }"
      class="basic-flow"
      :default-viewport="{ zoom: 1 }"
      :min-zoom="0.2"
      :max-zoom="4"
      @dragover="onDragOver"
      @dragleave="onDragLeave"
      @node-click="onNodeClick"
      @edge-click="onEdgeClick"
      @pane-click="onProcessClick"
    >
      <!-- bind your custom node type to a component by using slots, slot names are always `node-<type>` -->
      <template #node-special="specialNodeProps">
        <CommonNode v-bind="specialNodeProps" />
      </template>
      <!-- 开始节点 -->
      <template #node-start="StartNodeProps">
        <StartNode v-bind="StartNodeProps" />
      </template>
      <!-- 事件节点 -->
      <template #node-event="EventNodeProps">
        <CommonNode v-bind="EventNodeProps" />
      </template>

      <!-- 规则节点 -->
      <template #node-rule="RuleNodeProps">
        <CommonNode v-bind="RuleNodeProps" />
      </template>

      <!-- 选择节点 -->
      <template #node-select="SelectNodeProps">
        <CommonNode v-bind="SelectNodeProps" />
      </template>

      <!-- 条件节点 -->
      <template #node-condition="ConditionNodeProps">
        <CommonNode v-bind="ConditionNodeProps" />
      </template>

      <!-- for循环节点 -->
      <template #node-for="ForNodeProps">
        <CommonNode v-bind="ForNodeProps" />
      </template>

      <!-- while循环节点 -->
      <template #node-while="WhileNodeProps">
        <CommonNode v-bind="WhileNodeProps" />
      </template>
      <!-- 运维节点 -->
      <template #node-devOps="DevOpsNodeProps">
        <CommonNode v-bind="DevOpsNodeProps" />
      </template>

      <!-- bind your custom edge type to a component by using slots, slot names are always `edge-<type>` -->
      <template #edge-custom="customEdgeProps">
        <SpecialEdge v-bind="customEdgeProps" />
      </template>

      <DropzoneBackground
        :style="{
          backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
          transition: 'background-color 0.2s ease',
        }"
      >
        <p v-if="isDragOver"></p>
      </DropzoneBackground>

      <Controls position="top-right">
        <ControlButton title="重置变换" @click="resetTransform">
          <Icon name="reset" />
        </ControlButton>

        <ControlButton title="更新节点位置" @click="updatePos">
          <Icon name="update" />
        </ControlButton>

        <ControlButton title="切换模式" @click="toggleDarkMode">
          <Icon v-if="dark" name="sun" />
          <Icon v-else name="moon" />
        </ControlButton>
      </Controls>
    </VueFlow>
  </div>
</template>
<script setup lang="ts">
  import { reactive, ref } from 'vue';
  import type { Node, Edge } from '@vue-flow/core';
  import { VueFlow, useVueFlow } from '@vue-flow/core';
  import { ControlButton, Controls } from '@vue-flow/controls';
  // these components are only shown as examples of how to use a custom node or edge
  // you can find many examples of how to create these custom components in the examples page of the docs
  import StartNode from './components/StartNode.vue';
  import CommonNode from './components/CommonNode.vue';
  import SpecialEdge from './components/SpecialEdge.vue';
  import DropzoneBackground from './components/DropzoneBackground.vue';

  import Icon from './components/Icon.vue';
  import useDragAndDrop from './../hooks/useDragAndDrop';
  import { defaultNodes, getEdgePrefix } from './../config/index';
  import { useRuleProcessStore } from '../store';
  import { RuleProcessNodeKey } from '/@/model/ruleProcessConfig';
  interface ViewportItem {
    x: number;
    y: number;
    zoom: number;
  }
  interface Props {
    nodes: Array<Node>;
    edges: Array<Edge>;
    viewport: ViewportItem;
  }
  const props = withDefaults(defineProps<Props>(), {
    nodes: () => {
      return [];
    },
    edges: () => {
      return [];
    },
    viewport: () => {
      return { x: 0, y: 0, zoom: 1 };
    },
  });

  const emits = defineEmits(['nodeClick', 'edgeClick', 'processCLick']);
  const store = useRuleProcessStore();
  const { setProperties, setInfoId, setEdgeId, autoIncrementLastEdgeId } = store;
  const { onDragOver, onDrop, onDragLeave, isDragOver } = useDragAndDrop();

  const { onInit, onNodeDragStop, onConnect, addEdges, setViewport } = useVueFlow();
  const state = reactive({
    nodes: defaultNodes as Array<Node>,
    edges: [] as Array<Edge>,
    viewport: { x: 0, y: 0, zoom: 1 } as ViewportItem,
  });
  // our dark mode toggle flag
  const dark = ref(false);

  /**
   * This is a Vue Flow event-hook which can be listened to from anywhere you call the composable, instead of only on the main component
   * Any event that is available as `@event-name` on the VueFlow component is also available as `onEventName` on the composable and vice versa
   *
   * onInit is called when the VueFlow viewport is initialized
   */
  onInit((_vueFlowInstance) => {
    if (props.nodes) {
      state.nodes = props.nodes;
    }
    if (props.edges) {
      state.edges = props.edges;
    }
    if (props.viewport) {
      state.viewport = props.viewport;
      setViewport(state.viewport);
    } else {
      setViewport({ x: 0, y: 0, zoom: 1 });
    }
    // setViewport({ x: 0, y: 0, zoom: 1 });
    // instance is the same as the return of `useVueFlow`
    // vueFlowInstance.fitView();
    // resetTransform();
  });

  /**
   * onNodeDragStop is called when a node is done being dragged
   *
   * Node drag events provide you with:
   * 1. the event object
   * 2. the nodes array (if multiple nodes are dragged)
   * 3. the node that initiated the drag
   * 4. any intersections with other nodes
   */
  onNodeDragStop(({ event, nodes, node }) => {
    console.log('Node Drag Stop', { event, nodes, node });
  });

  /**
   * onConnect is called when a new connection is created.
   *
   * You can add additional properties to your new edge (like a type or label) or block the creation altogether by not calling `addEdges`
   */
  onConnect((connection) => {
    let incrementLastEdgeId = autoIncrementLastEdgeId();
    let id = getEdgePrefix + incrementLastEdgeId;
    let item = {
      type: RuleProcessNodeKey.EDGE_NODE,
      label: '连接线' + incrementLastEdgeId,
      id,
    };
    let json = {
      ...connection,
      type: RuleProcessNodeKey.EDGE_NODE,
      data: item,
      id,
      updatable: true,
    };
    setProperties(id, item);
    addEdges(json);
    setInfoId('');
  });

  /**
   * To update a node or multiple nodes, you can
   * 1. Mutate the node objects *if* you're using `v-model`
   * 2. Use the `updateNode` method (from `useVueFlow`) to update the node(s)
   * 3. Create a new array of nodes and pass it to the `nodes` ref
   */
  function updatePos() {
    state.nodes = state.nodes.map((node: Node) => {
      return {
        ...node,
        position: {
          x: Math.random() * 400,
          y: Math.random() * 400,
        },
      };
    });
  }

  /**
   * Resets the current viewport transformation (zoom & pan)
   */
  function resetTransform() {
    setViewport({ x: 0, y: 0, zoom: 1 });
  }

  function toggleDarkMode() {
    dark.value = !dark.value;
  }
  // Node click event handler
  function onNodeClick({ node }) {
    emits('nodeClick', node);
  }

  // Edge click event handler
  function onEdgeClick({ edge }) {
    emits('edgeClick', edge);
  }
  function onProcessClick() {
    setInfoId('');
    setEdgeId('');
  }
</script>

<style>
  /* import the necessary styles for Vue Flow to work */
  @import url('@vue-flow/core/dist/style.css');

  /* import the default theme, this is optional but generally recommended */
  @import url('@vue-flow/core/dist/theme-default.css');

  .rule-flow-box123456 {
    height: 100%;
  }

  .vue-flow.dark.basic-flow {
    background-color: #464242;
  }

  .basic-flow {
    flex-direction: column;
    display: flex;
    height: 100%;
  }

  .basic-flow .vue-flow-wrapper {
    flex-grow: 1;
    height: 100%;
  }

  .dropzone-background {
    position: relative;
    height: 100%;
    width: 100%;
  }

  .dropzone-background .overlay {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
    pointer-events: none;
  }

  .basic-flow .vue-flow__controls {
    padding: 4px;
    margin-right: 10px;
    position: absolute;
    top: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 34px;
    background: #fff;
    box-shadow: 0 0 8px 0 rgb(0 0 0 / 5%);
    border-radius: 6px;
  }

  .basic-flow .vue-flow__controls .vue-flow__controls-button {
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0 6px;
    cursor: pointer;
  }

  .basic-flow .vue-flow__controls .vue-flow__controls-button svg {
    width: 100%;
    width: 15px;
    height: 15px;
  }

  .basic-flow .vue-flow__node-custom {
    display: flex;
    align-items: center;
    padding: 0;
    min-width: 140px;
    border-radius: 4px;
  }

  .basic-flow .vue-flow__node-custom .text {
    font-size: 14px;
    margin-left: 10px;
  }

  .rule-box-123456 {
    position: relative;
    color: #000;
    background-color: #fff;
    width: 300px;
    border-radius: 8px 8px 0 0;
  }

  .rule-box-123456 .head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 30px;
    border-radius: 8px 8px 0 0;
    padding: 0 10px;
    color: #fff;
  }

  .rule-box-123456 .head .text {
    font-weight: 400;
    font-size: 12px;
    line-height: 12px;
  }

  .rule-box-123456 .head .left {
    display: flex;
    align-items: center;
  }

  .rule-box-123456 .head .right {
    display: flex;
    align-items: center;
  }

  .rule-box-123456 .head .right .icon {
    margin-right: 10px;
  }

  .rule-box-123456 .title-box {
    padding-left: 14px;
    display: flex;
    align-items: center;
    height: 40px;
    font-weight: bold;
    font-size: 12px;
    color: #333;
  }

  .vue-flow__handle {
    position: absolute;
    pointer-events: none;
    width: 4px;
    height: 80px;
    background: transparent;
    border-radius: 100%;
    border: none;
    z-index: 4;
  }

  .nodrag.nopan .nopan-btn {
    width: 24px;
    height: 24px;
    background: #fff;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
</style>
