const spawn = require("child_process").spawn;
const { WebSocketServer } = require("ws");

module.exports = class MicrophoneServer {
  constructor({ server }) {
    const path = `/microphone`;
    logger.info(`Microphone websocker server starting`, path);
    const wss = new WebSocketServer(
      {
        noServer: true,
        path,
      },
      () => {
        logger.info(`Microphone websocker server started`, path);
      }
    );

    server.on("upgrade", (request, socket, head) => {
      if (request.url === path)
        wss.handleUpgrade(request, socket, head, (ws) => {
          wss.emit("connection", ws, request);
        });
    });

    this.wss = wss;

    wss.on("connection", (socket) => {
      logger.info(`客户端已连接  Microphone Websocket Server`);
      socket.on("close", () => {
        if (wss.clients.size === 0) {
          this.close();
        }
      });

      this.open();
    });

    wss.on("error", (err) => {
      logger.error(err);
    });
  }

  async open() {
    logger.info(`Microphone start`);
    if (this.streamer) {
      logger.info(`Microphone ffmpeg streamer already open`);
      this.close();
    }
    this.streamer = ffmpeg();
    this.streamer.stdout.on("data", (data) => {
      this.broadcastStream(data);
    });
  }

  close() {
    if (this.streamer) {
      logger.info(`Microphone ffmpeg streamer killing`);
      this.streamer.kill("SIGHUP");
      this.streamer = undefined;
    }
  }

  sendBinary(socket, frame) {
    if (socket.buzy) return;
    socket.buzy = true;
    socket.buzy = false;

    socket.send(frame, { binary: true }, function ack() {
      socket.buzy = false;
    });
  }

  broadcast(action, payload) {
    this.wss.clients.forEach((socket) =>
      socket.send(JSON.stringify({ action, payload }))
    );
  }

  broadcastStream(data) {
    this.wss.clients.forEach((socket) => {
      this.sendBinary(socket, data);
    });
  }
};

const ffmpeg = function () {
  const device = process.env.NRC_MIC_DEVICE || "default";
  const sampleRate = Number(process.env.NRC_MIC_SAMPLE_RATE || 16000);
  const channels = Number(process.env.NRC_MIC_CHANNELS || 1);

  if (process.env.NRC_MIC_LOG === "1") {
    logger.info(
      `Microphone ffmpeg start: device=${device}, sampleRate=${sampleRate}, channels=${channels}`
    );
  }

  const streamer = spawn("ffmpeg", [
    "-f",
    "alsa",
    "-thread_queue_size",
    "1024",
    "-ar",
    sampleRate,
    "-ac",
    channels,
    "-i",
    device,
    "-c:a",
    "libmp3lame",
    "-b:a",
    "64k",
    "-f",
    "mp3",
    "-",
  ]);

  streamer.on("close", () => {
    logger.info(" Microphone ffmpeg close");
  });

  streamer.stderr.on("data", (data) => {
    if (process.env.NRC_MIC_LOG === "1") {
      logger.info(`Microphone ffmpeg stderr: ${data.toString()}`);
    }
  });

  return streamer;
};
