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

interface AdditiveBassParams {
  baseFreq: number;
  pitchSweep: number;
  pitchDecay: number;
  overtoneAmp: number;
  overtoneFreqMult: number;
  noiseAmp: number;
  noiseDecay: number;
  filterResonance: number;
  filterCutoff: number;
  attack: number;
  decay: number;
  waveshape: number;
  bodyResonance: number;
  click: number;
  harmonicSpread: number;
}

export class AdditiveBass extends CsoundEngine<AdditiveBassParams> {
  getName(): string {
    return 'Additive Bass';
  }

  getDescription(): string {
    return 'Deep bass drum using additive synthesis with pink noise and waveshaping';
  }

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

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

  protected getOrchestra(): string {
    return `
instr 1
  iBaseFreq chnget "baseFreq"
  iPitchSweep chnget "pitchSweep"
  iPitchDecay chnget "pitchDecay"
  iOvertoneAmp chnget "overtoneAmp"
  iOvertoneFreqMult chnget "overtoneFreqMult"
  iNoiseAmp chnget "noiseAmp"
  iNoiseDecay chnget "noiseDecay"
  iFilterResonance chnget "filterResonance"
  iFilterCutoff chnget "filterCutoff"
  iAttack chnget "attack"
  iDecay chnget "decay"
  iWaveshape chnget "waveshape"
  iBodyResonance chnget "bodyResonance"
  iClick chnget "click"
  iHarmonicSpread chnget "harmonicSpread"

  idur = p3
  iAttackTime = iAttack * idur
  iDecayTime = iDecay * idur
  iPitchDecayTime = iPitchDecay * idur
  iNoiseDecayTime = iNoiseDecay * idur

  ; Pitch envelope: exponential sweep from high to low
  kPitchEnv expseg iBaseFreq * (1 + iPitchSweep * 3), iPitchDecayTime, iBaseFreq, idur - iPitchDecayTime, iBaseFreq * 0.95

  ; Main amplitude envelope with attack and decay
  kAmpEnv linseg 0, iAttackTime, 1, iDecayTime, 0.001, 0.001, 0
  kAmpEnv = kAmpEnv * kAmpEnv

  ; Generate fundamental sine wave
  aFund oscili 0.7, kPitchEnv

  ; Generate overtone at multiple of fundamental
  aOvertone oscili iOvertoneAmp, kPitchEnv * iOvertoneFreqMult

  ; Add harmonic spread (additional harmonics)
  aHarm2 oscili iHarmonicSpread * 0.3, kPitchEnv * 3
  aHarm3 oscili iHarmonicSpread * 0.2, kPitchEnv * 5

  ; Mix oscillators
  aMix = aFund + aOvertone + aHarm2 + aHarm3

  ; Apply waveshaping (hyperbolic tangent style)
  if iWaveshape > 0.1 then
    aMix = tanh(aMix * (1 + iWaveshape * 3))
  endif

  ; Generate pink noise
  aPink pinkish 1

  ; Noise envelope (fast decay)
  kNoiseEnv expseg 1, iNoiseDecayTime, 0.001, idur - iNoiseDecayTime, 0.001
  aPinkScaled = aPink * iNoiseAmp * kNoiseEnv

  ; Add noise to mix
  aMix = aMix + aPinkScaled

  ; Click transient (high frequency burst at start)
  if iClick > 0.1 then
    kClickEnv linseg 1, 0.005, 0, idur - 0.005, 0
    aClick oscili iClick * 0.4, kPitchEnv * 8
    aMix = aMix + aClick * kClickEnv
  endif

  ; Apply resonant low-pass filter
  kFilterFreq = iFilterCutoff * (1 + kPitchEnv / iBaseFreq * 0.5)
  aFiltered rezzy aMix, kFilterFreq, iFilterResonance

  ; Body resonance (second resonant filter at fundamental)
  if iBodyResonance > 0.1 then
    aBodyFilt butterbp aFiltered, kPitchEnv * 0.5, 20
    aFiltered = aFiltered + aBodyFilt * iBodyResonance
  endif

  ; Apply main envelope
  aOut = aFiltered * kAmpEnv * 0.5

  ; Stereo - slightly different phase and detune for right channel
  kPitchEnvR expseg iBaseFreq * 1.002 * (1 + iPitchSweep * 3), iPitchDecayTime, iBaseFreq * 1.002, idur - iPitchDecayTime, iBaseFreq * 0.952

  aFundR oscili 0.7, kPitchEnvR
  aOvertoneR oscili iOvertoneAmp, kPitchEnvR * iOvertoneFreqMult
  aHarm2R oscili iHarmonicSpread * 0.3, kPitchEnvR * 3
  aHarm3R oscili iHarmonicSpread * 0.2, kPitchEnvR * 5

  aMixR = aFundR + aOvertoneR + aHarm2R + aHarm3R

  if iWaveshape > 0.1 then
    aMixR = tanh(aMixR * (1 + iWaveshape * 3))
  endif

  aPinkR pinkish 1
  aPinkScaledR = aPinkR * iNoiseAmp * kNoiseEnv
  aMixR = aMixR + aPinkScaledR

  if iClick > 0.1 then
    aClickR oscili iClick * 0.4, kPitchEnvR * 8
    aMixR = aMixR + aClickR * kClickEnv
  endif

  kFilterFreqR = iFilterCutoff * (1 + kPitchEnvR / iBaseFreq * 0.5)
  aFilteredR rezzy aMixR, kFilterFreqR, iFilterResonance

  if iBodyResonance > 0.1 then
    aBodyFiltR butterbp aFilteredR, kPitchEnvR * 0.5, 20
    aFilteredR = aFilteredR + aBodyFiltR * iBodyResonance
  endif

  aOutR = aFilteredR * kAmpEnv * 0.5

  outs aOut, aOutR
endin
`;
  }

