const { RTCAudioSource } = require("@koush/wrtc").nonstandard;
const ffmpeg = require("fluent-ffmpeg");
const { spawn } = require("child_process");

const sampleRate = 48000;

class NodeWebRtcAudioSource extends RTCAudioSource {
  constructor() {
    super();
    this.command = null;
    this.commandProcess = null;
    this.cache = Buffer.alloc(0);
  }

  createTrack() {
    const track = super.createTrack();
    // if (this.command === null) {
    //   this.start();
    // }
    return track;
  }

  async start() {
    if (this.command !== null || this.commandProcess !== null) {
      this.stop();
    }
    logger.info("WebRTC Audio Microphone Start");

    const captureEnv = process.env.NRC_RTC_AUDIO_CAPTURE;
    const capture = (
      captureEnv || (process.platform === "linux" ? "arecord" : "ffmpeg")
    ).toLowerCase();
    const inputDevice = process.env.NRC_RTC_AUDIO_DEVICE || "default";
    const inputFormat = process.env.NRC_RTC_AUDIO_FORMAT || "pulse";
    const inputSampleRate = Number(
      process.env.NRC_RTC_AUDIO_SAMPLE_RATE || sampleRate
    );
    const inputChannels = Number(process.env.NRC_RTC_AUDIO_CHANNELS || 1);

    if (capture === "arecord") {
      const args = [
        "-D",
        inputDevice,
        "-f",
        "S16_LE",
        "-r",
        String(inputSampleRate),
        "-c",
        String(inputChannels),
        "-t",
        "raw",
      ];

      logger.info(
        `WebRTC Audio Microphone Open: capture=arecord, device=${inputDevice}, rate=${inputSampleRate}, channels=${inputChannels}`
      );

      this.commandProcess = spawn("arecord", args);
      this.commandProcess.on("error", (err) => {
        logger.info("WebRTC Audio Microphone: error" + err.message);
      });
      this.commandProcess.on("close", (code, signal) => {
        logger.info(`WebRTC Audio Microphone closed: ${code || 0} ${signal || ""}`);
      });
      this.commandProcess.stderr.on("data", (data) => {
        if (process.env.NRC_RTC_AUDIO_LOG === "1") {
          logger.info("WebRTC Audio Microphone stderr: " + data.toString());
        }
      });
      this.ffstream = this.commandProcess.stdout;
    } else {
      this.command = ffmpeg(inputDevice)
        .inputFormat(inputFormat)
        .audioChannels(inputChannels)
        .audioFrequency(inputSampleRate)
        .audioCodec("pcm_s16le")
        .outputFormat("s16le")
        .on("start", () => {
          logger.info(
            `WebRTC Audio Microphone Open: device=${inputDevice}, format=${inputFormat}, rate=${inputSampleRate}, channels=${inputChannels}`
          );
        })
        .on("error", function (err) {
          logger.info("WebRTC Audio Microphone: error" + err.message);
        })
        .on("stderr", function (stderrLine) {
          if (process.env.NRC_RTC_AUDIO_LOG === "1") {
            logger.info("WebRTC Audio Microphone stderr: " + stderrLine);
          }
        })
        .on("end", function () {
          logger.info("WebRTC Audio Microphone processing finished !");
        });

      this.ffstream = this.command.pipe();
    }

    this.ffstream.on("data", (buffer) => {
      // logger.info("Video ffmpeg data length", buffer.length);
      this.cache = Buffer.concat([this.cache, buffer]);
    });
    this.ffstream.on("drain", () => {
      logger.info("WebRTC Audio Microphone stream drain");
    });
    this.ffstream.on("error", () => {
      logger.info("WebRTC Audio Microphone stream error");
    });
    this.ffstream.on("finish", () => {
      logger.info("WebRTC Audio Microphone stream finish");
    });

    const processData = () => {
      while (this.cache.length > 960) {
        const buffer = this.cache.slice(0, 960);
        this.cache = this.cache.slice(960);
        const samples = new Int16Array(new Uint8Array(buffer).buffer);
        this.onData({
          bitsPerSample: 16,
          sampleRate,
          channelCount: 1,
          numberOfFrames: samples.length,
          type: "data",
          samples,
        });
      }
      if (this.command !== null || this.commandProcess !== null) {
        setTimeout(() => processData(), 10);
      }
    };
    processData();
  }

  stop() {
    if (this.command !== null) {
      logger.info("WebRTC Audio Source stop");
      this.command.kill("SIGHUP");
      this.command = null;
    }
    if (this.commandProcess !== null) {
      logger.info("WebRTC Audio Source stop (arecord)");
      this.commandProcess.kill("SIGINT");
      this.commandProcess = null;
    }
  }
}

module.exports = NodeWebRtcAudioSource;
