interface WavePCMConfigType {
  sampleRate?: number;
  bitDepth?: number;
}

interface WavePCMMessageType {
  pcmArrays: Float32Array[];
  config: WavePCMConfigType;
}
export default class WavePCM {
  recordedBuffers: any;
  sampleRate: any;
  bitDepth: any;
  bytesPerSample: any;
  numberOfChannels: any;
  wav: any;

  constructor(config: WavePCMConfigType) {
    this.sampleRate = config.sampleRate || 48000;
    this.bitDepth = config.bitDepth || 16;
    this.recordedBuffers = [];
    this.bytesPerSample = this.bitDepth / 8;
  }

  record = (buffers: any) => {
    this.numberOfChannels = this.numberOfChannels || buffers.length;
    var bufferLength = buffers[0].length;
    var reducedData = new Uint8Array(
      bufferLength * this.numberOfChannels * this.bytesPerSample,
    );

    // Interleave
    for (var i = 0; i < bufferLength; i++) {
      for (
        var channel = 0;
        channel < this.numberOfChannels;
        channel++
      ) {
        var outputIndex =
          (i * this.numberOfChannels + channel) * this.bytesPerSample;
        var sample = buffers[channel][i];

        // Check for clipping
        if (sample > 1) {
          sample = 1;
        } else if (sample < -1) {
          sample = -1;
        }

        // bit reduce and convert to uInt
        switch (this.bytesPerSample) {
          case 4:
            sample = sample * 2147483648;
            reducedData[outputIndex] = sample;
            reducedData[outputIndex + 1] = sample >> 8;
            reducedData[outputIndex + 2] = sample >> 16;
            reducedData[outputIndex + 3] = sample >> 24;
            break;

          case 3:
            sample = sample * 8388608;
            reducedData[outputIndex] = sample;
            reducedData[outputIndex + 1] = sample >> 8;
            reducedData[outputIndex + 2] = sample >> 16;
            break;

          case 2:
            sample = sample * 32768;
            reducedData[outputIndex] = sample;
            reducedData[outputIndex + 1] = sample >> 8;
            break;

          case 1:
            reducedData[outputIndex] = (sample + 1) * 128;
            break;

          default:
            throw "Only 8, 16, 24 and 32 bits per sample are supported";
        }
      }
    }

    this.recordedBuffers.push(reducedData);
  };

  requestData = () => {
    var bufferLength = this.recordedBuffers[0].length;
    var dataLength = this.recordedBuffers.length * bufferLength;
    var headerLength = 44;
    var wav = new Uint8Array(headerLength + dataLength);
    var view = new DataView(wav.buffer);

    view.setUint32(0, 1380533830, false); // RIFF identifier 'RIFF'
    view.setUint32(4, 36 + dataLength, true); // file length minus RIFF identifier length and file description length
    view.setUint32(8, 1463899717, false); // RIFF type 'WAVE'
    view.setUint32(12, 1718449184, false); // format chunk identifier 'fmt '
    view.setUint32(16, 16, true); // format chunk length
    view.setUint16(20, 1, true); // sample format (raw)
    view.setUint16(22, this.numberOfChannels, true); // channel count
    view.setUint32(24, this.sampleRate, true); // sample rate
    view.setUint32(
      28,
      this.sampleRate * this.bytesPerSample * this.numberOfChannels,
      true,
    ); // byte rate (sample rate * block align)
    view.setUint16(
      32,
      this.bytesPerSample * this.numberOfChannels,
      true,
    ); // block align (channel count * bytes per sample)
    view.setUint16(34, this.bitDepth, true); // bits per sample
    view.setUint32(36, 1684108385, false); // data chunk identifier 'data'
    view.setUint32(40, dataLength, true); // data chunk length

    for (var i = 0; i < this.recordedBuffers.length; i++) {
      wav.set(
        this.recordedBuffers[i],
        i * bufferLength + headerLength,
      );
    }

    // WavePCM.postMessage(wav, [wav.buffer]);
  };

  static postMessage = (message: WavePCMMessageType) => {
    const wavPCM = new WavePCM(message.config);
    wavPCM.record(message.pcmArrays);
    wavPCM.requestData();
  };
}

// potential usage:
// const audioBuffer = await AudioSource.getAudioBuffer(
//   player.blob,
// );

// return new Promise(function (resolve, reject) {
//   var worker = new Worker(wavWorker);
//   console.log({ worker });
//   worker.onmessage = function (e) {
//     var blob = new Blob([e.data.buffer], {
//       type: "audio/wav",
//     });
//     resolve(blob);
//   };
// if (audioBuffer) {
//   let pcmArrays = [];
//   for (
//     let i = 0;
//     i < audioBuffer.numberOfChannels;
//     i++
//   ) {
//     pcmArrays.push(audioBuffer.getChannelData(i));
//   }

//   WavePCM.postMessage({
//     pcmArrays,
//     config: { sampleRate: audioBuffer.sampleRate },
//   });
// }
