<template>
  <view class="word-cloud-container">
    <!-- uni-app / 小程序 canvas，2d 模式 -->
    <canvas
      type="2d"
      class="word-cloud-canvas"
      :style="{ width: width + 'px', height: height + 'px' }"
    ></canvas>
  </view>
</template>

<script>
export default {
  name: 'WordCloud',

  props: {
    // 词云数据：[{ text: '关键词', value: 100 }]
    wordData: {
      type: Array,
      default: () => []
    },
    // 画布宽度
    width: {
      type: Number,
      default: 300
    },
    // 画布高度
    height: {
      type: Number,
      default: 300
    },
    // 颜色：外 / 中 / 内
    colorList: {
      type: Array,
      default: () => ['#60A5FA', '#FEC200', '#EF4444']
    }
  },

  data() {
    return {
      ctx: null,        // canvas 2d 上下文
      placedWords: [],  // 已放置文字的包围盒（用于碰撞检测）
      centerWords: [],  // value 最大的 3 个
      otherWords: []    // 其余词
    };
  },

  watch: {
    // 词云数据变化时重绘
    wordData: {
      deep: true,
      handler() {
        this.drawWordCloud();
      }
    }
  },

  mounted() {
    this.initCanvas();
  },

  methods: {
    // 初始化 canvas
    async initCanvas() {
      // 延迟，确保 canvas 已渲染（小程序必需）
      await new Promise(r => setTimeout(r, 50));

      const query = uni.createSelectorQuery().in(this);
      query
        .select('.word-cloud-canvas')
        .fields({ node: true })
        .exec(res => {
          if (!res || !res[0] || !res[0].node) return;

          const canvas = res[0].node;
          const ctx = canvas.getContext('2d');

          // 适配高清屏
          const dpr = uni.getSystemInfoSync().pixelRatio || 1;
          canvas.width = this.width * dpr;
          canvas.height = this.height * dpr;
          ctx.scale(dpr, dpr);

          this.ctx = ctx;
          this.drawWordCloud();
        });
    },

    // 绘制词云
    drawWordCloud() {
      if (!this.ctx || !this.wordData.length) return;

      // 清空画布
      this.ctx.clearRect(0, 0, this.width, this.height);
      this.placedWords = [];

      // 按 value 从大到小排序
      const sorted = [...this.wordData].sort((a, b) => b.value - a.value);

      // 中心层：取前 3 个
      this.centerWords = sorted.slice(0, 3);
      this.otherWords = sorted.slice(3);

      // 先画中心层（优先级最高）
      this.centerWords.forEach((word, index) => {
        this.placeWord(word, 'center', index);
      });

      // 再画中 / 外层
      this.otherWords.forEach(word => {
        this.placeWord(word, 'other');
      });
	    // ⭐ 通知父组件：canvas 绘制完成
	    this.$emit('rendered');
    },

    // 放置单个词
    placeWord(word, type, index = 0) {
      const ctx = this.ctx;
      const text = word.text || word.name;
      const maxAttempts = 200;

      let fontSize = 24;
      let layer = 'middle';

      // 根据层级设置字号
      if (type === 'center') {
        fontSize = 32;        // ⭐ 最中间三个：32px
        layer = 'center';
      } else {
        layer = Math.random() > 0.5 ? 'middle' : 'outer';
        fontSize = layer === 'outer' ? 18 : 24;
      }

      // 随机两档旋转角度
      const angleLimit =
        Math.random() > 0.5
          ? 60 * Math.PI / 180
          : 30 * Math.PI / 180;

      const angle = (Math.random() - 0.5) * 2 * angleLimit;

      // 设置字体（全部加粗）
      ctx.font = `bold ${fontSize}px sans-serif`;

      // 测量文字尺寸
      const textWidth = ctx.measureText(text).width;
      const textHeight = fontSize * 1.05;

      for (let i = 0; i < maxAttempts; i++) {
        let x, y;

        // ===== 中心层：固定布局，保证 3 个一定能放下 =====
        if (layer === 'center') {
          const centerX = this.width / 2;
          const centerY = this.height / 2;

          // 中心三个固定位置
          const offsets = [
            { x: 0, y: 0 },      // 最大的，正中
            { x: -80, y: 0 },   // 左
            { x: 80, y: 0 }     // 右
          ];

          const pos = offsets[index] || offsets[0];
          x = centerX + pos.x;
          y = centerY + pos.y;
        } else {
          // 中 / 外层：随机位置
          x = this.width * 0.05 + Math.random() * this.width * 0.9;
          y = this.height * 0.05 + Math.random() * this.height * 0.9;
        }

        // 计算旋转后的包围盒
        const rect = this.getBoundingRect(
          x,
          y,
          textWidth,
          textHeight,
          angle,
          2
        );

        // 外层：超出画布直接跳过，继续尝试
        if (layer === 'outer') {
          if (
            rect.left < 0 ||
            rect.right > this.width ||
            rect.top < 0 ||
            rect.bottom > this.height
          ) {
            continue;
          }
        }

        // 碰撞检测
        if (this.checkOverlapRect(rect)) continue;

        // 根据层级设置颜色
        ctx.fillStyle =
          layer === 'center'
            ? this.colorList[2]
            : layer === 'middle'
            ? this.colorList[1]
            : this.colorList[0];

        // 绘制文字
        this.drawText(text, x, y, angle);

        // 保存包围盒
        this.placedWords.push({ rect });
        break;
      }
    },

    // 碰撞检测（AABB）
    checkOverlapRect(current) {
      for (const item of this.placedWords) {
        const r = item.rect;
        if (
          current.left < r.right &&
          current.right > r.left &&
          current.top < r.bottom &&
          current.bottom > r.top
        ) {
          return true;
        }
      }
      return false;
    },

    // 计算旋转后的包围盒
    getBoundingRect(x, y, width, height, angle, gap = 2) {
      const cos = Math.cos(angle);
      const sin = Math.sin(angle);

      const halfW = (width - gap) / 2;
      const halfH = (height - gap) / 2;

      const points = [
        { x: -halfW, y: -halfH },
        { x: halfW, y: -halfH },
        { x: halfW, y: halfH },
        { x: -halfW, y: halfH }
      ];

      const xs = [];
      const ys = [];

      points.forEach(p => {
        xs.push(x + p.x * cos - p.y * sin);
        ys.push(y + p.x * sin + p.y * cos);
      });

      return {
        left: Math.min(...xs),
        right: Math.max(...xs),
        top: Math.min(...ys),
        bottom: Math.max(...ys)
      };
    },

    // 绘制文字
    drawText(text, x, y, angle) {
      const ctx = this.ctx;
      ctx.save();
      ctx.translate(x, y);
      ctx.rotate(angle);
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(text, 0, 0);
      ctx.restore();
    }
  }
};
</script>

<style scoped>
.word-cloud-container {
  width: 100%;
  height: 100%;
}

.word-cloud-canvas {
  width: 100%;
  height: 100%;
}
</style>
