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

interface FormantPopDrumParams {
  formant1Freq: number;
  formant1Width: number;
  formant2Freq: number;
  formant2Width: number;
  noiseDecay: number;
  ampAttack: number;
  ampDecay: number;
  brightness: number;
  stereoSpread: number;
}

export class FormantPopDrum extends CsoundEngine<FormantPopDrumParams> {
  getName(): string {
    return 'Formant Pop Drum';
  }

  getDescription(): string {
    return 'Short noise burst through dual bandpass filters creating marimba-like or wooden drum tones';
  }

  getType() {
    return 'generative' as const;
  }

  getCategory() {
    return 'Percussion' as const;
  }

  protected getOrchestra(): string {
    return `
instr 1
  iF1Freq chnget "formant1Freq"
  iF1Width chnget "formant1Width"
  iF2Freq chnget "formant2Freq"
  iF2Width chnget "formant2Width"
  iNoiseDecay chnget "noiseDecay"
  iAmpAttack chnget "ampAttack"
  iAmpDecay chnget "ampDecay"
  iBrightness chnget "brightness"
  iStereoSpread chnget "stereoSpread"

  idur = p3
  iNoiseDecayTime = iNoiseDecay * idur
  iAmpAttackTime = iAmpAttack * idur
  iAmpDecayTime = iAmpDecay * idur

  ; Declick envelope for noise (very short to avoid clicks)
  kDeclickEnv linseg 0, 0.001, 1, iNoiseDecayTime, 0, idur - iNoiseDecayTime - 0.001, 0

  ; Generate random noise
  aNoise noise 1, 0

  ; Apply declick envelope to noise
  aNoiseEnv = aNoise * kDeclickEnv

  ; First bandpass filter (formant 1)
  aFormant1 butterbp aNoiseEnv, iF1Freq, iF1Width

  ; Second bandpass filter (formant 2)
  aFormant2 butterbp aNoiseEnv, iF2Freq, iF2Width

  ; Mix formants with brightness control
  aMix = aFormant1 * (1 - iBrightness * 0.5) + aFormant2 * iBrightness

  ; Amplitude envelope (exponential decay)
  kAmpEnv expseg 0.001, iAmpAttackTime, 1, iAmpDecayTime, 0.001, idur - iAmpAttackTime - iAmpDecayTime, 0.001

  ; Apply amplitude envelope
  aOut = aMix * kAmpEnv

  ; Stereo output with slight frequency offset for right channel
  iF1FreqR = iF1Freq * (1 + iStereoSpread * 0.02)
  iF2FreqR = iF2Freq * (1 + iStereoSpread * 0.02)

  aFormant1R butterbp aNoiseEnv, iF1FreqR, iF1Width
  aFormant2R butterbp aNoiseEnv, iF2FreqR, iF2Width

  aMixR = aFormant1R * (1 - iBrightness * 0.5) + aFormant2R * iBrightness
  aOutR = aMixR * kAmpEnv

  outs aOut, aOutR
endin
`;
  }

  protected getParametersForCsound(params: FormantPopDrumParams): CsoundParameter[] {
    return [
      { channelName: 'formant1Freq', value: params.formant1Freq },
      { channelName: 'formant1Width', value: params.formant1Width },
      { channelName: 'formant2Freq', value: params.formant2Freq },
      { channelName: 'formant2Width', value: params.formant2Width },
      { channelName: 'noiseDecay', value: params.noiseDecay },
      { channelName: 'ampAttack', value: params.ampAttack },
      { channelName: 'ampDecay', value: params.ampDecay },
      { channelName: 'brightness', value: params.brightness },
      { channelName: 'stereoSpread', value: params.stereoSpread },
    ];
  }

  randomParams(pitchLock?: PitchLock): FormantPopDrumParams {
    const formant1FreqChoices = [200, 250, 300, 400, 500, 600, 800, 1000];
    const formant1Freq = pitchLock?.enabled
      ? pitchLock.frequency
      : this.randomChoice(formant1FreqChoices) * this.randomRange(0.9, 1.1);

    return {
      formant1Freq,
      formant1Width: this.randomRange(30, 120),
      formant2Freq: formant1Freq * this.randomRange(1.5, 3.5),
      formant2Width: this.randomRange(40, 150),
      noiseDecay: this.randomRange(0.05, 0.3),
      ampAttack: this.randomRange(0.001, 0.02),
      ampDecay: this.randomRange(0.1, 0.6),
      brightness: this.randomRange(0.2, 0.8),
      stereoSpread: this.randomRange(0, 0.5),
    };
  }

  mutateParams(
    params: FormantPopDrumParams,
    mutationAmount: number = 0.15,
    pitchLock?: PitchLock
  ): FormantPopDrumParams {
    const formant1Freq = pitchLock?.enabled ? pitchLock.frequency : params.formant1Freq;

    return {
      formant1Freq,
      formant1Width: this.mutateValue(params.formant1Width, mutationAmount, 20, 200),
      formant2Freq: this.mutateValue(params.formant2Freq, mutationAmount, 300, 4000),
      formant2Width: this.mutateValue(params.formant2Width, mutationAmount, 30, 250),
      noiseDecay: this.mutateValue(params.noiseDecay, mutationAmount, 0.02, 0.5),
      ampAttack: this.mutateValue(params.ampAttack, mutationAmount, 0.001, 0.05),
      ampDecay: this.mutateValue(params.ampDecay, mutationAmount, 0.05, 0.8),
      brightness: this.mutateValue(params.brightness, mutationAmount, 0, 1),
      stereoSpread: this.mutateValue(params.stereoSpread, mutationAmount, 0, 1),
    };
  }
}
