let streaming;
var current_stream;
var current_camera_is;
var supports_facing_mode;
var camera_options;

var width = document.getElementById("video").parentNode.parentElement.clientWidth;
var height = width / (4 / 3);
var socket = io();

socket.on('connect', function() {
    socket.emit('ping', {data: 'I\'m connected!'});
    console.log("Sent a ping to the server :)");
});


socket.on('pong', () => {
  console.log('Received pong back ! ');
});

socket.on('new_image', () => {
    console.log("Received new image event");
    const img = document.getElementById('snapshot');
    img.src = '/image?' + new Date().getTime();  // Bust cache
});


async function startup(){
  video = document.getElementById('video');
  canvas = document.getElementById('canvas');
  photo = document.getElementById('photo');
  switch_cameras = document.getElementById('flip')
  printButton = document.getElementById('print_button');

  console.log("Checking for client webcam capabilities");
  // We have a camera_options dictionnary in return, or false if the device does not support webcams.
  let client_webcam_capabilities = await check_webcam_capabilies();


  if ( client_webcam_capabilities != false ){
    get_webcam(client_webcam_capabilities);
    setup_events();
    clear_canvas();
  } else {
    console.log("Checking for server webcam capabilities");
    let server_webcam_capabilities = await check_server_camera();
    if (server_webcam_capabilities === true ){
      console.log("The server has a camera, using it.");
      console.log("TODO");
    }
    else {
      no_webcam_error();
      console.log("Seems like it's impossible to get a webcam from the client nor the server.");
    }
  }
}

async function check_webcam_capabilies(){
  console.log("Checking for a the capabilities of the client's media devices");

  // We try to start with the front facing camera,
  // if we have no support, we switch back to a normal camera.
  try {
    // We first check if the navigator has a media device
    if (!navigator.mediaDevices?.enumerateDevices) {
      console.log("enumerateDevices() not supported.");
      return false;
    } else {
      // List cameras and microphones.
      console.log("The device has the following media devices : ")
      await navigator.mediaDevices
        .enumerateDevices()
        .then((devices) => {
          devices.forEach((device) => {
            console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
          });
        })
        .catch((err) => {
          console.error(`${err.name}: ${err.message}`);
          return false;
        });
    }
  } catch (e) {
    console.log("The device does not seem to support webcams " + e);
    return false;
  }

  console.log("Checking for the supported constraints of the media devices ");
  try {
    const supports = navigator.mediaDevices.getSupportedConstraints();
    if (!supports['facingMode']) {
      throw new Error("This browser does not support facingMode!");
    } else {
      console.log("The device supports facing mode, selecting the front camera by default");
      supports_facing_mode = true;
      camera_options = {
        video: {
           facingMode: 'user', // Or 'environment' if we want a camera facing away
        },
        audio: false
      };
      return camera_options;
    }
  } catch (e) {
    console.log("Resetting to default camera : " + e);
    supports_facing_mode = false;
    camera_options = {
      video: true,
      audio: false
    };
    return camera_options
  }
}

async function get_webcam(options){
  stop_video_streams();

  try {
    navigator.mediaDevices.getUserMedia(options)
    .then(function(stream) {
      // on success, stream it in video tag
      // the video tag is hidden, as is the canvas.
      console.log("Got the webcam stream");

      printButton.removeAttribute("disabled","");
      current_stream = stream;
      video.srcObject = stream;
      video.setAttribute('autoplay', '');
      video.setAttribute('muted', '');
      video.setAttribute('playsinline', '')
      video.play();
      return true;
    })
    .catch(function(err) {
        console.log("Didn't manage to get a camera :" + err);
        return false;
    });
  } catch (err) {
    console.log("Didn't manage to get a camera :" + err);
    return false;
  }
}

async function check_server_camera(){
  console.log("Checking for a camera on the server");

  await socket.emit('get_camera_status', (response) => {
    if (response){
      console.log("Server has a camera !");
      return true;
    } else {
      console.log("The server doesn't seem to have a camera");
      return false;
    }
  });

}

