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

interface FMTomTomParams {
  baseFreq: number;
  pitchBendAmount: number;
  pitchBendDecay: number;
  modIndex: number;
  modRatio: number;
  noiseHPFreq: number;
  noiseResonance: number;
  noiseMix: number;
  ampAttack: number;
  ampDecay: number;
  sustain: number;
  release: number;
  tonality: number;
  stereoDetune: number;
}

export class FMTomTom extends CsoundEngine<FMTomTomParams> {
  getName(): string {
    return 'FM Tom-Tom';
  }

  getDescription(): string {
    return 'High-pass filtered noise modulating a sine oscillator with pitch bend envelope simulating tom-tom membrane';
  }

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

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

  protected getOrchestra(): string {
    return `
instr 1
  iBaseFreq chnget "baseFreq"
  iPitchBendAmount chnget "pitchBendAmount"
  iPitchBendDecay chnget "pitchBendDecay"
  iModIndex chnget "modIndex"
  iModRatio chnget "modRatio"
  iNoiseHPFreq chnget "noiseHPFreq"
  iNoiseResonance chnget "noiseResonance"
  iNoiseMix chnget "noiseMix"
  iAmpAttack chnget "ampAttack"
  iAmpDecay chnget "ampDecay"
  iSustain chnget "sustain"
  iRelease chnget "release"
  iTonality chnget "tonality"
  iStereoDetune chnget "stereoDetune"

  idur = p3
  iPitchBendTime = iPitchBendDecay * idur
  iAmpAttackTime = iAmpAttack * idur
  iAmpDecayTime = iAmpDecay * idur
  iReleaseTime = iRelease * idur

  ; Pitch bend envelope (simulates drum membrane tightening)
  ; Starts at higher pitch and decays to base pitch
  iPitchStart = iBaseFreq * (1 + iPitchBendAmount)
  kPitchEnv expseg iPitchStart, iPitchBendTime, iBaseFreq, idur - iPitchBendTime, iBaseFreq

  ; Generate high-pass filtered noise for modulation
  aNoise noise 1, 0
  aNoiseHP butterhp aNoise, iNoiseHPFreq
  aNoiseFiltered butterbp aNoiseHP, iNoiseHPFreq * 2, iNoiseResonance

  ; Scale noise for FM modulation
  aNoiseScaled = aNoiseFiltered * iModIndex * kPitchEnv * iTonality

  ; FM synthesis: noise modulates sine oscillator
  aModulator oscili iModIndex * kPitchEnv, kPitchEnv * iModRatio
  aCarrier oscili 0.5, kPitchEnv + aModulator + aNoiseScaled

  ; Add direct noise component for more realistic tom sound
  aNoiseDirect = aNoiseFiltered * iNoiseMix * 0.3

  ; Mix carrier and noise
  aMix = aCarrier * (1 - iNoiseMix * 0.5) + aNoiseDirect

  ; Amplitude envelope (ADSR-like with fast attack and decay)
  kAmpEnv expseg 0.001, iAmpAttackTime, 1, iAmpDecayTime, iSustain, idur - iAmpAttackTime - iAmpDecayTime - iReleaseTime, iSustain, iReleaseTime, 0.001

  ; Apply amplitude envelope
  aOut = aMix * kAmpEnv

  ; Right channel with stereo detune
  iBaseFreqR = iBaseFreq * (1 + iStereoDetune * 0.02)
  iPitchStartR = iBaseFreqR * (1 + iPitchBendAmount)
  kPitchEnvR expseg iPitchStartR, iPitchBendTime, iBaseFreqR, idur - iPitchBendTime, iBaseFreqR

  aNoiseR noise 1, 0
  aNoiseHPR butterhp aNoiseR, iNoiseHPFreq * (1 + iStereoDetune * 0.01)
  aNoiseFilteredR butterbp aNoiseHPR, iNoiseHPFreq * 2 * (1 + iStereoDetune * 0.01), iNoiseResonance

  aNoiseScaledR = aNoiseFilteredR * iModIndex * kPitchEnvR * iTonality
  aModulatorR oscili iModIndex * kPitchEnvR, kPitchEnvR * iModRatio
  aCarrierR oscili 0.5, kPitchEnvR + aModulatorR + aNoiseScaledR

  aNoiseDirectR = aNoiseFilteredR * iNoiseMix * 0.3
  aMixR = aCarrierR * (1 - iNoiseMix * 0.5) + aNoiseDirectR
  aOutR = aMixR * kAmpEnv

  outs aOut, aOutR
endin
`;
  }

