<template>
  <view class="graph-container">
    <view class="canvas-wrapper">
      <canvas
          canvas-id="graphCanvas"
          class="graph-canvas"
          @touchstart="onTouchStart"
          @touchmove="onTouchMove"
          @touchend="onTouchEnd"
      ></canvas>
    </view>

    <!-- 控制面板 -->
    <view class="control-panel">
      <view class="control-group">
        <text class="control-label">布局算法:</text>
        <picker @change="onLayoutChange" :value="layoutIndex" :range="layoutOptions">
          <view class="picker">{{ layoutOptions[layoutIndex] }}</view>
        </picker>
      </view>

      <view class="control-group">
        <text class="control-label">显示标签:</text>
        <switch :checked="showLabels" @change="toggleLabels" />
      </view>

      <view class="button-group">
        <button class="btn primary" @tap="addRandomNode">添加节点</button>
        <button class="btn secondary" @tap="resetView">重置视图</button>
        <button class="btn warning" @tap="exportData">导出数据</button>
      </view>
    </view>

    <!-- 节点信息面板 -->
    <view v-if="selectedNode" class="node-info-panel">
      <view class="panel-header">
        <text class="panel-title">节点详情</text>
        <text class="close-btn" @tap="deselectNode">×</text>
      </view>
      <view class="node-details">
        <view class="detail-item">
          <text class="detail-label">名称:</text>
          <text class="detail-value">{{ selectedNode.name }}</text>
        </view>
        <view class="detail-item">
          <text class="detail-label">ID:</text>
          <text class="detail-value">{{ selectedNode.id }}</text>
        </view>
        <view class="detail-item">
          <text class="detail-label">类型:</text>
          <text class="detail-value">{{ selectedNode.type }}</text>
        </view>
        <view class="detail-item">
          <text class="detail-label">连接数:</text>
          <text class="detail-value">{{ getNodeDegree(selectedNode) }}</text>
        </view>
      </view>
    </view>

    <!-- 统计信息 -->
    <view class="stats-panel">
      <text class="stat-item">节点: {{ graphData.nodes.length }}</text>
      <text class="stat-item">连接: {{ graphData.links.length }}</text>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      ctx: null,
      canvasWidth: 750,
      canvasHeight: 500,
      graphData: {
        nodes: [],
        links: []
      },
      selectedNode: null,
      hoveredNode: null,
      showLabels: true,
      layoutIndex: 0,
      layoutOptions: ['力导向布局', '环形布局', '树状布局'],
      isDragging: false,
      dragStartX: 0,
      dragStartY: 0,
      offsetX: 0,
      offsetY: 0,
      scale: 1,
      animationId: null
    }
  },
  onReady() {
    this.initCanvas();
    this.initGraphData();
    this.startAnimation();
  },
  onUnload() {
    this.stopAnimation();
  },
  methods: {
    initCanvas() {
      this.ctx = uni.createCanvasContext('graphCanvas', this);

      // 获取系统信息设置Canvas尺寸
      const systemInfo = uni.getSystemInfoSync();
      this.canvasWidth = systemInfo.windowWidth;
      this.canvasHeight = systemInfo.windowHeight * 0.6;
    },

    initGraphData() {
      // 示例数据
      this.graphData = {
        nodes: [
          { id: 'node1', name: '中心节点', type: 'center', x: 375, y: 250, size: 20, color: '#ff6b6b' },
          { id: 'node2', name: '用户节点', type: 'user', x: 275, y: 150, size: 15, color: '#4ecdc4' },
          { id: 'node3', name: '产品节点', type: 'product', x: 475, y: 150, size: 15, color: '#45b7d1' },
          { id: 'node4', name: '分类节点', type: 'category', x: 275, y: 350, size: 15, color: '#96ceb4' },
          { id: 'node5', name: '服务节点', type: 'service', x: 475, y: 350, size: 15, color: '#feca57' }
        ],
        links: [
          { source: 'node1', target: 'node2' },
          { source: 'node1', target: 'node3' },
          { source: 'node1', target: 'node4' },
          { source: 'node1', target: 'node5' },
          { source: 'node2', target: 'node3' },
          { source: 'node4', target: 'node5' }
        ]
      };
    },

    startAnimation() {
      const animate = () => {
        this.updateLayout();
        this.drawGraph();
        this.animationId = requestAnimationFrame(animate);
      };
      animate();
    },

    stopAnimation() {
      if (this.animationId) {
        cancelAnimationFrame(this.animationId);
      }
    },

    updateLayout() {
      // 简单的力导向布局模拟
      if (this.layoutIndex === 0) {
        this.applyForceDirectedLayout();
      } else if (this.layoutIndex === 1) {
        this.applyCircularLayout();
      } else if (this.layoutIndex === 2) {
        this.applyTreeLayout();
      }
    },

    applyForceDirectedLayout() {
      const centerX = this.canvasWidth / 2 + this.offsetX;
      const centerY = this.canvasHeight / 2 + this.offsetY;

      // 简化的力导向布局
      this.graphData.nodes.forEach((node, i) => {
        if (node.id === 'node1') {
          // 中心节点保持在中心
          node.x = centerX;
          node.y = centerY;
          return;
        }

        // 向中心节点的引力
        const centerNode = this.graphData.nodes[0];
        const dx = centerNode.x - node.x;
        const dy = centerNode.y - node.y;
        const distance = Math.sqrt(dx * dx + dy * dy);

        if (distance > 0) {
          const force = 0.1;
          node.x += dx * force;
          node.y += dy * force;
        }

        // 节点间的斥力
        this.graphData.nodes.forEach((otherNode, j) => {
          if (i !== j) {
            const dx = node.x - otherNode.x;
            const dy = node.y - otherNode.y;
            const distance = Math.sqrt(dx * dx + dy * dy);

            if (distance > 0 && distance < 100) {
              const force = 50 / (distance * distance);
              node.x += dx * force * 0.1;
              node.y += dy * force * 0.1;
            }
          }
        });
      });
    },

    applyCircularLayout() {
      const centerX = this.canvasWidth / 2 + this.offsetX;
      const centerY = this.canvasHeight / 2 + this.offsetY;
      const radius = 150;

      this.graphData.nodes.forEach((node, i) => {
        if (node.id === 'node1') {
          node.x = centerX;
          node.y = centerY;
        } else {
          const angle = ((i - 1) / (this.graphData.nodes.length - 1)) * Math.PI * 2;
          node.x = centerX + Math.cos(angle) * radius;
          node.y = centerY + Math.sin(angle) * radius;
        }
      });
    },

    applyTreeLayout() {
      const centerX = this.canvasWidth / 2 + this.offsetX;
      const centerY = this.canvasHeight / 2 + this.offsetY;

      this.graphData.nodes.forEach((node, i) => {
        if (node.id === 'node1') {
          node.x = centerX;
          node.y = centerY;
        } else {
          const angle = ((i - 1) / (this.graphData.nodes.length - 1)) * Math.PI * 2;
          const radius = 120;
          node.x = centerX + Math.cos(angle) * radius;
          node.y = centerY + Math.sin(angle) * radius;
        }
      });
    },

    drawGraph() {
      if (!this.ctx) return;

      // 清空画布
      this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);

      // 设置缩放
      this.ctx.scale(this.scale, this.scale);

      // 绘制连接线
      this.drawLinks();

      // 绘制节点
      this.drawNodes();

      // 绘制标签
      if (this.showLabels) {
        this.drawLabels();
      }

      this.ctx.draw();
    },

    drawLinks() {
      this.graphData.links.forEach(link => {
        const sourceNode = this.graphData.nodes.find(n => n.id === link.source);
        const targetNode = this.graphData.nodes.find(n => n.id === link.target);

        if (sourceNode && targetNode) {
          const isHighlighted = this.isLinkHighlighted(link);

          this.ctx.beginPath();
          this.ctx.moveTo(sourceNode.x, sourceNode.y);
          this.ctx.lineTo(targetNode.x, targetNode.y);
          this.ctx.strokeStyle = isHighlighted ? '#ff6b6b' : '#cccccc';
          this.ctx.lineWidth = isHighlighted ? 3 : 2;
          this.ctx.stroke();
        }
      });
    },

    drawNodes() {
      this.graphData.nodes.forEach(node => {
        const isSelected = this.selectedNode && this.selectedNode.id === node.id;
        const isHovered = this.hoveredNode && this.hoveredNode.id === node.id;
        const isHighlighted = isSelected || isHovered || this.isNodeConnected(node);

        // 绘制节点
        this.ctx.beginPath();
        this.ctx.arc(node.x, node.y, isHighlighted ? node.size + 3 : node.size, 0, 2 * Math.PI);
        this.ctx.fillStyle = node.color;
        this.ctx.fill();

        // 绘制边框
        this.ctx.strokeStyle = isHighlighted ? '#ffffff' : node.color;
        this.ctx.lineWidth = isHighlighted ? 3 : 2;
        this.ctx.stroke();
      });
    },

    drawLabels() {
      this.graphData.nodes.forEach(node => {
        this.ctx.setFontSize(12);
        this.ctx.setFillStyle('#333333');
        this.ctx.setTextAlign('center');
        this.ctx.fillText(node.name, node.x, node.y - node.size - 8);
      });
    },

    isNodeConnected(node) {
      if (!this.selectedNode && !this.hoveredNode) return false;

      const targetNode = this.selectedNode || this.hoveredNode;
      return this.graphData.links.some(link =>
          (link.source === node.id && link.target === targetNode.id) ||
          (link.target === node.id && link.source === targetNode.id)
      );
    },

    isLinkHighlighted(link) {
      if (!this.selectedNode && !this.hoveredNode) return false;

      const targetNode = this.selectedNode || this.hoveredNode;
      return link.source === targetNode.id || link.target === targetNode.id;
    },

    getNodeDegree(node) {
      return this.graphData.links.filter(link =>
          link.source === node.id || link.target === node.id
      ).length;
    },

    onTouchStart(e) {
      this.isDragging = true;
      this.dragStartX = e.touches[0].x;
      this.dragStartY = e.touches[0].y;

      // 检测点击的节点
      const touchX = e.touches[0].x / this.scale;
      const touchY = e.touches[0].y / this.scale;

      this.selectedNode = this.getNodeAtPosition(touchX, touchY);
      if (this.selectedNode) {
        uni.vibrateShort(); // 触觉反馈
      }
    },

    onTouchMove(e) {
      if (!this.isDragging) return;

      const touchX = e.touches[0].x;
      const touchY = e.touches[0].y;

      if (this.selectedNode) {
        // 拖动节点
        this.selectedNode.x = touchX / this.scale;
        this.selectedNode.y = touchY / this.scale;
      } else {
        // 拖动画布
        const deltaX = touchX - this.dragStartX;
        const deltaY = touchY - this.dragStartY;

        this.offsetX += deltaX;
        this.offsetY += deltaY;

        this.dragStartX = touchX;
        this.dragStartY = touchY;
      }
    },

    onTouchEnd() {
      this.isDragging = false;
      this.hoveredNode = null;
    },

    getNodeAtPosition(x, y) {
      for (let i = this.graphData.nodes.length - 1; i >= 0; i--) {
        const node = this.graphData.nodes[i];
        const distance = Math.sqrt(Math.pow(node.x - x, 2) + Math.pow(node.y - y, 2));
        if (distance <= node.size + 5) {
          return node;
        }
      }
      return null;
    },

    onLayoutChange(e) {
      this.layoutIndex = parseInt(e.detail.value);
      this.resetView();
    },

    toggleLabels(e) {
      this.showLabels = e.detail.value;
    },

    addRandomNode() {
      const nodeTypes = ['user', 'product', 'category', 'service'];
      const colors = ['#4ecdc4', '#45b7d1', '#96ceb4', '#feca57'];

      const newNode = {
        id: `node${this.graphData.nodes.length + 1}`,
        name: `新节点${this.graphData.nodes.length}`,
        type: nodeTypes[Math.floor(Math.random() * nodeTypes.length)],
        x: Math.random() * this.canvasWidth,
        y: Math.random() * this.canvasHeight,
        size: 12 + Math.random() * 8,
        color: colors[Math.floor(Math.random() * colors.length)]
      };

      this.graphData.nodes.push(newNode);

      // 连接到随机现有节点
      if (this.graphData.nodes.length > 1) {
        const randomIndex = Math.floor(Math.random() * (this.graphData.nodes.length - 1));
        this.graphData.links.push({
          source: newNode.id,
          target: this.graphData.nodes[randomIndex].id
        });
      }
    },

    resetView() {
      this.offsetX = 0;
      this.offsetY = 0;
      this.scale = 1;
      this.selectedNode = null;
    },

    deselectNode() {
      this.selectedNode = null;
    },

    exportData() {
      const exportData = {
        nodes: this.graphData.nodes,
        links: this.graphData.links
      };

      uni.showModal({
        title: '导出数据',
        content: JSON.stringify(exportData, null, 2),
        showCancel: false,
        confirmText: '确定'
      });
    }
  }
}
</script>

