/* El puente entre el HUD y el nucleo, por WebSocket.
 *
 * En la version sobre Newelle el HUD vivia dentro de un widget WebKit y hablaba
 * por window.webkit.messageHandlers. Fuera del flatpak no hay tal widget, pero
 * hud.js usa ese puente en ocho sitios y no merece la pena tocarlos: aqui se
 * SUPLANTA window.webkit con un objeto que manda lo mismo por el socket.
 *
 * Asi hud.js se queda exactamente igual que estaba —cero diff sobre 2.064
 * lineas que ya funcionaban— y ademas sigue abriendose suelto en un navegador
 * para mirar el diseño, que es como se ha estado trabajando en el.
 *
 * Este fichero se carga ANTES que hud.js: cuando hud.js pregunte por
 * window.webkit, ya esta.
 */
(function () {
  "use strict";

  // Ya estamos dentro de WebKit (queda de la epoca de Newelle): no tocar nada.
  if (window.webkit && window.webkit.messageHandlers) return;

  const URL = "ws://" + (location.host || "127.0.0.1:8790") + "/puente";

  let socket = null;
  let cola = [];          // lo que se pulse antes de que abra el socket
  let esperaReconexion = 500;

  function manda(canal, dato) {
    const mensaje = JSON.stringify({ canal: canal, dato: String(dato) });
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.send(mensaje);
    } else {
      // No se pierde: un boton pulsado mientras reconecta tiene que llegar
      // igual, o el panel parece que se ha colgado. Con tope, que si el nucleo
      // esta caido de verdad no queremos acumular mil pulsaciones.
      if (cola.length < 50) cola.push(mensaje);
    }
  }

  // El mismo interfaz que ofrecia WebKit, para que hud.js no note el cambio.
  window.webkit = {
    messageHandlers: {
      accion: { postMessage: (d) => manda("accion", d) },
      listo: { postMessage: (d) => manda("listo", d) },
    },
  };

  function conecta() {
    try {
      socket = new WebSocket(URL);
    } catch (e) {
      reintenta();
      return;
    }

    socket.addEventListener("open", () => {
      esperaReconexion = 500;
      marcaCaido(false);
      const pendientes = cola;
      cola = [];
      pendientes.forEach((m) => socket.send(m));
    });

    socket.addEventListener("message", (ev) => {
      let mensaje;
      try {
        mensaje = JSON.parse(ev.data);
      } catch (e) {
        return;
      }
      // El formato {tipo, datos} es el mismo que empujaba Python por
      // evaluate_javascript, asi que recibe() no cambia.
      if (window.jarvis && window.jarvis.recibe) window.jarvis.recibe(mensaje);
    });

    socket.addEventListener("close", () => { marcaCaido(true); reintenta(); });
    socket.addEventListener("error", () => { try { socket.close(); } catch (e) {} });
  }

  function reintenta() {
    // Espera creciente hasta 8 s: reintentar cada 500 ms contra un nucleo
    // parado calienta el portatil para nada.
    setTimeout(conecta, esperaReconexion);
    esperaReconexion = Math.min(esperaReconexion * 2, 8000);
  }

  // Que se vea que el panel esta hablando solo. Un HUD con datos congelados y
  // sin avisar es peor que uno que dice claramente que perdio el hilo.
  function marcaCaido(caido) {
    document.documentElement.classList.toggle("sin-nucleo", caido);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", conecta);
  } else {
    conecta();
  }
})();
