let leftchannel = [];
let rightchannel = [];
let recording = false;
let paused = false;
let recordingLength = 0;
let volume = null;
let audioInput = null;
let sampleRate = null;
let AudioContext = window.AudioContext || window.webkitAudioContext;
let context = null;
let analyser = null;
let canvas = document.querySelector("canvas");
let canvasCtx = canvas.getContext("2d");
let micSelect = document.querySelector("#micSelect");
let stream = null;
let tested = false;
let timerInterval = null;
let elapsedTime = 0;
let drawVisual;

async function getStream(constraints) {
   if (!constraints) {
      constraints = { audio: true, video: false };
   }
   return navigator.mediaDevices.getUserMedia(constraints);
}

async function initStream() {
   try {
      window.stream = stream = await getStream();
      console.log("Got stream");
   } catch (err) {
      alert("Issue getting mic", err);
   }
}

var mics = [];
navigator.mediaDevices.enumerateDevices().then((deviceInfos) => {
   for (let i = 0; i < deviceInfos.length; i++) {
      let deviceInfo = deviceInfos[i];
      if (deviceInfo.kind === "audioinput") {
         mics.push(deviceInfo);
         let label = deviceInfo.label || "Microphone " + mics.length;
         const option = document.createElement("option");
         option.value = deviceInfo.deviceId;
         option.text = label;
         micSelect.appendChild(option);
      }
   }
});

async function setUpRecording() {
   context = new AudioContext();
   sampleRate = context.sampleRate;
   volume = context.createGain();
   audioInput = context.createMediaStreamSource(stream);
   analyser = context.createAnalyser();
   audioInput.connect(analyser);

   let bufferSize = 2048;
   const recorder = context.createScriptProcessor(bufferSize, 2, 2);
   analyser.connect(recorder);
   recorder.connect(context.destination);

   recorder.onaudioprocess = function (e) {
      if (!recording || paused) return;
      let left = e.inputBuffer.getChannelData(0);
      let right = e.inputBuffer.getChannelData(1);
      if (!tested) {
         tested = true;
         if (!left.reduce((a, b) => a + b)) {
            alert("There seems to be an issue with your Mic");
            stop();
            stream.getTracks().forEach(function (track) {
               track.stop();
            });
            context.close();
         }
      }
      leftchannel.push(new Float32Array(left));
      rightchannel.push(new Float32Array(right));
      recordingLength += bufferSize;
   };
   visualize();
}

function mergeBuffers(channelBuffer, recordingLength) {
   let result = new Float32Array(recordingLength);
   let offset = 0;
   let lng = channelBuffer.length;
   for (let i = 0; i < lng; i++) {
      let buffer = channelBuffer[i];
      result.set(buffer, offset);
      offset += buffer.length;
   }
   return result;
}

function interleave(leftChannel, rightChannel) {
   let length = leftChannel.length + rightChannel.length;
   let result = new Float32Array(length);
   let inputIndex = 0;
   for (let index = 0; index < length; ) {
      result[index++] = leftChannel[inputIndex];
      result[index++] = rightChannel[inputIndex];
      inputIndex++;
   }
   return result;
}

function writeUTFBytes(view, offset, string) {
   let lng = string.length;
   for (let i = 0; i < lng; i++) {
      view.setUint8(offset + i, string.charCodeAt(i));
   }
}

async function start() {
   await initStream();
   recording = true;
   paused = false;
   document.querySelector("#msg").style.visibility = "visible";
   leftchannel.length = rightchannel.length = 0;
   recordingLength = 0;
   elapsedTime = 0;
   clearInterval(timerInterval);
   timerInterval = setInterval(updateTimer, 1000);
   document.querySelector("#timer").textContent = "00:00";
   setUpRecording();
}

function pause() {
   paused = true;
   clearInterval(timerInterval);
}

function resume() {
   paused = false;
   timerInterval = setInterval(updateTimer, 1000);
}

function stop() {
   try {
      recording = false;
      paused = false;
      clearTimerAndHideMsg();
      cancelAnimationFrame(drawVisual);
      // Detener el stream y cerrar contexto
      stream.getTracks().forEach((track) => track.stop());
      if (context) {
         context.close();
         context = null;
      }
   } catch (error) {}
}

