/**
 * Воздушные частицы области сверх атмосферы v1: падающие листья рощи
 * (профиль 'leaves' — медленный листопад с покачиванием, редкий тёплый
 * лист) и мелкая пыль разъезда ('dust' — лёгкая взвесь, еле ползёт).
 * Отдельный слой частиц движка (спрайты-билборды, не воксели): позы —
 * детерминированные синусы от времени цикла (гейт-стабильно); окно
 * видимости камеры с перекрытием, в марже за экраном альфа гаснет —
 * лист/пылинка не «появляется из воздуха».
 */
import type { ParticleLayer, VoxelRenderer } from '@rpg/engine';
import { edgeAlpha, particleWindow, wrap } from './particlesWindow';

/** Профиль воздушных частиц области. */
export type AirFxKind = 'leaves' | 'dust';

/** Параметры профилей: всё в юнитах мира. */
const COUNT = 26; // листьев в кронах рощи
const LEAVES_H = 26; // слой листопада (кроны ~17-23 + запас)
const LEAVES_FALL = 1.1; // скорость падения листа (юнит/сек)
const LEAVES_COLORS = ['#7d8662', '#5e6b4a', '#c9a04a'] as const; // крона + тёплый лист
const DUST_COUNT = 40;
const DUST_H = 18; // слой пыли над разъездом
const DUST_COLOR = '#94949e'; // P6 — пыль в свете
const DUST_ALPHA = 0.32;
const MARGIN = 16; // перекрытие окна за краем кадра

export class AirFx {
    private readonly layer: ParticleLayer;

    constructor(
        private readonly renderer: VoxelRenderer,
        private readonly kind: AirFxKind,
    ) {
        this.layer = renderer.addParticles();
    }

    /** Снапшот для моста: профиль и число частиц. */
    get info(): { kind: AirFxKind; count: number } {
        return { kind: this.kind, count: this.kind === 'leaves' ? COUNT : DUST_COUNT };
    }

    /** Поза слоя: время цикла (окно — из цели камеры рендерера). */
    update(t: number): void {
        const points: { pos: [number, number, number]; color: string; alpha?: number; size?: number }[] = [];
        if (this.kind === 'leaves') {
            const win = particleWindow(this.renderer, 0, LEAVES_H, MARGIN);
            for (let i = 0; i < COUNT; i++) {
                const p = i * 2.399; // золотое сечение: разброс фаз
                // мировой якорь (окно камеры заворачивает) + покачивание падения
                const ax = wrap(i * 67.3 + Math.sin(t * 0.21 + p) * 2.5, win.minX, win.maxX - win.minX);
                const az = wrap(i * 43.9 + Math.cos(t * 0.17 + p * 1.7) * 2.5, win.minZ, win.maxZ - win.minZ);
                // падение: лист опускается и рождается снова в кронах
                const y = LEAVES_H - ((t * (LEAVES_FALL * (0.7 + (i % 4) * 0.2)) + (i * 4.1) % LEAVES_H) % LEAVES_H);
                points.push({
                    pos: [ax + Math.sin(t * 1.1 + p) * 0.8, y, az + Math.cos(t * 0.9 + p) * 0.8],
                    color: LEAVES_COLORS[i % 7 === 6 ? 2 : i % 2]!,
                    alpha: edgeAlpha(win, MARGIN, ax, az),
                    size: 1.4,
                });
            }
        } else {
            const win = particleWindow(this.renderer, 0, DUST_H, MARGIN);
            for (let i = 0; i < DUST_COUNT; i++) {
                const p = i * 2.399;
                // пыль почти стоит на месте: медленный дрейф-взвесь
                const ax = wrap(i * 71.9 + Math.sin(t * 0.07 + p) * 3, win.minX, win.maxX - win.minX);
                const az = wrap(i * 97.3 + Math.cos(t * 0.06 + p * 1.3) * 3, win.minZ, win.maxZ - win.minZ);
                const y = 2 + Math.sin(t * 0.11 + p * 2.3) * (2 + (i % 3) * 3);
                points.push({
                    pos: [ax, y, az],
                    color: DUST_COLOR,
                    alpha: DUST_ALPHA * edgeAlpha(win, MARGIN, ax, az),
                    size: 0.7,
                });
            }
        }
        this.layer.update(points);
    }

    /** Скрыть слой (мост setAsh / область без профиля). */
    hide(): void {
        this.layer.update([]);
    }

    dispose(): void {
        this.layer.dispose();
    }
}