import type { AudioProcessor, ProcessorCategory } from './AudioProcessor';

export class Stutter implements AudioProcessor {
  getName(): string {
    return 'Stutter';
  }

  getDescription(): string {
    return 'Rapidly repeats small fragments with smooth crossfades';
  }

  getCategory(): ProcessorCategory {
    return 'Time';
  }

  process(
    leftChannel: Float32Array,
    rightChannel: Float32Array
  ): [Float32Array, Float32Array] {
    const length = leftChannel.length;
    const sampleRate = 44100;

    const minFragmentMs = 10;
    const maxFragmentMs = 50;
    const minFragmentSamples = Math.floor((minFragmentMs / 1000) * sampleRate);
    const maxFragmentSamples = Math.floor((maxFragmentMs / 1000) * sampleRate);

    const crossfadeSamples = Math.floor(sampleRate * 0.002);

    const outputLeft = new Float32Array(length);
    const outputRight = new Float32Array(length);

    let readPos = 0;
    let writePos = 0;

    while (readPos < length && writePos < length) {
      const fragmentSize = Math.floor(
        Math.random() * (maxFragmentSamples - minFragmentSamples) + minFragmentSamples
      );
      const actualFragmentSize = Math.min(fragmentSize, length - readPos);

      const numRepeats = Math.floor(Math.random() * 4) + 1;

      for (let repeat = 0; repeat < numRepeats; repeat++) {
        for (let i = 0; i < actualFragmentSize && writePos < length; i++, writePos++) {
          let leftSample = leftChannel[readPos + i];
          let rightSample = rightChannel[readPos + i];

          if (repeat > 0 && i < crossfadeSamples) {
            const crossfadePos = i / crossfadeSamples;
            const fadeIn = this.easeInOut(crossfadePos);
            const fadeOut = 1.0 - fadeIn;

            const prevIdx = readPos + actualFragmentSize - crossfadeSamples + i;
            if (prevIdx >= 0 && prevIdx < length) {
              leftSample = leftChannel[prevIdx] * fadeOut + leftSample * fadeIn;
              rightSample = rightChannel[prevIdx] * fadeOut + rightSample * fadeIn;
            }
          }

          outputLeft[writePos] = leftSample;
          outputRight[writePos] = rightSample;
        }

        if (writePos >= length) break;
      }

      readPos += actualFragmentSize;
    }

    return [outputLeft, outputRight];
  }

  private easeInOut(t: number): number {
    return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
  }
}