async function save() {
   stop();
   let leftBuffer = mergeBuffers(leftchannel, recordingLength);
   let rightBuffer = mergeBuffers(rightchannel, recordingLength);
   let interleaved = interleave(leftBuffer, rightBuffer);
   let buffer = new ArrayBuffer(44 + interleaved.length * 2);
   let view = new DataView(buffer);
   writeUTFBytes(view, 0, "RIFF");
   view.setUint32(4, 44 + interleaved.length * 2, true);
   writeUTFBytes(view, 8, "WAVE");
   writeUTFBytes(view, 12, "fmt ");
   view.setUint32(16, 16, true);
   view.setUint16(20, 1, true);
   view.setUint16(22, 2, true);
   view.setUint32(24, sampleRate, true);
   view.setUint32(28, sampleRate * 4, true);
   view.setUint16(32, 4, true);
   view.setUint16(34, 16, true);
   writeUTFBytes(view, 36, "data");
   view.setUint32(40, interleaved.length * 2, true);
   let lng = interleaved.length;
   let index = 44;
   let volume = 1;
   for (let i = 0; i < lng; i++) {
      view.setInt16(index, interleaved[i] * (0x7fff * volume), true);
      index += 2;
   }
   const blob = new Blob([view], { type: "audio/ogg; codecs=opus" });

   // Verificar el tamaño del archivo
   if (blob.size > 15 * 1024 * 1024) {
      return Promise.reject({
         error: "error",
         mensaje: "The file exceeds the maximum allowed size of 15 MB.",
         data: "",
      });
   }

   return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onloadend = function () {
         const base64data = reader.result;

         if (base64data) {
            const base64WithoutPrefix = base64data.split(",")[1];
            resolve({
               error: "success",
               mensaje: "",
               data: base64WithoutPrefix,
            });
         } else {
            reject({
               error: "error",
               mensaje: "Error reading the file.",
               data: "",
            });
         }
      };
      reader.onerror = function () {
         reject({
            error: "error",
            mensaje: "Error reading the file.",
            data: "",
         });
      };
   });
}

function clearTimerAndHideMsg() {
   clearInterval(timerInterval);
   elapsedTime = 0;
   document.querySelector("#timer").textContent = "00:00";
   //document.querySelector("#msg").style.visibility = "hidden";
}

function visualize() {
   WIDTH = canvas.width;
   HEIGHT = canvas.height;
   if (!analyser) return;
   analyser.fftSize = 2048;
   var bufferLength = analyser.fftSize;
   var dataArray = new Uint8Array(bufferLength);
   canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

   var draw = function () {
      drawVisual = requestAnimationFrame(draw);

      if (paused) return;

      analyser.getByteTimeDomainData(dataArray);
      canvasCtx.fillStyle = "rgb(248, 248, 248)";
      canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
      canvasCtx.lineWidth = 2;
      canvasCtx.strokeStyle = "rgb(0, 0, 0)";
      canvasCtx.beginPath();
      var sliceWidth = (WIDTH * 1.0) / bufferLength;
      var x = 0;
      for (var i = 0; i < bufferLength; i++) {
         var v = dataArray[i] / 128.0;
         var y = (v * HEIGHT) / 2;
         if (i === 0) {
            canvasCtx.moveTo(x, y);
         } else {
            canvasCtx.lineTo(x, y);
         }
         x += sliceWidth;
      }
      canvasCtx.lineTo(canvas.width, canvas.height / 2);
      canvasCtx.stroke();
   };
   draw();
}

micSelect.onchange = async (e) => {
   stream.getTracks().forEach(function (track) {
      track.stop();
   });
   if (context) {
      context.close();
      context = null;
   }
   stream = await getStream({
      audio: { deviceId: { exact: micSelect.value } },
      video: false,
   });
   setUpRecording();
};

/* document.querySelector("#record").onclick = (e) => {
      start();
   };

   document.querySelector("#pause").onclick = (e) => {
      pause();
   };

   document.querySelector("#resume").onclick = (e) => {
      resume();
   };

   document.querySelector("#stop").onclick = (e) => {
      stop();
   };

   document.querySelector("#save").onclick = (e) => {
      save();
   }; */

function updateTimer() {
   if (paused) return;
   elapsedTime += 1;
   let hours = Math.floor(elapsedTime / 3600);
   let minutes = Math.floor((elapsedTime % 3600) / 60);
   let seconds = elapsedTime % 60;
   let timerText;
   if (hours > 0) {
      timerText = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(
         2,
         "0",
      )}:${String(seconds).padStart(2, "0")}`;
   } else {
      timerText = `${String(minutes).padStart(2, "0")}:${String(
         seconds,
      ).padStart(2, "0")}`;
   }
   document.querySelector("#timer").textContent = timerText;
}
