// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::f32::consts::TAU;

use super::waves::Waves;

const AMPLITUDE: f32 = 0.5;
const TIME: f32 = 0.75;
const FADE_IN: f32 = TIME / 32.0;

const GROUP_COUNT: usize = 3;
const NOTES_PER_GROUP: usize = 3;
const NOTE_COUNT: usize = GROUP_COUNT * NOTES_PER_GROUP;
const NOTE_TIME: f32 = TIME / NOTE_COUNT as f32;
const NOTE_FADE_IN: f32 = 0.125;
const NOTE_GROUPING: f32 = 1.25;

const GROUP_LEN: f32 = ((NOTES_PER_GROUP - 1) as f32 / NOTE_GROUPING + 1.0) * NOTE_TIME;
const GROUP_SPACING_MUL: f32 = (TIME - GROUP_LEN) / (NOTES_PER_GROUP * (GROUP_COUNT - 1)) as f32 / NOTE_TIME;

const NOTE_TIME_MUL: f32 = 1.75;

struct Note {
	freq: f32,
	start: f32,
}

fn envelope(t: f32) -> f32 {
	let mut amplitude = 1.0;
	amplitude *= 1.0 - (1.0 - (t / NOTE_FADE_IN).min(1.0)).powi(2); // Fade in
	amplitude *= (1.0 - t).max(0.0).powi(2);
	amplitude
}

pub fn generate() {
	let mut notes = Vec::with_capacity(NOTE_COUNT);
	for i in 0..GROUP_COUNT {
		for j in 0..NOTES_PER_GROUP {
			let freq = 550.0 * 2.0f32.powf(i as f32 * 0.0625 + j as f32 * 0.25);
			let start = ((i * NOTES_PER_GROUP) as f32 * GROUP_SPACING_MUL + j as f32 / NOTE_GROUPING) * NOTE_TIME;
			notes.push(Note { freq, start });
		}
	}

	super::generate("forcefield", TIME, AMPLITUDE, |t| {
		let mut wave = 0.0;
		for note in &notes {
			let note_t = (t - note.start) / (NOTE_TIME * NOTE_TIME_MUL);
			if !(0.0..=1.0).contains(&note_t) { continue; }

			for i in 0..4 {
				let amplitude = envelope(note_t).powf(1.0 + i as f32 * 0.75) / (i as f32 + 1.0).powi(2);
				wave += (t * (i + 1) as f32 * note.freq * TAU).smooth_square(0.5625 + i as f32 * 0.25) * amplitude;
			}
		}

		let mut amplitude = 1.0 - (1.0 - (t / FADE_IN).min(1.0)).powi(2); // Fade in
		amplitude *= 1.0 - t / TIME; // Fade out
		wave * amplitude
	});
}
