import FFTRunner from '../signal-processing/FFTRunner.js';

import decode from '../signal-processing/decodeAudio.js';

const pause = () => new Promise((resolve) => setTimeout(resolve, 0));

const generateBandEnergy = async (data: Buffer) => {
  const output: number[][] = [[], [], [], []];
  const audioBuffer = await decode(data, { sampleRate: 44100 });
  const { channelData } = audioBuffer;

  const monoData = Array.from({ length: channelData[0].length }, () => 0);

  for (let i = 0; i < channelData.length; i++) {
    const channelSamples = channelData[i];
    for (let j = 0; j < channelSamples.length; j++) {
      monoData[j] += channelSamples[j];
    }
  }

  monoData.forEach((sample, i) => {
    monoData[i] = sample / channelData.length;
  });

  const fftSize = 2048; // window size - how many samples is runner looking at at a time
  const hopSize = 512; // amount window jumps forward each time
  const lfBinCutoff = fftSize / 300;
  const lmfBinCutoff = fftSize / 150;
  const hfBinCutoff = fftSize / 50;

  const runner = new FFTRunner(fftSize, hopSize);
  for (let i = 0; i < monoData.length; i++) {
    if (i % 2048 === 0) {
      await pause();
    }

    const curr = monoData[i];
    runner.receiveSample(curr);
    if (runner.isProcessingStep) {
      runner.updateSpectrum();
      let totalLFAmp = 0;
      let totalLMFAmp = 0;
      let totalMFAmp = 0;
      let totalHFAmp = 0;

      runner.spectrumOutput.forEach((amplitude, i) => {
        if (i < lfBinCutoff) {
          totalLFAmp += amplitude;
        } else if (i < lmfBinCutoff) {
          totalLMFAmp += amplitude;
        } else if (i < hfBinCutoff) {
          totalMFAmp += amplitude;
        } else {
          totalHFAmp += amplitude;
        }
      });

      output[0].push(totalLFAmp);
      output[1].push(totalLMFAmp);
      output[2].push(totalMFAmp);
      output[3].push(totalHFAmp);
    }
  }

  return output;
};

export default generateBandEnergy;