  protected getParametersForCsound(params: AdditiveBassParams): CsoundParameter[] {
    return [
      { channelName: 'baseFreq', value: params.baseFreq },
      { channelName: 'pitchSweep', value: params.pitchSweep },
      { channelName: 'pitchDecay', value: params.pitchDecay },
      { channelName: 'overtoneAmp', value: params.overtoneAmp },
      { channelName: 'overtoneFreqMult', value: params.overtoneFreqMult },
      { channelName: 'noiseAmp', value: params.noiseAmp },
      { channelName: 'noiseDecay', value: params.noiseDecay },
      { channelName: 'filterResonance', value: params.filterResonance },
      { channelName: 'filterCutoff', value: params.filterCutoff },
      { channelName: 'attack', value: params.attack },
      { channelName: 'decay', value: params.decay },
      { channelName: 'waveshape', value: params.waveshape },
      { channelName: 'bodyResonance', value: params.bodyResonance },
      { channelName: 'click', value: params.click },
      { channelName: 'harmonicSpread', value: params.harmonicSpread },
    ];
  }

  randomParams(pitchLock?: PitchLock): AdditiveBassParams {
    const baseFreq = pitchLock?.enabled ? pitchLock.frequency : this.randomRange(35, 80);

    const overtoneMultChoices = [1.5, 2.0, 2.5, 3.0, 4.0];

    return {
      baseFreq,
      pitchSweep: this.randomRange(0.3, 1.0),
      pitchDecay: this.randomRange(0.02, 0.15),
      overtoneAmp: this.randomRange(0.2, 0.7),
      overtoneFreqMult: this.randomChoice(overtoneMultChoices),
      noiseAmp: this.randomRange(0.05, 0.3),
      noiseDecay: this.randomRange(0.01, 0.08),
      filterResonance: this.randomRange(5, 25),
      filterCutoff: this.randomRange(100, 800),
      attack: this.randomRange(0.001, 0.02),
      decay: this.randomRange(0.3, 0.8),
      waveshape: this.randomRange(0, 0.7),
      bodyResonance: this.randomRange(0, 0.5),
      click: this.randomRange(0, 0.6),
      harmonicSpread: this.randomRange(0, 0.5),
    };
  }

  mutateParams(
    params: AdditiveBassParams,
    mutationAmount: number = 0.15,
    pitchLock?: PitchLock
  ): AdditiveBassParams {
    const baseFreq = pitchLock?.enabled ? pitchLock.frequency : params.baseFreq;
    const overtoneMultChoices = [1.5, 2.0, 2.5, 3.0, 4.0];

    return {
      baseFreq,
      pitchSweep: this.mutateValue(params.pitchSweep, mutationAmount, 0.1, 1.5),
      pitchDecay: this.mutateValue(params.pitchDecay, mutationAmount, 0.01, 0.25),
      overtoneAmp: this.mutateValue(params.overtoneAmp, mutationAmount, 0, 1.0),
      overtoneFreqMult:
        Math.random() < 0.1 ? this.randomChoice(overtoneMultChoices) : params.overtoneFreqMult,
      noiseAmp: this.mutateValue(params.noiseAmp, mutationAmount, 0, 0.5),
      noiseDecay: this.mutateValue(params.noiseDecay, mutationAmount, 0.005, 0.15),
      filterResonance: this.mutateValue(params.filterResonance, mutationAmount, 2, 40),
      filterCutoff: this.mutateValue(params.filterCutoff, mutationAmount, 80, 1200),
      attack: this.mutateValue(params.attack, mutationAmount, 0.001, 0.05),
      decay: this.mutateValue(params.decay, mutationAmount, 0.15, 0.95),
      waveshape: this.mutateValue(params.waveshape, mutationAmount, 0, 1),
      bodyResonance: this.mutateValue(params.bodyResonance, mutationAmount, 0, 0.8),
      click: this.mutateValue(params.click, mutationAmount, 0, 0.8),
      harmonicSpread: this.mutateValue(params.harmonicSpread, mutationAmount, 0, 0.7),
    };
  }
}
