<template>
  <div class="particle-logo-container" :style="{ backgroundColor: backgroundColor }">
    <canvas ref="canvas" class="particle-canvas"></canvas>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';

/**
 * 粒子类，用于存储粒子相关属性。
 * 非响应式，避免 Vue 的响应式性能开销。
 */
class Particle {
  // 粒子的初始位置（原点坐标）
  public originalX: number;
  public originalY: number;
  public originalZ: number;

  // 粒子的当前坐标
  public currentX: number;
  public currentY: number;
  public currentZ: number;

  // 粒子的散开目标坐标（可选）
  public scatterX?: number;
  public scatterY?: number;
  public scatterZ?: number;

  // 粒子的透明度、大小以及是否为边缘粒子
  public alpha: number;
  public size: number;
  public isEdge: boolean;

  constructor(oX: number, oY: number, oZ: number, size: number, isEdge: boolean) {
    this.originalX = oX;
    this.originalY = oY;
    this.originalZ = oZ;

    this.currentX = oX;
    this.currentY = oY;
    this.currentZ = oZ;

    this.alpha = 1;
    this.size = size;
    this.isEdge = isEdge;
  }
}

// 定义组件的 Props
const props = defineProps({
  imageUrl: {
    type: String,
    required: true, // 必须提供图片路径
  },
  canvasSize: {
    type: Number,
    default: 600, // 画布的宽高，单位像素
  },
  imageSize: {
    type: Number,
    default: null, // 默认为 null 表示自动适配 canvas 的大小 （width/2）
  },
  perspective: {
    type: Number,
    default: 400, // 透视深度，越大越平面，这个平面是整效果上的平面
  },
  particleSize: {
    type: Number,
    default: 1.2, // 粒子的基础大小
  },
  depthRange: {
    type: Number,
    default: 50, // 默认 Z 轴范围为 -50 到 +50
  },
  backgroundColor: {
    type: String,
    default: 'black', // 背景颜色
  },
  particleColor: {
    type: String,
    default: 'rgba(255, 255, 255, 1)', // 普通粒子颜色
  },
  edgeColor: {
    type: String,
    default: 'rgba(255, 255, 255, 1)', // 边缘粒子颜色
  },
  rotationDuration: {
    type: Number,
    default: 15000, // 粒子一圈旋转的时间，单位毫秒
  },
  particleStep: {
    type: Number,
    default: 2, // 粒子稀疏度，值越大，粒子越少
  },
  advancedEdgeDetection: {
    type: Boolean,
    default: true, // 是否启用高级边缘检测
  },
  edgeAlphaThreshold: {
    type: Number,
    default: 200, // 边缘透明度阈值，用于判断边缘粒子
  },
  mouseMoveEffect: {
    type: Boolean,
    default: false, // 是否启用鼠标移动效果
  },
  mouseSensitivity: {
    type: Number,
    default: 0.002, // 鼠标移动的灵敏度
  },
  mouseMoveMode: {
    type: String,
    default: 'canvas', // 鼠标移动的影响范围（整个画布或整体窗口）
  },
  openChangeAnimation: {
    type: Boolean,
    default: false, // 是否开启粒子散开动画
  },
  transitionTotalDuration: {
    type: Number,
    default: 1500, // 过渡时间，单位毫秒
  },
});

// Canvas 引用，用于获取 DOM 元素
const canvas = ref<HTMLCanvasElement | null>(null);

// 动画帧 ID，用于取消动画
let animationId: number | null = null;

// 粒子数组，用于存储当前的粒子
let particles: Particle[] = [];
let oldParticles: Particle[] = []; // 旧粒子（散开效果用）
let newParticles: Particle[] = []; // 新粒子（聚合效果用）

// 过渡状态
let transitionInProgress = false; // 是否正在过渡
let transitionProgress = 0; // 过渡的进度

// 是否允许旋转
let rotationEnabled = true;

// 鼠标位置
let mouseX = 0;
let mouseY = 0;

// 清理画布和动画帧
const clearCanvas = () => {
  if (canvas.value) {
    const ctx = canvas.value.getContext('2d');
    if (ctx) {
      ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
    }
  }
  if (animationId !== null) {
    cancelAnimationFrame(animationId);
    animationId = null;
  }
};

// ---------------------- 边缘检测 ----------------------
const isEdgeSimple = (alpha: number, threshold: number) => alpha > threshold;