<style scoped>
.graph-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: #f5f5f5;
}

.canvas-wrapper {
  flex: 1;
  background: white;
  position: relative;
}

.graph-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.control-panel {
  background: white;
  padding: 20rpx;
  border-top: 1rpx solid #eee;
}

.control-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20rpx;
  padding: 15rpx 0;
}

.control-label {
  font-size: 28rpx;
  color: #333;
}

.picker {
  background: #f0f0f0;
  padding: 10rpx 20rpx;
  border-radius: 8rpx;
  font-size: 26rpx;
}

.button-group {
  display: flex;
  justify-content: space-between;
}

.btn {
  flex: 1;
  margin: 0 10rpx;
  padding: 20rpx;
  border: none;
  border-radius: 8rpx;
  font-size: 26rpx;
}

.btn.primary {
  background: #4ecdc4;
  color: white;
}

.btn.secondary {
  background: #45b7d1;
  color: white;
}

.btn.warning {
  background: #ff6b6b;
  color: white;
}

.node-info-panel {
  position: absolute;
  top: 20rpx;
  right: 20rpx;
  background: rgba(255, 255, 255, 0.95);
  padding: 30rpx;
  border-radius: 16rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
  max-width: 300rpx;
  z-index: 100;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20rpx;
}

.panel-title {
  font-size: 30rpx;
  font-weight: bold;
  color: #333;
}

.close-btn {
  font-size: 40rpx;
  color: #999;
  cursor: pointer;
}

.node-details {
  margin-bottom: 20rpx;
}

.detail-item {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10rpx;
}

.detail-label {
  font-size: 26rpx;
  color: #666;
}

.detail-value {
  font-size: 26rpx;
  color: #333;
  font-weight: 500;
}

.stats-panel {
  position: absolute;
  bottom: 20rpx;
  left: 20rpx;
  background: rgba(0, 0, 0, 0.7);
  padding: 20rpx;
  border-radius: 8rpx;
  z-index: 100;
}

.stat-item {
  display: block;
  color: white;
  font-size: 24rpx;
  margin-bottom: 5rpx;
}
</style>
