<script lang="ts">
export default {
  inheritAttrs: false
}
</script>

<script setup lang="ts">
import { nanoid } from 'nanoid'
import type { GraphNode } from '@vue-flow/core'

import { VueFlow, useVueFlow, PanelPosition, MarkerType, ConnectionLineType } from '@vue-flow/core'
import { Controls } from '@vue-flow/controls'
import CustomNode from './CustomNode.vue'
import CustomTextNode from './CustomTextNode.vue'
import CustomEdge from './CustomEdge.vue'

interface Props {
  action?: string
  role?: string
  templateType?: string
  score?: number
}
const props = withDefaults(defineProps<Props>(), { action: 'edit', role: 'teacher', score: 0 })
provide('action', props.action)
provide('role', props.role)
provide('templateType', props.templateType)
provide('score', props.score)

const { findNode, onConnect, addEdges, addNodes, project, vueFlowRef, nodesDraggable } = useVueFlow()

const onDragOver = (event: DragEvent) => {
  event.preventDefault()

  if (event.dataTransfer) {
    event.dataTransfer.dropEffect = 'move'
  }
}

function getDirection(source: GraphNode, target: GraphNode) {
  const horizontalDistance = target.position.x - source.position.x
  const verticalDistance = target.position.y - source.position.y
  if (Math.abs(horizontalDistance) > Math.abs(verticalDistance)) {
    return horizontalDistance > 0 ? 'right' : 'left'
  } else {
    return verticalDistance > 0 ? 'bottom' : 'top'
  }
}

onConnect(params => {
  const map: any = {
    'handle-yes': { label: '是', style: { stroke: '#81b337' } },
    'handle-no': { label: '否', style: { stroke: '#a16222' } },
    'handle-success': { label: '成功', style: { stroke: '#81b337' } },
    'handle-failure': { label: '失败', style: { stroke: '#a16222' } },
    'handle-a': { label: 'A', style: { stroke: '#81b337' } },
    'handle-b': { label: 'B', style: { stroke: '#a16222' } }
  }
  let customParams = {}
  if (params.sourceHandle) {
    customParams = map[params.sourceHandle]
  }
  const style = {
    stroke: '#5b6b73'
    // strokeWidth: 2
  }
  const sourceNode = findNode(params.source)
  const targetNode = findNode(params.target)
  let direction = 'right'
  if (sourceNode && targetNode) {
    direction = getDirection(sourceNode, targetNode)
  }
  addEdges([
    {
      ...params,
      sourceHandle: `handle-${direction}`,
      type: 'custom',
      markerEnd: MarkerType.ArrowClosed,
      style,
      ...customParams
    }
  ])
})

const onDrop = (event: DragEvent) => {
  let data: any = event.dataTransfer?.getData('application/vueflow') || ''
  try {
    data = JSON.parse(data)
  } catch (error) {
    console.log(error)
  }

  if (!vueFlowRef.value) return

  const { left, top } = vueFlowRef.value.getBoundingClientRect()

  const position = project({ x: event.clientX - left, y: event.clientY - top })

  const newNode = {
    id: nanoid(4),
    type: 'custom',
    position,
    label: data.name,
    data
  }

  addNodes([newNode])

  // align node position after drop, so it's centered to the mouse
  nextTick(() => {
    const node = findNode(newNode.id)
    if (!node) return
    const stop = watch(
      () => node.dimensions,
      dimensions => {
        if (dimensions.width > 0 && dimensions.height > 0) {
          node.position = {
            x: node.position.x - node.dimensions.width / 2,
            y: node.position.y - node.dimensions.height / 2
          }
          stop()
        }
      },
      { deep: true, flush: 'post' }
    )
  })
}

const onAddText = (event: MouseEvent) => {
  if (!vueFlowRef.value) return
  const { width } = vueFlowRef.value.getBoundingClientRect()
  const position = project({ x: width - 158, y: 60 })
  const newNode = {
    id: nanoid(4),
    type: 'text',
    position,
    label: '编辑说明'
  }
  addNodes([newNode])
}
</script>
<template>
  <div class="flow">
    <slot name="left-panel"> </slot>
    <el-card shadow="never" class="flow-main" @drop="onDrop">
      <slot name="header"> </slot>
      <VueFlow
        snap-to-grid
        :snap-grid="[40, 40]"
        :zoom-on-scroll="false"
        :prevent-scrolling="false"
        :connection-radius="30"
        :connection-line-options="{ markerEnd: MarkerType.ArrowClosed, type: ConnectionLineType.Straight }"
        @dragover="onDragOver"
        v-bind="$attrs">
        <template #node-custom="node">
          <CustomNode :node="node" />
        </template>
        <template #node-text="node">
          <CustomTextNode :node="node" />
        </template>
        <template #edge-custom="props">
          <CustomEdge v-bind="props" />
        </template>
        <Controls :show-interactive="false" :show-fit-view="false" :position="PanelPosition.TopRight">
          <template #top>
            <slot name="controls"></slot>
            <el-button type="primary" size="small" @click="onAddText" v-if="nodesDraggable">添加说明</el-button>
          </template>
        </Controls>
      </VueFlow>
      <slot name="footer"></slot>
    </el-card>
    <slot name="right-panel"></slot>
  </div>
</template>

<style lang="scss">
@import '@vue-flow/core/dist/style.css';
// @import '@vue-flow/core/dist/theme-default.css';
@import '@vue-flow/controls/dist/style.css';

.flow {
  display: flex;
  column-gap: 20px;
  height: 80vh;
}
.flow-main {
  flex: 1;
  .el-card__body {
    height: 100%;
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
  }
  .vue-flow {
    flex: 1;
  }
  .vue-flow__controls {
    display: flex;
    align-items: center;
    margin: 15px 5px;
    box-shadow: none;
    .vue-flow__controls-button {
      border: 1px solid #eee;
      + .vue-flow__controls-button {
        border-left: none;
      }
    }
    .el-button {
      margin-right: 10px;
    }
  }
}
</style>