function isEdgeAdvanced(
  x: number,
  y: number,
  imgWidth: number,
  imgHeight: number,
  imageData: Uint8ClampedArray,
  step: number,
) {
  const index = (y * imgWidth + x) * 4 + 3;
  const alpha = imageData[index];
  const checkOffset = (dx: number, dy: number) => {
    const nx = x + dx;
    const ny = y + dy;
    if (nx < 0 || nx >= imgWidth || ny < 0 || ny >= imgHeight) return false;
    const neighborAlpha = imageData[(ny * imgWidth + nx) * 4 + 3];
    return Math.abs(alpha - neighborAlpha) > 50;
  };
  return checkOffset(step, 0) || checkOffset(0, step);
}

// ---------------------- 初始化 ----------------------
function initParticleEffect() {
  clearCanvas();

  const canvasEl = canvas.value;
  if (!canvasEl) return;
  const ctx = canvasEl.getContext('2d');
  if (!ctx) return;

  const width = props.canvasSize;
  const height = props.canvasSize;
  canvasEl.width = width;
  canvasEl.height = height;

  const logoImage = new Image();
  logoImage.src = props.imageUrl;
  logoImage.onload = () => {
    // 绘制 Logo 到画布
    const maxLogoWidth = width / 2;
    const imgWidth = props.imageSize ? props.imageSize : maxLogoWidth;
    // 保持图片宽高比
    const imgHeight = (logoImage.height / logoImage.width) * imgWidth;
    // 绘制图片
    ctx.drawImage(logoImage, (width - imgWidth) / 2, (height - imgHeight) / 2, imgWidth, imgHeight);

    // 获取像素
    const imageData = ctx.getImageData(
      (width - imgWidth) / 2,
      (height - imgHeight) / 2,
      imgWidth,
      imgHeight,
    ).data;

    const step = props.particleStep;
    const freshParticles: Particle[] = [];

    // 遍历图片像素，生成粒子
    for (let y = 0; y < imgHeight; y += step) {
      for (let x = 0; x < imgWidth; x += step) {
        const alpha = imageData[(y * imgWidth + x) * 4 + 3];
        if (alpha > 50) {
          // 如果像素透明度大于 50，则认为是边缘像素
          let isEdgeParticle = false;
          if (props.advancedEdgeDetection) {
            // 如果启用高级边缘检测，则使用 isEdgeAdvanced 函数判断是否为边缘像素
            isEdgeParticle = isEdgeAdvanced(x, y, imgWidth, imgHeight, imageData, step);
          } else {
            isEdgeParticle = isEdgeSimple(alpha, props.edgeAlphaThreshold);
          }
          // 计算粒子的原点坐标
          const oX = x - imgWidth / 2;
          const oY = y - imgHeight / 2;
          // 使用 depthRange 控制 Z 坐标范围
          const oZ = Math.random() * props.depthRange - props.depthRange / 2;
          // 生成粒子
          freshParticles.push(new Particle(oX, oY, oZ, props.particleSize, isEdgeParticle));
        }
      }
    }

    if (particles.length > 0 && props.openChangeAnimation) {
      // 已存在 => 散开过渡
      oldParticles = particles;
      newParticles = freshParticles;
      startScatterTransition();
    } else {
      // 第一次
      particles = freshParticles;
      startRenderLoop();
    }
  };

  logoImage.onerror = () => {
    console.error('Failed to load imageUrl:', logoImage.src);
  };
}

// ---------------------- 散开并禁用旋转 ----------------------
function startScatterTransition() {
  transitionInProgress = true;
  transitionProgress = 0;

  // 过渡期间禁用旋转
  rotationEnabled = false;

  // 给旧粒子散开坐标
  oldParticles.forEach((p) => {
    const angle = Math.random() * Math.PI * 2;
    const radius = 200 + Math.random() * 200;
    const randomZ = 80 + Math.random() * 120;

    // 计算粒子的散开坐标
    p.scatterX = p.currentX + Math.cos(angle) * radius;
    p.scatterY = p.currentY + Math.sin(angle) * radius;
    p.scatterZ = p.currentZ + (Math.random() > 0.5 ? randomZ : -randomZ);
    p.alpha = 1;
  });

  // 给新粒子散开起点
  newParticles.forEach((p) => {
    const angle = Math.random() * Math.PI * 2;
    const radius = 200 + Math.random() * 200;
    const randomZ = 80 + Math.random() * 120;

    // 计算粒子的散开坐标
    p.scatterX = p.currentX + Math.cos(angle) * radius;
    p.scatterY = p.currentY + Math.sin(angle) * radius;
    p.scatterZ = p.currentZ + (Math.random() > 0.5 ? randomZ : -randomZ);

    // 立刻把当前坐标移动到散开处
    p.currentX = p.scatterX;
    p.currentY = p.scatterY;
    p.currentZ = p.scatterZ;

    p.alpha = 0;
  });

  startRenderLoop();
}

