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

interface LaserSweepParams {
  startFreq: number;
  endFreq: number;
  sweepCurve: number;
  resonance: number;
  brightness: number;
  delayTime: number;
  delayFeedback: number;
  reverbMix: number;
  reverbSize: number;
  noiseAmount: number;
  decayShape: number;
  waveformMix: number;
  mainWaveType: number;
  secondWaveType: number;
  pulseWidth: number;
  filterTracking: number;
  subOctave: number;
  subOctaveLevel: number;
}

export class LaserSweep extends CsoundEngine<LaserSweepParams> {
  getName(): string {
    return 'Laser Sweep';
  }

  getDescription(): string {
    return 'Sweeping laser beam with delay and reverb';
  }

  getType(): 'generative' | 'sample' | 'input' {
    return 'generative';
  }

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

  getOrchestra(): string {
    return `
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1

instr 1
  iDur = p3

  iStartFreq chnget "startFreq"
  iEndFreq chnget "endFreq"
  iSweepCurve chnget "sweepCurve"
  iResonance chnget "resonance"
  iBrightness chnget "brightness"
  iDelayTime chnget "delayTime"
  iDelayFeedback chnget "delayFeedback"
  iReverbMix chnget "reverbMix"
  iReverbSize chnget "reverbSize"
  iNoiseAmount chnget "noiseAmount"
  iDecayShape chnget "decayShape"
  iWaveformMix chnget "waveformMix"
  iMainWaveType chnget "mainWaveType"
  iSecondWaveType chnget "secondWaveType"
  iPulseWidth chnget "pulseWidth"
  iFilterTracking chnget "filterTracking"
  iSubOctave chnget "subOctave"
  iSubOctaveLevel chnget "subOctaveLevel"

  if iDecayShape < 0.33 then
    kEnv expseg 1, iDur * 0.85, 1, iDur * 0.15, 0.001
  elseif iDecayShape < 0.67 then
    kEnv linseg 1, iDur * 0.85, 1, iDur * 0.15, 0
  else
    kEnv expseg 1, iDur * 0.8, 1, iDur * 0.18, 0.001
  endif

  kFreq expseg iStartFreq, iDur * iSweepCurve, iEndFreq
  kFreqLimited limit kFreq, 40, 16000

  if iMainWaveType < 0.2 then
    aMain vco2 0.6, kFreqLimited, 10
  elseif iMainWaveType < 0.4 then
    aMain vco2 0.6, kFreqLimited, 0
  elseif iMainWaveType < 0.6 then
    aMain vco2 0.6, kFreqLimited, 2, iPulseWidth
  elseif iMainWaveType < 0.8 then
    aMain vco2 0.6, kFreqLimited, 12
  else
    aMain oscili 0.6, kFreqLimited
  endif

  if iSecondWaveType < 0.2 then
    aSecond vco2 0.5, kFreqLimited, 2, iPulseWidth
  elseif iSecondWaveType < 0.4 then
    aSecond vco2 0.5, kFreqLimited, 0
  elseif iSecondWaveType < 0.6 then
    aSecond vco2 0.5, kFreqLimited, 12
  elseif iSecondWaveType < 0.8 then
    aSecond vco2 0.5, kFreqLimited, 10
  else
    aSecond oscili 0.5, kFreqLimited * 1.5
  endif

  aNoise rand 0.3
  kNoiseFiltFreq limit kFreqLimited * 0.5, 50, 12000
  aNoise butterbp aNoise, kNoiseFiltFreq, kNoiseFiltFreq * 0.5

  if iSubOctaveLevel > 0.01 then
    if iSubOctave < 0.5 then
      kSubFreq = kFreqLimited * 0.5
    else
      kSubFreq = kFreqLimited * 0.25
    endif
    aSub oscili iSubOctaveLevel * 0.7, kSubFreq
  else
    aSub = 0
  endif

  aOsc = aMain + aSecond * iWaveformMix + aSecond * iBrightness * 0.3 + aNoise * iNoiseAmount + aSub

  kFilterFreq = kFreqLimited * (1 + (1 - iFilterTracking) * 2)
  kFilterFreq limit kFilterFreq, 50, 15000

  kBandwidth = kFilterFreq * (0.1 + iResonance * 0.3)
  aFilt butterbp aOsc, kFilterFreq, kBandwidth

  aFilt = aFilt * kEnv * 0.8

  if iDelayTime > 0.01 then
    aDlyL vdelay3 aFilt, iDelayTime * 1000, 2000
    aDlyR vdelay3 aFilt, iDelayTime * 1000 * 1.1, 2000

    aDlyL = aDlyL * iDelayFeedback * kEnv
    aDlyR = aDlyR * iDelayFeedback * kEnv
  else
    aDlyL = 0
    aDlyR = 0
  endif

  if iReverbMix > 0.01 then
    aWetL, aWetR reverbsc aFilt + aDlyL * 0.5, aFilt + aDlyR * 0.5, iReverbSize, 8000
  else
    aWetL = 0
    aWetR = 0
  endif

  aOutL = aFilt + aDlyL * 0.4 + aWetL * iReverbMix
  aOutR = aFilt + aDlyR * 0.4 + aWetR * iReverbMix

  outs aOutL, aOutR
endin
`;
  }

