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

interface FeedbackSnareParams {
  baseFreq: number;
  tonalDecay: number;
  noiseDecay: number;
  toneResonance: number;
  springDecay: number;
  springTone: number;
  pitchBend: number;
  pitchBendSpeed: number;
  pulseRate: number;
  feedbackAmount: number;
  delayTime: number;
  crossFeedMix: number;
  snap: number;
  brightness: number;
}

export class FeedbackSnare extends CsoundEngine<FeedbackSnareParams> {
  getName(): string {
    return 'Feedback Snare';
  }

  getDescription(): string {
    return 'Complex snare using cross-feedback delay network with pulsed noise modulation';
  }

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

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

  protected getOrchestra(): string {
    return `
instr 1
  iBaseFreq chnget "baseFreq"
  iTonalDecay chnget "tonalDecay"
  iNoiseDecay chnget "noiseDecay"
  iToneResonance chnget "toneResonance"
  iSpringDecay chnget "springDecay"
  iSpringTone chnget "springTone"
  iPitchBend chnget "pitchBend"
  iPitchBendSpeed chnget "pitchBendSpeed"
  iPulseRate chnget "pulseRate"
  iFeedbackAmount chnget "feedbackAmount"
  iDelayTime chnget "delayTime"
  iCrossFeedMix chnget "crossFeedMix"
  iSnap chnget "snap"
  iBrightness chnget "brightness"

  idur = p3
  iTonalDecayTime = iTonalDecay * idur
  iNoiseDecayTime = iNoiseDecay * idur
  iSpringDecayTime = iSpringDecay * idur
  iPitchBendTime = iPitchBendSpeed * idur

  ; Pitch envelope with bend
  kPitchEnv expseg iBaseFreq * (1 + iPitchBend * 2), iPitchBendTime, iBaseFreq, idur - iPitchBendTime, iBaseFreq * 0.95

  ; Generate square wave pulse for tonal component
  aPulse vco2 0.5, kPitchEnv, 2, 0.5

  ; Tonal envelope
  kToneEnv expseg 1, iTonalDecayTime, 0.001, idur - iTonalDecayTime, 0.001
  aTonal = aPulse * kToneEnv

  ; Apply drum tone resonant filter
  aDrumTone rezzy aTonal, kPitchEnv, iToneResonance

  ; Generate white noise
  aNoise noise 1, 0

  ; Pulse modulation of noise (creates rhythmic texture)
  kPulseMod oscili 1, iPulseRate
  kPulseMod = (kPulseMod + 1) * 0.5
  aNoiseModulated = aNoise * kPulseMod

  ; Noise envelope
  kNoiseEnv expseg 1, iNoiseDecayTime, 0.001, idur - iNoiseDecayTime, 0.001

  ; Parallel filters on noise
  ; Bandpass filter 1 (body)
  aNoiseBody butterbp aNoiseModulated, iBaseFreq * 1.5, 100
  ; Bandpass filter 2 (mid)
  aNoiseMid butterbp aNoiseModulated, iBaseFreq * 3, 200
  ; Highpass filter (crispness)
  aNoiseHigh butterhp aNoiseModulated, 3000

  ; Mix noise components
  aNoiseMix = (aNoiseBody * 0.4 + aNoiseMid * 0.3 + aNoiseHigh * 0.3 * iBrightness) * kNoiseEnv

  ; Mix tonal and noise
  aMix = aDrumTone * 0.5 + aNoiseMix * 0.5

  ; Cross-feedback delay network (simulates spring/snare wires)
  ; Create two delay lines that feed back into each other
  aDelay1Init init 0
  aDelay2Init init 0

  ; Spring tone filter (for the delayed signal)
  iSpringFreq = 800 + iSpringTone * 4000

  ; Delay line 1
  aDelayIn1 = aMix + aDelay2Init * iFeedbackAmount * iCrossFeedMix
  aDelay1 vdelay aDelayIn1, iDelayTime * 1000, 50
  aDelay1Filt butterbp aDelay1, iSpringFreq, 100
  aDelay1Out = aDelay1Filt * exp(-p3 / iSpringDecayTime)

  ; Delay line 2
  aDelayIn2 = aMix + aDelay1Out * iFeedbackAmount
  aDelay2 vdelay aDelayIn2, iDelayTime * 1.3 * 1000, 50
  aDelay2Filt butterbp aDelay2, iSpringFreq * 1.2, 120
  aDelay2Out = aDelay2Filt * exp(-p3 / iSpringDecayTime)

  ; Update feedback
  aDelay1Init = aDelay1Out
  aDelay2Init = aDelay2Out

  ; Mix dry and delay
  aOut = aMix * 0.6 + aDelay1Out * 0.2 + aDelay2Out * 0.2

  ; Add snap transient
  if iSnap > 0.1 then
    kSnapEnv linseg 1, 0.003, 0, idur - 0.003, 0
    aSnap noise iSnap * 0.5, 0
    aSnapFilt butterhp aSnap, 8000
    aOut = aOut + aSnapFilt * kSnapEnv
  endif

  ; Final output scaling
  aOut = aOut * 0.4

  ; Right channel with slightly different parameters
  aPulseR vco2 0.5, kPitchEnv * 1.002, 2, 0.5
  aTonalR = aPulseR * kToneEnv
  aDrumToneR rezzy aTonalR, kPitchEnv * 1.002, iToneResonance

  aNoiseR noise 1, 0
  aNoiseModulatedR = aNoiseR * kPulseMod
  aNoiseBodyR butterbp aNoiseModulatedR, iBaseFreq * 1.52, 100
  aNoiseMidR butterbp aNoiseModulatedR, iBaseFreq * 3.03, 200
  aNoiseHighR butterhp aNoiseModulatedR, 3100

  aNoiseMixR = (aNoiseBodyR * 0.4 + aNoiseMidR * 0.3 + aNoiseHighR * 0.3 * iBrightness) * kNoiseEnv
  aMixR = aDrumToneR * 0.5 + aNoiseMixR * 0.5

  aDelay1InitR init 0
  aDelay2InitR init 0

  aDelayIn1R = aMixR + aDelay2InitR * iFeedbackAmount * iCrossFeedMix
  aDelay1R vdelay aDelayIn1R, iDelayTime * 1.05 * 1000, 50
  aDelay1FiltR butterbp aDelay1R, iSpringFreq * 1.01, 100
  aDelay1OutR = aDelay1FiltR * exp(-p3 / iSpringDecayTime)

  aDelayIn2R = aMixR + aDelay1OutR * iFeedbackAmount
  aDelay2R vdelay aDelayIn2R, iDelayTime * 1.35 * 1000, 50
  aDelay2FiltR butterbp aDelay2R, iSpringFreq * 1.22, 120
  aDelay2OutR = aDelay2FiltR * exp(-p3 / iSpringDecayTime)

  aDelay1InitR = aDelay1OutR
  aDelay2InitR = aDelay2OutR

  aOutR = aMixR * 0.6 + aDelay1OutR * 0.2 + aDelay2OutR * 0.2

  if iSnap > 0.1 then
    aSnapR noise iSnap * 0.5, 0
    aSnapFiltR butterhp aSnapR, 8100
    aOutR = aOutR + aSnapFiltR * kSnapEnv
  endif

  aOutR = aOutR * 0.4

  outs aOut, aOutR
endin
`;
  }