// ---------------------- 渲染循环 ----------------------
function startRenderLoop() {
  if (animationId !== null) {
    cancelAnimationFrame(animationId);
    animationId = null;
  }

  const canvasEl = canvas.value;
  if (!canvasEl) return;
  const ctx = canvasEl.getContext('2d');
  if (!ctx) return;

  const width = canvasEl.width;
  const height = canvasEl.height;
  const centerX = width / 2;
  const centerY = height / 2;

  let rotationY = 0;
  const baseRotationSpeed = (2 * Math.PI) / props.rotationDuration;

  let lastTimestamp: number | null = null;

  // ====  缓动函数 (easeInOutQuad) ====
  function easeInOutQuad(t: number) {
    return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
  }

  const renderFrame = (timestamp: number) => {
    if (!lastTimestamp) lastTimestamp = timestamp;
    const deltaTime = timestamp - lastTimestamp;
    lastTimestamp = timestamp;

    // 1) 每帧都先清理画布, 避免残影
    ctx.clearRect(0, 0, width, height);

    // 2) 若 rotationEnabled，就更新旋转
    if (rotationEnabled) {
      rotationY += baseRotationSpeed * deltaTime;
      if (rotationY >= 2 * Math.PI) rotationY -= 2 * Math.PI;
    }
    // 也可加判断：若不启用旋转，则 rotationY 不变

    // 3) 鼠标水平带来的额外旋转(可选)
    const rotationOffsetY = rotationEnabled ? mouseX * props.mouseSensitivity : 0;
    const finalRotationY = rotationY + rotationOffsetY;

    const cosY = Math.cos(finalRotationY);
    const sinY = Math.sin(finalRotationY);

    const perspective = props.perspective;

    // 4) 如果在散开/聚合过渡中
    if (transitionInProgress) {
      transitionProgress += deltaTime;
      let linearT = transitionProgress / props.transitionTotalDuration;
      if (linearT > 1) linearT = 1;

      // ====  使用 easeInOutQuad 缓动 ====
      const t = easeInOutQuad(linearT);

      // 定义散开与聚合的分段
      const half = 0.5;

      // 旧粒子：前半散开 / 后半淡出
      oldParticles.forEach((p) => {
        if (p.scatterX == null) return;
        if (t <= half) {
          const scatterProgress = t / half;
          // 计算粒子的当前坐标
          p.currentX = lerp(p.currentX, p.scatterX, scatterProgress);
          p.currentY = lerp(p.currentY, p.scatterY!, scatterProgress);
          p.currentZ = lerp(p.currentZ, p.scatterZ!, scatterProgress);
        } else {
          // 后半段淡出
          const fadeOut = (t - half) / (1 - half);
          p.alpha = 1 - fadeOut;
        }
      });

      // 新粒子：前半保持散开起点，后半聚合回 original
      newParticles.forEach((p) => {
        if (p.scatterX == null) return;
        if (t <= half) {
          // 慢慢显现
          p.alpha = t * 0.5;
        } else {
          const gatherProgress = (t - half) / (1 - half);
          // 计算粒子的当前坐标
          p.currentX = lerp(p.scatterX, p.originalX, gatherProgress);
          p.currentY = lerp(p.scatterY!, p.originalY, gatherProgress);
          p.currentZ = lerp(p.scatterZ!, p.originalZ, gatherProgress);
          p.alpha = gatherProgress;
        }
      });

      // 如果动画结束
      if (linearT >= 1) {
        transitionInProgress = false;
        oldParticles = [];
        particles = newParticles;
        newParticles = [];

        // 恢复旋转
        rotationEnabled = true;
      }
    }

    // 5) 绘制 old + particles + new
    drawParticles(ctx, oldParticles, width, height, centerX, centerY, cosY, sinY, perspective);
    drawParticles(ctx, particles, width, height, centerX, centerY, cosY, sinY, perspective);
    drawParticles(ctx, newParticles, width, height, centerX, centerY, cosY, sinY, perspective);

    // 6) 循环
    animationId = requestAnimationFrame(renderFrame);
  };

  animationId = requestAnimationFrame(renderFrame);
}

