<script lang="ts">
	import { onMount } from 'svelte';
	import type { ParticlesConfig } from '../types';

	interface Props { config: ParticlesConfig; width: number; height: number; }
	let { config, width, height }: Props = $props();

	let canvasEl: HTMLCanvasElement;
	let animFrameId: number;

	interface Particle { x: number; y: number; vx: number; vy: number; size: number; opacity: number; }
	let particles: Particle[] = [];

	function initParticles() {
		particles = [];
		for (let i = 0; i < config.count; i++) particles.push(createParticle(true));
	}

	function createParticle(randomPosition: boolean): Particle {
		const size = config.minSize + Math.random() * (config.maxSize - config.minSize);
		const speed = config.speed * 0.5;
		let vx = 0, vy = 0;
		switch (config.direction) {
			case 'up': vx = (Math.random() - 0.5) * speed * 0.3; vy = -speed * (0.3 + Math.random() * 0.7); break;
			case 'down': vx = (Math.random() - 0.5) * speed * 0.3; vy = speed * (0.3 + Math.random() * 0.7); break;
			case 'left': vx = -speed * (0.3 + Math.random() * 0.7); vy = (Math.random() - 0.5) * speed * 0.3; break;
			case 'right': vx = speed * (0.3 + Math.random() * 0.7); vy = (Math.random() - 0.5) * speed * 0.3; break;
			default: { const angle = Math.random() * Math.PI * 2; vx = Math.cos(angle) * speed * (0.3 + Math.random() * 0.7); vy = Math.sin(angle) * speed * (0.3 + Math.random() * 0.7); }
		}
		return {
			x: randomPosition ? Math.random() * width : (config.direction === 'right' ? -size : config.direction === 'left' ? width + size : Math.random() * width),
			y: randomPosition ? Math.random() * height : (config.direction === 'down' ? -size : config.direction === 'up' ? height + size : Math.random() * height),
			vx, vy, size, opacity: config.opacity * (0.3 + Math.random() * 0.7),
		};
	}

	function drawShape(ctx: CanvasRenderingContext2D, p: Particle) {
		ctx.globalAlpha = p.opacity;
		ctx.fillStyle = config.color;
		switch (config.shape) {
			case 'square': ctx.fillRect(p.x - p.size / 2, p.y - p.size / 2, p.size, p.size); break;
			case 'triangle': {
				ctx.beginPath(); ctx.moveTo(p.x, p.y - p.size);
				ctx.lineTo(p.x - p.size * 0.866, p.y + p.size * 0.5);
				ctx.lineTo(p.x + p.size * 0.866, p.y + p.size * 0.5);
				ctx.closePath(); ctx.fill(); break;
			}
			case 'star': {
				ctx.beginPath();
				for (let i = 0; i < 5; i++) {
					const outerAngle = (i * 2 * Math.PI / 5) - Math.PI / 2;
					const innerAngle = outerAngle + Math.PI / 5;
					const outerR = p.size, innerR = p.size * 0.4;
					if (i === 0) ctx.moveTo(p.x + outerR * Math.cos(outerAngle), p.y + outerR * Math.sin(outerAngle));
					else ctx.lineTo(p.x + outerR * Math.cos(outerAngle), p.y + outerR * Math.sin(outerAngle));
					ctx.lineTo(p.x + innerR * Math.cos(innerAngle), p.y + innerR * Math.sin(innerAngle));
				}
				ctx.closePath(); ctx.fill(); break;
			}
			default:
				ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fill();
		}
	}

	function drawConnections(ctx: CanvasRenderingContext2D) {
		if (config.connectDistance <= 0) return;
		const maxDist = config.connectDistance;
		const maxDistSq = maxDist * maxDist;
		ctx.strokeStyle = config.color; ctx.lineWidth = 1;
		for (let i = 0; i < particles.length; i++) {
			for (let j = i + 1; j < particles.length; j++) {
				const dx = particles[i].x - particles[j].x;
				const dy = particles[i].y - particles[j].y;
				const distSq = dx * dx + dy * dy;
				if (distSq < maxDistSq) {
					ctx.globalAlpha = config.opacity * 0.6 * (1 - Math.sqrt(distSq) / maxDist);
					ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y);
					ctx.lineTo(particles[j].x, particles[j].y); ctx.stroke();
				}
			}
		}
	}

	function animate() {
		const ctx = canvasEl?.getContext('2d');
		if (!ctx) return;
		ctx.clearRect(0, 0, width, height);
		drawConnections(ctx);
		const margin = config.maxSize * 2;
		for (const p of particles) {
			p.x += p.vx; p.y += p.vy;
			if (p.x < -margin) p.x = width + margin;
			if (p.x > width + margin) p.x = -margin;
			if (p.y < -margin) p.y = height + margin;
			if (p.y > height + margin) p.y = -margin;
			drawShape(ctx, p);
		}
		ctx.globalAlpha = 1;
		animFrameId = requestAnimationFrame(animate);
	}

	$effect(() => {
		const _ = [config.count, config.color, config.opacity, config.minSize, config.maxSize, config.speed, config.shape, config.connectDistance, config.direction, width, height];
		if (canvasEl) initParticles();
	});

	onMount(() => {
		initParticles();
		animFrameId = requestAnimationFrame(animate);
		return () => { if (animFrameId) cancelAnimationFrame(animFrameId); };
	});
</script>

<canvas bind:this={canvasEl} {width} {height} style="position:absolute;inset:0;pointer-events:none;z-index:0;"></canvas>
