/**
 * Атмосферные частицы (v1 Atmosphere): тёплые мотыльки над лугами
 * (светятся цветом, дрейф по синусу) и дымка прудов (серые полупрозрачные
 * клочья, лежащие на воде). Слои частиц движка (не воксели): мотыльки —
 * спрайты-билборды с окном видимости камеры (с перекрытием и альфой в
 * марже — заворачивание за экраном), дымка — плоские квадраты НА воде,
 * якорящиеся на клетки и медленно бродящие на месте. Позы — детермини-
 * рованные синусы от времени цикла, гейт-стабильно.
 */
import type { ParticleLayer, VoxelRenderer } from '@rpg/engine';
import { edgeAlpha, particleWindow, wrap } from './particlesWindow';
import type { WaterCell } from './waterShimmer';

/** Профиль частиц области. */
export type AmbientKind = 'moths' | 'fog';

const MOTH_COUNT = 16; // окно — весь кадр: чуть больше, чтобы стая читалась
const MOTH_COLORS = ['#f0d878', '#d8b050']; // B-тёплые: только «жизнь»
const FOG_COUNT = 22;
const FOG_COLOR = '#a7b3bd'; // светлее воды — клочья читаются туманом
const FOG_ALPHA = 0.55; // полупрозрачные пятна — как в v1
const FOG_Y = 3.05; // чуть над поверхностью воды (вода — слой y=2, верх y=3)
const MARGIN = 16; // перекрытие окна мотыльков за краем кадра

export class AmbientParticles {
    private readonly layer: ParticleLayer;

    constructor(
        private readonly renderer: VoxelRenderer,
        private readonly kind: AmbientKind,
        /** Клетки поверхности воды — якоря клочьев дымки (только для fog). */
        private readonly cells: readonly WaterCell[] = [],
    ) {
        this.layer = kind === 'fog' ? renderer.addFlatParticles() : renderer.addParticles();
    }

    /** Снапшот для моста: профиль и число частиц (у мотыльков всегда видимы). */
    get info(): { kind: AmbientKind; count: number } {
        return {
            kind: this.kind,
            count: this.kind === 'moths' ? MOTH_COUNT : this.cells.length ? FOG_COUNT : 0,
        };
    }

    /** Поза слоя: время цикла (окно мотыльков — из цели камеры рендерера). */
    update(t: number): void {
        const points: { pos: [number, number, number]; color: string; alpha?: number; size?: number }[] = [];
        if (this.kind === 'moths') {
            const win = particleWindow(this.renderer, 5, 13, MARGIN);
            for (let i = 0; i < MOTH_COUNT; i++) {
                const p = i * 2.399; // золотое сечение: разброс фаз
                // мировой якорь (окно камеры заворачивает) + кружение НА месте:
                // мотылёк не следует за героем — герой летит сквозь стаю
                const ax = wrap(i * 53.1 + Math.sin(t * 0.13 + p) * 4, win.minX, win.maxX - win.minX);
                const az = wrap(i * 89.7 + Math.cos(t * 0.11 + p * 1.7) * 4, win.minZ, win.maxZ - win.minZ);
                // полёт: плавная волна над травой + трепет (герой ~16 юнитов)
                const y = 9 + Math.sin(t * 0.4 + p * 2) * 4.4;
                const x = ax + Math.sin(t * 1.7 + p) * 0.6;
                const z = az + Math.cos(t * 1.5 + p) * 0.6;
                points.push({
                    pos: [x, y, z],
                    color: MOTH_COLORS[i % MOTH_COLORS.length]!,
                    alpha: edgeAlpha(win, MARGIN, x, z),
                });
            }
        } else {
            // клочья дымят НА воде: якорь — клетка поверхности, медленное
            // блуждание на месте (без якорей — клочьев нет: дымка только прудам)
            const n = this.cells.length;
            for (let i = 0; i < FOG_COUNT && n > 0; i++) {
                const p = i * 2.399;
                const a = this.cells[(i * 131) % n]!; // 131 взаимно прост с длиной — разброс
                points.push({
                    pos: [a.x + Math.sin(t * 0.05 + p) * 2.5, FOG_Y, a.z + Math.cos(t * 0.04 + p * 1.3) * 2.5],
                    color: FOG_COLOR,
                    alpha: FOG_ALPHA,
                    size: 2, // клок 2×2, лежит на воде
                });
            }
        }
        this.layer.update(points);
    }

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

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