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

interface RingCymbalParams {
  baseFreq: number;
  overtone1Freq: number;
  overtone2Freq: number;
  overtone3Freq: number;
  overtone1Vol: number;
  overtone2Vol: number;
  overtone3Vol: number;
  filterCutoff: number;
  resonance: number;
  decay: number;
  attack: number;
  noise: number;
  brightness: number;
  spread: number;
}

export class RingCymbal extends CsoundEngine<RingCymbalParams> {
  getName(): string {
    return 'Ring Cymbal';
  }

  getDescription(): string {
    return 'Metallic cymbal using ring modulation with noise and multiple oscillators';
  }

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

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

  protected getOrchestra(): string {
    return `
instr 1
  iBaseFreq chnget "baseFreq"
  iOvertone1Freq chnget "overtone1Freq"
  iOvertone2Freq chnget "overtone2Freq"
  iOvertone3Freq chnget "overtone3Freq"
  iOvertone1Vol chnget "overtone1Vol"
  iOvertone2Vol chnget "overtone2Vol"
  iOvertone3Vol chnget "overtone3Vol"
  iFilterCutoff chnget "filterCutoff"
  iResonance chnget "resonance"
  iDecay chnget "decay"
  iAttack chnget "attack"
  iNoise chnget "noise"
  iBrightness chnget "brightness"
  iSpread chnget "spread"

  idur = p3
  iDecayTime = iDecay * idur
  iAttackTime = iAttack * idur

  ; Exponential decay envelope with attack
  kEnv linseg 0, iAttackTime, 1, iDecayTime - iAttackTime, 0.001, 0.001, 0
  kEnv = kEnv * kEnv

  ; Generate white noise source
  aNoise noise 1, 0

  ; Generate impulse oscillators at different frequencies
  aOsc1 oscili 1, iBaseFreq
  aOsc2 oscili iOvertone1Vol, iOvertone1Freq
  aOsc3 oscili iOvertone2Vol, iOvertone2Freq
  aOsc4 oscili iOvertone3Vol, iOvertone3Freq

  ; Ring modulation: multiply noise with oscillators
  aRing1 = aNoise * aOsc1
  aRing2 = aNoise * aOsc2
  aRing3 = aNoise * aOsc3
  aRing4 = aNoise * aOsc4

  ; Mix ring modulated signals
  aMix = (aRing1 + aRing2 + aRing3 + aRing4) * 0.25

  ; Add raw noise for character
  aMix = aMix * (1 - iNoise) + aNoise * iNoise * 0.3

  ; Apply resonant high-pass filter for metallic character
  aFiltered butterhp aMix, iFilterCutoff, iResonance

  ; Additional high-pass for brightness
  if iBrightness > 0.3 then
    aFiltered butterhp aFiltered, iFilterCutoff * (1 + iBrightness), iResonance * 0.5
  endif

  ; Apply envelope
  aOut = aFiltered * kEnv * 0.4

  ; Stereo spread using slightly different filter parameters
  iFilterCutoffR = iFilterCutoff * (1 + iSpread * 0.1)
  aFilteredR butterhp aMix, iFilterCutoffR, iResonance

  if iBrightness > 0.3 then
    aFilteredR butterhp aFilteredR, iFilterCutoffR * (1 + iBrightness), iResonance * 0.5
  endif

  aOutR = aFilteredR * kEnv * 0.4

  outs aOut, aOutR
endin
`;
  }

  protected getParametersForCsound(params: RingCymbalParams): CsoundParameter[] {
    return [
      { channelName: 'baseFreq', value: params.baseFreq },
      { channelName: 'overtone1Freq', value: params.overtone1Freq },
      { channelName: 'overtone2Freq', value: params.overtone2Freq },
      { channelName: 'overtone3Freq', value: params.overtone3Freq },
      { channelName: 'overtone1Vol', value: params.overtone1Vol },
      { channelName: 'overtone2Vol', value: params.overtone2Vol },
      { channelName: 'overtone3Vol', value: params.overtone3Vol },
      { channelName: 'filterCutoff', value: params.filterCutoff },
      { channelName: 'resonance', value: params.resonance },
      { channelName: 'decay', value: params.decay },
      { channelName: 'attack', value: params.attack },
      { channelName: 'noise', value: params.noise },
      { channelName: 'brightness', value: params.brightness },
      { channelName: 'spread', value: params.spread },
    ];
  }

  randomParams(pitchLock?: PitchLock): RingCymbalParams {
    const baseFreq = pitchLock?.enabled ? pitchLock.frequency : this.randomRange(800, 1800);

    const inharmonicRatios = [1.4, 1.7, 2.1, 2.3, 2.9, 3.1, 3.7, 4.3];

    return {
      baseFreq,
      overtone1Freq: baseFreq * this.randomChoice(inharmonicRatios),
      overtone2Freq: baseFreq * this.randomChoice(inharmonicRatios),
      overtone3Freq: baseFreq * this.randomChoice(inharmonicRatios),
      overtone1Vol: this.randomRange(0.4, 1.0),
      overtone2Vol: this.randomRange(0.3, 0.9),
      overtone3Vol: this.randomRange(0.2, 0.7),
      filterCutoff: this.randomRange(3000, 8000),
      resonance: this.randomRange(2, 8),
      decay: this.randomRange(0.3, 0.9),
      attack: this.randomRange(0.001, 0.02),
      noise: this.randomRange(0.1, 0.5),
      brightness: this.randomRange(0, 0.8),
      spread: this.randomRange(0.01, 0.15),
    };
  }

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

    return {
      baseFreq,
      overtone1Freq: this.mutateValue(params.overtone1Freq * freqRatio, mutationAmount, baseFreq * 1.2, baseFreq * 5),
      overtone2Freq: this.mutateValue(params.overtone2Freq * freqRatio, mutationAmount, baseFreq * 1.2, baseFreq * 5),
      overtone3Freq: this.mutateValue(params.overtone3Freq * freqRatio, mutationAmount, baseFreq * 1.2, baseFreq * 5),
      overtone1Vol: this.mutateValue(params.overtone1Vol, mutationAmount, 0.2, 1.0),
      overtone2Vol: this.mutateValue(params.overtone2Vol, mutationAmount, 0.1, 1.0),
      overtone3Vol: this.mutateValue(params.overtone3Vol, mutationAmount, 0.1, 0.9),
      filterCutoff: this.mutateValue(params.filterCutoff, mutationAmount, 2000, 10000),
      resonance: this.mutateValue(params.resonance, mutationAmount, 1, 12),
      decay: this.mutateValue(params.decay, mutationAmount, 0.2, 1.0),
      attack: this.mutateValue(params.attack, mutationAmount, 0.001, 0.05),
      noise: this.mutateValue(params.noise, mutationAmount, 0, 0.7),
      brightness: this.mutateValue(params.brightness, mutationAmount, 0, 1),
      spread: this.mutateValue(params.spread, mutationAmount, 0.005, 0.25),
    };
  }
}