  protected getParametersForCsound(params: FeedbackSnareParams): CsoundParameter[] {
    return [
      { channelName: 'baseFreq', value: params.baseFreq },
      { channelName: 'tonalDecay', value: params.tonalDecay },
      { channelName: 'noiseDecay', value: params.noiseDecay },
      { channelName: 'toneResonance', value: params.toneResonance },
      { channelName: 'springDecay', value: params.springDecay },
      { channelName: 'springTone', value: params.springTone },
      { channelName: 'pitchBend', value: params.pitchBend },
      { channelName: 'pitchBendSpeed', value: params.pitchBendSpeed },
      { channelName: 'pulseRate', value: params.pulseRate },
      { channelName: 'feedbackAmount', value: params.feedbackAmount },
      { channelName: 'delayTime', value: params.delayTime },
      { channelName: 'crossFeedMix', value: params.crossFeedMix },
      { channelName: 'snap', value: params.snap },
      { channelName: 'brightness', value: params.brightness },
    ];
  }

  randomParams(pitchLock?: PitchLock): FeedbackSnareParams {
    const baseFreq = pitchLock?.enabled ? pitchLock.frequency : this.randomRange(150, 350);

    return {
      baseFreq,
      tonalDecay: this.randomRange(0.1, 0.3),
      noiseDecay: this.randomRange(0.3, 0.7),
      toneResonance: this.randomRange(5, 25),
      springDecay: this.randomRange(0.2, 0.6),
      springTone: this.randomRange(0.2, 0.8),
      pitchBend: this.randomRange(0.3, 0.9),
      pitchBendSpeed: this.randomRange(0.01, 0.05),
      pulseRate: this.randomRange(50, 300),
      feedbackAmount: this.randomRange(0.3, 0.7),
      delayTime: this.randomRange(0.005, 0.025),
      crossFeedMix: this.randomRange(0.4, 0.9),
      snap: this.randomRange(0, 0.6),
      brightness: this.randomRange(0.3, 0.9),
    };
  }

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

    return {
      baseFreq,
      tonalDecay: this.mutateValue(params.tonalDecay, mutationAmount, 0.05, 0.5),
      noiseDecay: this.mutateValue(params.noiseDecay, mutationAmount, 0.2, 0.9),
      toneResonance: this.mutateValue(params.toneResonance, mutationAmount, 2, 40),
      springDecay: this.mutateValue(params.springDecay, mutationAmount, 0.1, 0.8),
      springTone: this.mutateValue(params.springTone, mutationAmount, 0, 1),
      pitchBend: this.mutateValue(params.pitchBend, mutationAmount, 0.1, 1.2),
      pitchBendSpeed: this.mutateValue(params.pitchBendSpeed, mutationAmount, 0.005, 0.1),
      pulseRate: this.mutateValue(params.pulseRate, mutationAmount, 20, 500),
      feedbackAmount: this.mutateValue(params.feedbackAmount, mutationAmount, 0.1, 0.85),
      delayTime: this.mutateValue(params.delayTime, mutationAmount, 0.003, 0.04),
      crossFeedMix: this.mutateValue(params.crossFeedMix, mutationAmount, 0.2, 1),
      snap: this.mutateValue(params.snap, mutationAmount, 0, 0.8),
      brightness: this.mutateValue(params.brightness, mutationAmount, 0, 1),
    };
  }
}
