import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
import type { PitchLock } from './base/SynthEngine';

interface Form1Params {
	frequency: number;
	vibratoRate: number;
	vibratoDepth: number;
	formant1Freq: number;
	formant1BW: number;
	formant1Amp: number;
	formant2Freq: number;
	formant2BW: number;
	formant2Amp: number;
	formant3Freq: number;
	formant3BW: number;
	formant3Amp: number;
	formant4Freq: number;
	formant4BW: number;
	formant4Amp: number;
	formant5Freq: number;
	formant5BW: number;
	formant5Amp: number;
	attack: number;
	decay: number;
	sustain: number;
	release: number;
}

export class Form1 extends CsoundEngine<Form1Params> {
	getName(): string {
		return 'Throat';
	}

	getDescription(): string {
		return 'Multi-voice formant vocal synthesizer with deep sub-bass and wide stereo field';
	}

	getType(): 'generative' | 'sample' | 'input' {
		return 'generative';
	}

	getCategory(): 'Additive' | 'Subtractive' | 'FM' | 'Percussion' | 'Noise' | 'Physical' | 'Modulation' | 'Experimental' | 'Utility' {
		return 'Physical';
	}

	getOrchestra(): string {
		return `
instr 1
	ifreq = chnget("frequency")
	iamp = 0.3
	ivibratoRate = chnget("vibratoRate")
	ivibratoDepth = chnget("vibratoDepth")

	if1freq = chnget("formant1Freq")
	if1bw = chnget("formant1BW")
	if1amp = chnget("formant1Amp")
	if2freq = chnget("formant2Freq")
	if2bw = chnget("formant2BW")
	if2amp = chnget("formant2Amp")
	if3freq = chnget("formant3Freq")
	if3bw = chnget("formant3BW")
	if3amp = chnget("formant3Amp")
	if4freq = chnget("formant4Freq")
	if4bw = chnget("formant4BW")
	if4amp = chnget("formant4Amp")
	if5freq = chnget("formant5Freq")
	if5bw = chnget("formant5BW")
	if5amp = chnget("formant5Amp")

	iatt = chnget("attack") * p3
	idec = chnget("decay") * p3
	isus = chnget("sustain")
	irel = chnget("release") * p3

	; Amplitude envelope
	aenv = madsr(iatt, idec, isus, irel)

	; Dual vibrato LFOs for stereo width
	kvibL = lfo(ivibratoDepth, ivibratoRate)
	kvibR = lfo(ivibratoDepth, ivibratoRate * 1.03)

	; === LEFT CHANNEL VOICES ===
	; Voice 1L: Main voice
	kfreq1L = ifreq * (1 + kvibL)
	inumharm1L = int(sr / (2 * ifreq))
	asig1L = buzz(1, kfreq1L, inumharm1L, -1)

	; Voice 2L: Slightly detuned (0.987x for beating)
	kfreq2L = ifreq * 0.987 * (1 + kvibL * 1.02)
	inumharm2L = int(sr / (2 * ifreq * 0.987))
	asig2L = buzz(0.85, kfreq2L, inumharm2L, -1)

	; Voice 3L: Slightly detuned up (1.013x for beating)
	kfreq3L = ifreq * 1.013 * (1 + kvibL * 0.98)
	inumharm3L = int(sr / (2 * ifreq * 1.013))
	asig3L = buzz(0.8, kfreq3L, inumharm3L, -1)

	; Voice 4L: Octave down
	kfreq4L = ifreq * 0.5 * (1 + kvibL * 1.04)
	inumharm4L = int(sr / (2 * ifreq * 0.5))
	asig4L = buzz(0.65, kfreq4L, inumharm4L, -1)

	; Voice 5L: Octave up
	kfreq5L = ifreq * 2 * (1 + kvibL * 0.96)
	inumharm5L = int(sr / (2 * ifreq * 2))
	asig5L = buzz(0.45, kfreq5L, inumharm5L, -1)

	; === RIGHT CHANNEL VOICES (different detuning) ===
	; Voice 1R: Main voice slightly different
	kfreq1R = ifreq * 1.002 * (1 + kvibR)
	inumharm1R = int(sr / (2 * ifreq * 1.002))
	asig1R = buzz(1, kfreq1R, inumharm1R, -1)

	; Voice 2R: Different detuning (0.993x)
	kfreq2R = ifreq * 0.993 * (1 + kvibR * 1.01)
	inumharm2R = int(sr / (2 * ifreq * 0.993))
	asig2R = buzz(0.85, kfreq2R, inumharm2R, -1)

	; Voice 3R: Different detuning up (1.007x)
	kfreq3R = ifreq * 1.007 * (1 + kvibR * 0.99)
	inumharm3R = int(sr / (2 * ifreq * 1.007))
	asig3R = buzz(0.8, kfreq3R, inumharm3R, -1)

	; Voice 4R: Octave down different phase
	kfreq4R = ifreq * 0.501 * (1 + kvibR * 1.06)
	inumharm4R = int(sr / (2 * ifreq * 0.501))
	asig4R = buzz(0.6, kfreq4R, inumharm4R, -1)

	; Voice 5R: Octave up different phase
	kfreq5R = ifreq * 1.998 * (1 + kvibR * 0.94)
	inumharm5R = int(sr / (2 * ifreq * 1.998))
	asig5R = buzz(0.4, kfreq5R, inumharm5R, -1)

	; Sub-bass: Buzz oscillator one octave below with harmonic content
	kfreqSubL = ifreq * 0.5 * (1 + kvibL * 0.5)
	kfreqSubR = ifreq * 0.502 * (1 + kvibR * 0.5)
	inumharmSubL = int(sr / (2 * ifreq * 0.5))
	inumharmSubR = int(sr / (2 * ifreq * 0.502))
	aSubRawL = buzz(1, kfreqSubL, inumharmSubL, -1)
	aSubRawR = buzz(1, kfreqSubR, inumharmSubR, -1)

	; Apply very wide formants for vocal character with depth
	aSubF1L = butterbp(aSubRawL, if1freq * 0.6, if1bw * 8)
	aSubF2L = butterbp(aSubRawL, if2freq * 0.7, if2bw * 10)
	aSubF3L = butterbp(aSubRawL, if3freq * 0.65, if3bw * 9)
	aSubF1R = butterbp(aSubRawR, if1freq * 0.62, if1bw * 8.5)
	aSubF2R = butterbp(aSubRawR, if2freq * 0.68, if2bw * 10.5)
	aSubF3R = butterbp(aSubRawR, if3freq * 0.67, if3bw * 9.5)

	; Mix sub with formants for depth and complexity
	aSubMixL = (aSubRawL * 0.3) + (aSubF1L * 0.3) + (aSubF2L * 0.25) + (aSubF3L * 0.15)
	aSubMixR = (aSubRawR * 0.3) + (aSubF1R * 0.3) + (aSubF2R * 0.25) + (aSubF3R * 0.15)

	; Add gentle low-pass for warmth and smooth out harsh frequencies
	aSubL = butterlp(aSubMixL, 1200)
	aSubR = butterlp(aSubMixR, 1250)

	; Scale sub-bass
	aSubL = aSubL * 0.8
	aSubR = aSubR * 0.8

	; Mix voices per channel (sub added separately later)
	asigMixL = asig1L + asig2L + asig3L + asig4L + asig5L
	asigMixR = asig1R + asig2R + asig3R + asig4R + asig5R

	; === LEFT CHANNEL FORMANTS ===
	; Main formants
	a1L = butterbp(asigMixL * if1amp, if1freq, if1bw)
	a2L = butterbp(asigMixL * if2amp, if2freq, if2bw)
	a3L = butterbp(asigMixL * if3amp, if3freq, if3bw)
	a4L = butterbp(asigMixL * if4amp, if4freq, if4bw)
	a5L = butterbp(asigMixL * if5amp, if5freq, if5bw)

	; Additional formant layers
	a1bL = butterbp(asigMixL * if1amp * 0.35, if1freq * 1.025, if1bw * 1.12)
	a2bL = butterbp(asigMixL * if2amp * 0.3, if2freq * 0.975, if2bw * 1.18)
	a3bL = butterbp(asigMixL * if3amp * 0.25, if3freq * 1.035, if3bw * 1.1)

	; === RIGHT CHANNEL FORMANTS (different shifts) ===
	; Main formants
	a1R = butterbp(asigMixR * if1amp, if1freq * 1.005, if1bw * 1.02)
	a2R = butterbp(asigMixR * if2amp, if2freq * 0.995, if2bw * 1.03)
	a3R = butterbp(asigMixR * if3amp, if3freq * 1.008, if3bw * 1.01)
	a4R = butterbp(asigMixR * if4amp, if4freq * 0.997, if4bw * 1.04)
	a5R = butterbp(asigMixR * if5amp, if5freq * 1.003, if5bw * 1.02)

	; Additional formant layers
	a1bR = butterbp(asigMixR * if1amp * 0.3, if1freq * 0.98, if1bw * 1.15)
	a2bR = butterbp(asigMixR * if2amp * 0.28, if2freq * 1.022, if2bw * 1.2)
	a3bR = butterbp(asigMixR * if3amp * 0.22, if3freq * 0.97, if3bw * 1.12)

	; Combine formants per channel
	asigOutL = a1L + a2L + a3L + a4L + a5L + a1bL + a2bL + a3bL
	asigOutR = a1R + a2R + a3R + a4R + a5R + a1bR + a2bR + a3bR

	; Apply envelope and level
	asigFormantL = asigOutL * aenv * iamp * 4
	asigFormantR = asigOutR * aenv * iamp * 4

	; Add sub-bass directly (bypassing formants)
	aL = asigFormantL + (aSubL * aenv)
	aR = asigFormantR + (aSubR * aenv)

	outs aL, aR
endin
`;
	}