  getParametersForCsound(params: LaserSweepParams): CsoundParameter[] {
    return [
      { channelName: 'startFreq', value: params.startFreq },
      { channelName: 'endFreq', value: params.endFreq },
      { channelName: 'sweepCurve', value: params.sweepCurve },
      { channelName: 'resonance', value: params.resonance },
      { channelName: 'brightness', value: params.brightness },
      { channelName: 'delayTime', value: params.delayTime },
      { channelName: 'delayFeedback', value: params.delayFeedback },
      { channelName: 'reverbMix', value: params.reverbMix },
      { channelName: 'reverbSize', value: params.reverbSize },
      { channelName: 'noiseAmount', value: params.noiseAmount },
      { channelName: 'decayShape', value: params.decayShape },
      { channelName: 'waveformMix', value: params.waveformMix },
      { channelName: 'mainWaveType', value: params.mainWaveType },
      { channelName: 'secondWaveType', value: params.secondWaveType },
      { channelName: 'pulseWidth', value: params.pulseWidth },
      { channelName: 'filterTracking', value: params.filterTracking },
      { channelName: 'subOctave', value: params.subOctave },
      { channelName: 'subOctaveLevel', value: params.subOctaveLevel }
    ];
  }

  randomParams(pitchLock?: number): LaserSweepParams {
    const isUpward = Math.random() > 0.3;

    const startFreq = isUpward
      ? 60 + Math.random() * 200
      : 1000 + Math.random() * 10000;

    const endFreq = isUpward
      ? 1000 + Math.random() * 10000
      : 60 + Math.random() * 200;

    return {
      startFreq,
      endFreq,
      sweepCurve: 0.2 + Math.random() * 0.7,
      resonance: 0.2 + Math.random() * 0.7,
      brightness: 0.1 + Math.random() * 0.8,
      delayTime: Math.random() < 0.3 ? 0 : 0.05 + Math.random() * 0.5,
      delayFeedback: 0.1 + Math.random() * 0.7,
      reverbMix: Math.random() < 0.2 ? 0 : 0.1 + Math.random() * 0.5,
      reverbSize: 0.4 + Math.random() * 0.5,
      noiseAmount: 0.05 + Math.random() * 0.5,
      decayShape: Math.random(),
      waveformMix: 0.3 + Math.random() * 0.7,
      mainWaveType: Math.random(),
      secondWaveType: Math.random(),
      pulseWidth: 0.1 + Math.random() * 0.8,
      filterTracking: 0.3 + Math.random() * 0.7,
      subOctave: Math.random(),
      subOctaveLevel: Math.random() < 0.4 ? 0 : 0.3 + Math.random() * 0.7
    };
  }

  mutateParams(
    params: LaserSweepParams,
    mutationAmount: number = 0.3,
    pitchLock?: number
  ): LaserSweepParams {
    const mutate = (value: number, min: number, max: number): number => {
      const range = max - min;
      const change = (Math.random() - 0.5) * range * mutationAmount;
      return Math.max(min, Math.min(max, value + change));
    };

    return {
      startFreq: mutate(params.startFreq, 40, 12000),
      endFreq: mutate(params.endFreq, 40, 12000),
      sweepCurve: mutate(params.sweepCurve, 0.1, 0.95),
      resonance: mutate(params.resonance, 0.1, 1.0),
      brightness: mutate(params.brightness, 0.0, 1.0),
      delayTime: mutate(params.delayTime, 0.0, 0.6),
      delayFeedback: mutate(params.delayFeedback, 0.0, 0.85),
      reverbMix: mutate(params.reverbMix, 0.0, 0.7),
      reverbSize: mutate(params.reverbSize, 0.3, 0.95),
      noiseAmount: mutate(params.noiseAmount, 0.0, 0.7),
      decayShape: mutate(params.decayShape, 0.0, 1.0),
      waveformMix: mutate(params.waveformMix, 0.0, 1.0),
      mainWaveType: mutate(params.mainWaveType, 0.0, 1.0),
      secondWaveType: mutate(params.secondWaveType, 0.0, 1.0),
      pulseWidth: mutate(params.pulseWidth, 0.05, 0.95),
      filterTracking: mutate(params.filterTracking, 0.0, 1.0),
      subOctave: mutate(params.subOctave, 0.0, 1.0),
      subOctaveLevel: mutate(params.subOctaveLevel, 0.0, 1.0)
    };
  }
}