function setup_events(){

  // When the video is ready, we start streaming it to the canvas.
  // The canvas is hidden, but it still exists in the browser.
  video.addEventListener('canplay', function(ev) {
      if (!streaming) {
          height = video.videoHeight / (video.videoWidth / width);

          if (isNaN(height)) {
              height = width / (4 / 3);
          }

          video.setAttribute('width', width);
          video.setAttribute('height', height);
          canvas.setAttribute('width', width);
          canvas.setAttribute('height', height);

          photo.setAttribute('width', width);
          photo.setAttribute('height', height);


          streaming = true;
          printButton.removeAttribute("disabled","");
          switch_cameras.removeAttribute("disabled","");


      }
  }, false);

  switch_cameras.addEventListener('click', function(ev) {
    flip_cameras();
  }, false );

  printButton.addEventListener('click', async function(ev) {
    ev.preventDefault();
    try {
      await countDownToPicture();         // wait for countdown to finish
      var data = await take_picture();  // take_picture can be async
      print_picture(data);
    } catch (e) {
      alert("Failed to print a picture because : " + e);
    }
  }, false);

}

function clear_canvas() {
    var context = canvas.getContext('2d');
    context.fillStyle = "#AAA";
    context.fillRect(0, 0, canvas.width, canvas.height);

    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
}

function dataURLtoFile(dataurl, filename) {
  if(dataurl === "") {
    console.log("We didn't receive data");
  }

  var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  while(n--){
      u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, {type:mime});
}

async function take_picture() {
  const context = canvas.getContext('2d');
  let data = null;
  if (width && height) {
    canvas.width = width;
    canvas.height = height;
    context.drawImage(video, 0, 0, width, height);
    data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  } else {
    clear_canvas();
  }
  return data;
}

function print_picture(data){
  var url = "/api/print/img"
  var picture = dataURLtoFile(data);
  const formData = new FormData();
  let currentDate = new Date();
  let time = currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();

  formData.set("img", picture, "picture.png");
  formData.set("signature", "Webcam")

  fetch(url, {
    method: 'POST', // or 'PUT'
    body: formData,
    // headers:{
    //   'Content-Type': 'multipart/form-data'
    // }
  }).then(function(response) {
    console.log('Reponse:', response);
    if(response.status != 200 ){
      alert("The picture could not be printed be : " + response.statusText)
    }
  } , true)
  .catch(error => console.error('Error:', error), false);

}

function flip_cameras(){
  console.log("Current facing mode : " + camera_options.video.facingMode)
  if ( supports_facing_mode ) {
    console.log("Support facing mode, trying to switch !");
    if (camera_options.video.facingMode == 'user'){
      camera_options = { audio: false, video: { facingMode: "environment" },};
    } else {
      camera_options = { audio: false, video: { facingMode: "user" },};
    }
  }
  console.log("Switching to " + camera_options.video.facingMode );
  get_webcam(camera_options);
}

function stop_video_streams(){
  console.log("Stopping existing video streams.");
  // Stop the tracks
  try {
    if (current_stream) {
      const tracks = current_stream.getTracks();
      tracks.forEach(track => track.stop());
      console.log("Stopped playing current videostreams.");
      return true;
    } else {
      console.log("No streams currently playing.");
    }
  } catch (e) {
    console.log("Could not stop playing current streams." + e);
  }
}

function no_webcam_error(){
  console.log("Seems like they is no webcam available.")

  // We disable the print button is it cannot be clicked.
  printButton.setAttribute("disabled","");

  // We create an alert message.
  const frame_div = document.getElementById("image_dither");
  frame_div.removeAttribute('class');

  const alert_div = document.createElement("div");
  alert_div.setAttribute("class", "alert alert-warning");
  alert_div.setAttribute("role", "alert");

  var alert_message = document.createTextNode("We where unable to get a Webcam device, this page will not work.");
  alert_div.appendChild(alert_message);
  frame_div.appendChild(alert_div);
  throw new Error("Unable to get a video device, stopping the photobooth.");
}

async function countDownToPicture(){
  return new Promise((resolve) => {
    console.log("Starting countdown");
    // We create full-page overlay that displays a number to countdown until the picture is taken.
    const countdown_div = document.getElementById("countdown");
    countdown_div.style.display = "block";

    // Set the different styling attributes of the element
    countdown_div.setAttribute("class", "fullscreen-countdown");

    // The loop must run for 3 seconds
    interval = 1000;
    var loops = 3;

    var x = setInterval(() => {
      console.log(loops);
      // Update the content of the div
      document.getElementById("countdown-number").innerHTML = loops;
      if (loops == 0) {
        countdown_div.style.display = "none";
        // The timer has finished
        clearInterval(x);
        resolve();
      }
      loops--;
    }, interval);
  });
}

window.addEventListener('load', startup, false);