	getParametersForCsound(params: Form1Params): CsoundParameter[] {
		return [
			{ channelName: 'frequency', value: params.frequency },
			{ channelName: 'vibratoRate', value: params.vibratoRate },
			{ channelName: 'vibratoDepth', value: params.vibratoDepth },
			{ channelName: 'formant1Freq', value: params.formant1Freq },
			{ channelName: 'formant1BW', value: params.formant1BW },
			{ channelName: 'formant1Amp', value: params.formant1Amp },
			{ channelName: 'formant2Freq', value: params.formant2Freq },
			{ channelName: 'formant2BW', value: params.formant2BW },
			{ channelName: 'formant2Amp', value: params.formant2Amp },
			{ channelName: 'formant3Freq', value: params.formant3Freq },
			{ channelName: 'formant3BW', value: params.formant3BW },
			{ channelName: 'formant3Amp', value: params.formant3Amp },
			{ channelName: 'formant4Freq', value: params.formant4Freq },
			{ channelName: 'formant4BW', value: params.formant4BW },
			{ channelName: 'formant4Amp', value: params.formant4Amp },
			{ channelName: 'formant5Freq', value: params.formant5Freq },
			{ channelName: 'formant5BW', value: params.formant5BW },
			{ channelName: 'formant5Amp', value: params.formant5Amp },
			{ channelName: 'attack', value: params.attack },
			{ channelName: 'decay', value: params.decay },
			{ channelName: 'sustain', value: params.sustain },
			{ channelName: 'release', value: params.release }
		];
	}

