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

interface Squine1Params {
	frequency: number;
	shapeAmount1: number;
	shapeAmount2: number;
	modulationDepth1: number;
	modulationDepth2: number;
	osc2Detune: number;
	osc2Level: number;
	hpfCutoff: number;
	attack: number;
	decay: number;
	sustain: number;
	release: number;
}

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

	getDescription(): string {
		return 'Squinewave synthesizer with waveshaping and dual oscillators';
	}

	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
	ishape1 = chnget("shapeAmount1")
	ishape2 = chnget("shapeAmount2")
	imod1 = chnget("modulationDepth1")
	imod2 = chnget("modulationDepth2")
	idetune = chnget("osc2Detune")
	iosc2Level = chnget("osc2Level")
	ihpf = chnget("hpfCutoff")
	iatt = chnget("attack") * p3
	idec = chnget("decay") * p3
	isus = chnget("sustain")
	irel = chnget("release") * p3

	; First oscillator with waveshaping (squine approximation)
	aosc1 = poscil(1, ifreq)

	; Create dynamic shape envelope for osc1
	kshape1 = expseg(ishape1, p3, ishape1 * imod1)

	; Waveshaping: blend between sine and square-like
	asig1 = tanh(aosc1 * kshape1 * 5) / tanh(kshape1 * 5)

	; Second oscillator slightly detuned
	aosc2 = poscil(1, ifreq * (1 + idetune))

	; Create dynamic shape envelope for osc2
	kshape2 = expseg(ishape2, p3, ishape2 * imod2)

	; Waveshaping for second oscillator
	asig2 = tanh(aosc2 * kshape2 * 5) / tanh(kshape2 * 5)

	; Mix oscillators
	asig = asig1 + (asig2 * iosc2Level)

	; High-pass filter to remove DC and rumble
	kcutoff = max(ihpf, 20)
	asig = butterhp(asig, kcutoff)

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

	; DC blocker
	asig = dcblock2(asig)

	outs asig, asig
endin
`;
	}

	getParametersForCsound(params: Squine1Params): CsoundParameter[] {
		return [
			{ channelName: 'frequency', value: params.frequency },
			{ channelName: 'shapeAmount1', value: params.shapeAmount1 },
			{ channelName: 'shapeAmount2', value: params.shapeAmount2 },
			{ channelName: 'modulationDepth1', value: params.modulationDepth1 },
			{ channelName: 'modulationDepth2', value: params.modulationDepth2 },
			{ channelName: 'osc2Detune', value: params.osc2Detune },
			{ channelName: 'osc2Level', value: params.osc2Level },
			{ channelName: 'hpfCutoff', value: params.hpfCutoff },
			{ channelName: 'attack', value: params.attack },
			{ channelName: 'decay', value: params.decay },
			{ channelName: 'sustain', value: params.sustain },
			{ channelName: 'release', value: params.release }
		];
	}

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

		return {
			frequency,
			shapeAmount1: 0.2 + Math.random() * 0.8,
			shapeAmount2: 0.2 + Math.random() * 0.8,
			modulationDepth1: 0.05 + Math.random() * 0.7,
			modulationDepth2: 0.05 + Math.random() * 0.7,
			osc2Detune: 0.0001 + Math.random() * 0.005,
			osc2Level: 0.01 + Math.random() * 0.15,
			hpfCutoff: frequency * 0.5 + Math.random() * frequency,
			attack: 0.001 + Math.random() * 0.05,
			decay: 0.05 + Math.random() * 0.3,
			sustain: 0.2 + Math.random() * 0.6,
			release: 0.01 + Math.random() * 0.3
		};
	}

	mutateParams(params: Squine1Params, mutationAmount = 0.2, pitchLock?: PitchLock): Squine1Params {
		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));
		};

		const newFreq = pitchLock?.enabled ? pitchLock.frequency : mutate(params.frequency, 55, 55 * Math.pow(2, 5));

		return {
			frequency: newFreq,
			shapeAmount1: mutate(params.shapeAmount1, 0.2, 1.0),
			shapeAmount2: mutate(params.shapeAmount2, 0.2, 1.0),
			modulationDepth1: mutate(params.modulationDepth1, 0.05, 0.75),
			modulationDepth2: mutate(params.modulationDepth2, 0.05, 0.75),
			osc2Detune: mutate(params.osc2Detune, 0.0001, 0.0051),
			osc2Level: mutate(params.osc2Level, 0.01, 0.16),
			hpfCutoff: mutate(params.hpfCutoff, newFreq * 0.5, newFreq * 1.5),
			attack: mutate(params.attack, 0.001, 0.051),
			decay: mutate(params.decay, 0.05, 0.35),
			sustain: mutate(params.sustain, 0.2, 0.8),
			release: mutate(params.release, 0.01, 0.31)
		};
	}
}
