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

interface Sub8Params {
	frequency: number;
	filterCutoff: number;
	filterEnvAmount: number;
	resonance: number;
	saturation: number;
	osc1Level: number;
	osc2Level: number;
	osc3Level: number;
	attack: number;
	decay: number;
	sustain: number;
	release: number;
}

export class Sub8 extends CsoundEngine<Sub8Params> {
	getName(): string {
		return 'Sub8';
	}

	getDescription(): string {
		return 'Multi-oscillator subtractive synth with diode ladder filter and saturation';
	}

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

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

	getOrchestra(): string {
		return `
instr 1
	ifreq = chnget("frequency")
	iamp = 0.5
	ifilterCutoff = chnget("filterCutoff")
	ifilterEnvAmount = chnget("filterEnvAmount")
	ireso = chnget("resonance")
	isat = chnget("saturation")
	iosc1Level = chnget("osc1Level")
	iosc2Level = chnget("osc2Level")
	iosc3Level = chnget("osc3Level")
	iatt = chnget("attack") * p3
	idec = chnget("decay") * p3
	isus = chnget("sustain")
	irel = chnget("release") * p3

	; Three detuned oscillators
	asig1 = vco2(iamp * iosc1Level, ifreq, 10)
	asig2 = vco2(iamp * iosc2Level, ifreq * 2, 10)
	asig3 = vco2(iamp * iosc3Level, ifreq * 3.5, 12)
	asig = asig1 + asig2 + asig3

	; Saturation
	asig = tanh(asig * isat) / tanh(isat)

	; Filter envelope
	aenv = madsr(iatt, idec, isus, irel)
	kcutoff = ifilterCutoff + (aenv * ifilterEnvAmount)
	kcutoff = limit(kcutoff, 20, 18000)

	; Diode ladder filter
	asig = diode_ladder(asig, kcutoff, ireso)

	; Final amplitude envelope
	asig = asig * aenv * 0.7

	outs asig, asig
endin
`;
	}

	getParametersForCsound(params: Sub8Params): CsoundParameter[] {
		return [
			{ channelName: 'frequency', value: params.frequency },
			{ channelName: 'filterCutoff', value: params.filterCutoff },
			{ channelName: 'filterEnvAmount', value: params.filterEnvAmount },
			{ channelName: 'resonance', value: params.resonance },
			{ channelName: 'saturation', value: params.saturation },
			{ channelName: 'osc1Level', value: params.osc1Level },
			{ channelName: 'osc2Level', value: params.osc2Level },
			{ channelName: 'osc3Level', value: params.osc3Level },
			{ channelName: 'attack', value: params.attack },
			{ channelName: 'decay', value: params.decay },
			{ channelName: 'sustain', value: params.sustain },
			{ channelName: 'release', value: params.release }
		];
	}

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

		return {
			frequency,
			filterCutoff: 200 + Math.random() * 3800,
			filterEnvAmount: Math.random() * 6000,
			resonance: 0.5 + Math.random() * 14.5,
			saturation: 1 + Math.random() * 9,
			osc1Level: 0.5 + Math.random() * 0.5,
			osc2Level: Math.random() * 0.5,
			osc3Level: Math.random() * 0.3,
			attack: Math.random() * 0.1,
			decay: 0.05 + Math.random() * 0.3,
			sustain: 0.2 + Math.random() * 0.6,
			release: 0.05 + Math.random() * 0.4
		};
	}

	mutateParams(params: Sub8Params, mutationAmount = 0.2, pitchLock?: PitchLock): Sub8Params {
		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, 5)),
			filterCutoff: mutate(params.filterCutoff, 200, 4000),
			filterEnvAmount: mutate(params.filterEnvAmount, 0, 6000),
			resonance: mutate(params.resonance, 0.5, 15),
			saturation: mutate(params.saturation, 1, 10),
			osc1Level: mutate(params.osc1Level, 0.5, 1),
			osc2Level: mutate(params.osc2Level, 0, 0.5),
			osc3Level: mutate(params.osc3Level, 0, 0.3),
			attack: mutate(params.attack, 0, 0.1),
			decay: mutate(params.decay, 0.05, 0.35),
			sustain: mutate(params.sustain, 0.2, 0.8),
			release: mutate(params.release, 0.05, 0.45)
		};
	}
}