/** 绘制粒子 */
function drawParticles(
  ctx: CanvasRenderingContext2D,
  list: Particle[],
  width: number,
  height: number,
  centerX: number,
  centerY: number,
  cosY: number,
  sinY: number,
  perspective: number,
) {
  for (const p of list) {
    if (p.alpha <= 0) continue; // 透明则跳过

    // 做 3D 旋转 + 透视投影
    const tempX = p.currentX * cosY - p.currentZ * sinY;
    const tempZ = p.currentX * sinY + p.currentZ * cosY;
    const scale = perspective / (perspective - tempZ);
    const screenX = centerX + tempX * scale;
    const screenY = centerY + p.currentY * scale;

    // 绘制粒子
    ctx.beginPath();
    // 绘制圆形
    ctx.arc(screenX, screenY, p.size * scale, 0, Math.PI * 2);
    // 设置颜色
    ctx.fillStyle = p.isEdge ? props.edgeColor : props.particleColor;
    // 设置透明度
    ctx.globalAlpha = p.alpha;
    // 填充
    ctx.fill();
  }
  // 恢复透明度
  ctx.globalAlpha = 1;
}

/** 简单的线性插值 */
function lerp(a: number, b: number, t: number) {
  return a + (b - a) * t;
}

// ---------------------- 鼠标移动 ----------------------
function handleMouseMove(e: MouseEvent) {
  // 获取鼠标位置
  const canvasEl = canvas.value;
  if (!canvasEl) return;
  const rect = canvasEl.getBoundingClientRect();
  const width = canvasEl.width;
  const height = canvasEl.height;

  const cx = rect.left + width / 2;
  const cy = rect.top + height / 2;

  mouseX = e.clientX - cx;
  mouseY = e.clientY - cy;
}

const openMouseMoveEffect = () => {
  // 开启鼠标移动效果
  if (props.mouseMoveEffect) {
    if (props.mouseMoveMode === 'overall') {
      // 监听整个窗口的鼠标移动
      window.addEventListener('mousemove', handleMouseMove);
    } else {
      const canvasEl = canvas.value;
      if (canvasEl) {
        canvasEl.addEventListener('mousemove', handleMouseMove);
      }
    }
  }
};
const closeMouseMoveEffect = () => {
  // 关闭鼠标移动效果
  if (props.mouseMoveEffect) {
    if (props.mouseMoveMode === 'overall') {
      window.removeEventListener('mousemove', handleMouseMove);
    } else {
      const canvasEl = canvas.value;
      if (canvasEl) {
        canvasEl.removeEventListener('mousemove', handleMouseMove);
      }
    }
  }
};

defineExpose({
  openMouseMoveEffect,
  closeMouseMoveEffect,
});

// 挂载时初始化粒子效果并开启鼠标移动监听
onMounted(() => {
  initParticleEffect();
  openMouseMoveEffect();
});

// 组件卸载前清理画布和事件监听
onBeforeUnmount(() => {
  clearCanvas();
  closeMouseMoveEffect();
});

// 控制初始化标识
let isInitialized = ref(false);
watchEffect(() => {
  // 监听 props 的变化，重新初始化粒子效果,使用_式解构命名避免触发ts报错
  const {
    imageUrl: _imageUrl,
    canvasSize: _canvasSize,
    imageSize: _imageSize,
    perspective: _perspective,
    particleSize: _particleSize,
    particleColor: _particleColor,
    edgeColor: _edgeColor,
    rotationDuration: _rotationDuration,
    particleStep: _particleStep,
    advancedEdgeDetection: _advancedEdgeDetection,
    edgeAlphaThreshold: _edgeAlphaThreshold,
    depthRange: _depthRange,
  } = props;

  if (isInitialized.value) {
    // 属性变化时触发的逻辑
    initParticleEffect(); // 重新初始化
  } else {
    // 首次运行，不触发散射特效，仅完成标记
    isInitialized.value = true;
  }
});
</script>

<style scoped>
/* 容器样式 */
.particle-logo-container {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

.particle-canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* 确保 canvas 在高分屏下显示清晰 */
  image-rendering: crisp-edges;
}
</style>