  protected getParametersForCsound(params: FMTomTomParams): CsoundParameter[] {
    return [
      { channelName: 'baseFreq', value: params.baseFreq },
      { channelName: 'pitchBendAmount', value: params.pitchBendAmount },
      { channelName: 'pitchBendDecay', value: params.pitchBendDecay },
      { channelName: 'modIndex', value: params.modIndex },
      { channelName: 'modRatio', value: params.modRatio },
      { channelName: 'noiseHPFreq', value: params.noiseHPFreq },
      { channelName: 'noiseResonance', value: params.noiseResonance },
      { channelName: 'noiseMix', value: params.noiseMix },
      { channelName: 'ampAttack', value: params.ampAttack },
      { channelName: 'ampDecay', value: params.ampDecay },
      { channelName: 'sustain', value: params.sustain },
      { channelName: 'release', value: params.release },
      { channelName: 'tonality', value: params.tonality },
      { channelName: 'stereoDetune', value: params.stereoDetune },
    ];
  }

  randomParams(pitchLock?: PitchLock): FMTomTomParams {
    const baseFreqChoices = [80, 100, 120, 150, 180, 220, 260, 300];
    const baseFreq = pitchLock?.enabled
      ? pitchLock.frequency
      : this.randomChoice(baseFreqChoices) * this.randomRange(0.9, 1.1);

    const modRatios = [0.5, 1, 1.5, 2, 2.5, 3];

    return {
      baseFreq,
      pitchBendAmount: this.randomRange(0.2, 0.8),
      pitchBendDecay: this.randomRange(0.05, 0.2),
      modIndex: this.randomRange(1, 8),
      modRatio: this.randomChoice(modRatios),
      noiseHPFreq: this.randomRange(200, 800),
      noiseResonance: this.randomRange(20, 100),
      noiseMix: this.randomRange(0.1, 0.6),
      ampAttack: this.randomRange(0.001, 0.01),
      ampDecay: this.randomRange(0.1, 0.3),
      sustain: this.randomRange(0.2, 0.6),
      release: this.randomRange(0.2, 0.5),
      tonality: this.randomRange(0.3, 0.9),
      stereoDetune: this.randomRange(0, 0.5),
    };
  }

  mutateParams(
    params: FMTomTomParams,
    mutationAmount: number = 0.15,
    pitchLock?: PitchLock
  ): FMTomTomParams {
    const baseFreq = pitchLock?.enabled ? pitchLock.frequency : params.baseFreq;
    const modRatios = [0.5, 1, 1.5, 2, 2.5, 3];

    return {
      baseFreq,
      pitchBendAmount: this.mutateValue(params.pitchBendAmount, mutationAmount, 0.1, 1),
      pitchBendDecay: this.mutateValue(params.pitchBendDecay, mutationAmount, 0.02, 0.4),
      modIndex: this.mutateValue(params.modIndex, mutationAmount, 0.5, 12),
      modRatio:
        Math.random() < 0.15 ? this.randomChoice(modRatios) : params.modRatio,
      noiseHPFreq: this.mutateValue(params.noiseHPFreq, mutationAmount, 100, 1200),
      noiseResonance: this.mutateValue(params.noiseResonance, mutationAmount, 15, 150),
      noiseMix: this.mutateValue(params.noiseMix, mutationAmount, 0, 0.8),
      ampAttack: this.mutateValue(params.ampAttack, mutationAmount, 0.001, 0.02),
      ampDecay: this.mutateValue(params.ampDecay, mutationAmount, 0.05, 0.5),
      sustain: this.mutateValue(params.sustain, mutationAmount, 0.1, 0.8),
      release: this.mutateValue(params.release, mutationAmount, 0.1, 0.7),
      tonality: this.mutateValue(params.tonality, mutationAmount, 0.1, 1),
      stereoDetune: this.mutateValue(params.stereoDetune, mutationAmount, 0, 1),
    };
  }
}