	randomParams(pitchLock?: PitchLock): Form1Params {
		const frequency = pitchLock?.enabled ? pitchLock.frequency : 55 * Math.pow(2, Math.random() * 4);

		return {
			frequency,
			vibratoRate: 2 + Math.random() * 6,
			vibratoDepth: 0.001 + Math.random() * 0.008,
			formant1Freq: 400 + Math.random() * 800,
			formant1BW: 40 + Math.random() * 100,
			formant1Amp: 0.8 + Math.random() * 0.2,
			formant2Freq: 800 + Math.random() * 600,
			formant2BW: 50 + Math.random() * 100,
			formant2Amp: 0.4 + Math.random() * 0.4,
			formant3Freq: 2000 + Math.random() * 1500,
			formant3BW: 80 + Math.random() * 120,
			formant3Amp: 0.05 + Math.random() * 0.15,
			formant4Freq: 3000 + Math.random() * 1500,
			formant4BW: 100 + Math.random() * 100,
			formant4Amp: 0.1 + Math.random() * 0.2,
			formant5Freq: 4000 + Math.random() * 1500,
			formant5BW: 100 + Math.random() * 150,
			formant5Amp: 0.01 + Math.random() * 0.1,
			attack: Math.random() * 0.15,
			decay: 0.05 + Math.random() * 0.3,
			sustain: 0.3 + Math.random() * 0.5,
			release: 0.05 + Math.random() * 0.4
		};
	}

	mutateParams(params: Form1Params, mutationAmount = 0.2, pitchLock?: PitchLock): Form1Params {
		const mutate = (value: number, min: number, max: number) => {
			const change = (Math.random() - 0.5) * 2 * mutationAmount * (max - min);
			return Math.max(min, Math.min(max, value + change));
		};

		return {
			frequency: pitchLock?.enabled ? pitchLock.frequency : mutate(params.frequency, 55, 55 * Math.pow(2, 4)),
			vibratoRate: mutate(params.vibratoRate, 2, 8),
			vibratoDepth: mutate(params.vibratoDepth, 0.001, 0.009),
			formant1Freq: mutate(params.formant1Freq, 400, 1200),
			formant1BW: mutate(params.formant1BW, 40, 140),
			formant1Amp: mutate(params.formant1Amp, 0.8, 1.0),
			formant2Freq: mutate(params.formant2Freq, 800, 1400),
			formant2BW: mutate(params.formant2BW, 50, 150),
			formant2Amp: mutate(params.formant2Amp, 0.4, 0.8),
			formant3Freq: mutate(params.formant3Freq, 2000, 3500),
			formant3BW: mutate(params.formant3BW, 80, 200),
			formant3Amp: mutate(params.formant3Amp, 0.05, 0.2),
			formant4Freq: mutate(params.formant4Freq, 3000, 4500),
			formant4BW: mutate(params.formant4BW, 100, 200),
			formant4Amp: mutate(params.formant4Amp, 0.1, 0.3),
			formant5Freq: mutate(params.formant5Freq, 4000, 5500),
			formant5BW: mutate(params.formant5BW, 100, 250),
			formant5Amp: mutate(params.formant5Amp, 0.01, 0.11),
			attack: mutate(params.attack, 0, 0.15),
			decay: mutate(params.decay, 0.05, 0.35),
			sustain: mutate(params.sustain, 0.3, 0.8),
			release: mutate(params.release, 0.05, 0.45)
		};
	}
}
