/**
 * Energiesparmodus des Rechners.
 *
 * **Wozu.** Die Prüfungssimulation misst ihre Restzeit an der Wanduhr:
 * `endeMs = startMs + Minuten`, und `verbrauchtMs = Date.now() − startMs`.
 * Klappt jemand mitten in einem 120-Minuten-Bogen den Laptop zu, zählt die
 * gesamte Schlafzeit als Bearbeitungszeit – nach einer Mittagspause ist der
 * Bogen beim Aufwachen abgelaufen, und der Herzschlag der Simulation schreibt
 * die aufgeblähte Dauer sogar in die Datenbank. Dieses Modul misst, wie lange
 * der Rechner wirklich geschlafen hat, und meldet es der Oberfläche; die
 * schreibt ihren Zeitursprung um denselben Betrag vor. Die Uhr steht damit
 * während des Schlafs still – dieselbe Zusage, die der Schließen-Dialog in
 * {@link ./fenster} für das Beenden schon macht.
 *
 * **Warum das nicht im Renderer gemessen wird.** Naheliegend wäre ein
 * Sekundentakt, der prüft, ob zwischen zwei Takten mehr als eine Sekunde
 * vergangen ist. Chromium drosselt Zeitgeber in verdeckten Fenstern jedoch auf
 * einen Takt pro Minute: Ein bloß **minimiertes** Fenster sähe damit aus wie
 * ein schlafender Rechner, und die Prüfungsuhr ließe sich durch Minimieren
 * anhalten. Das wäre ein Schummelweg, kein Nachteilsausgleich. `powerMonitor`
 * meldet ausschließlich echten Energiesparmodus und ist von der Drosselung
 * unberührt.
 *
 * **Was hier bewusst nicht behandelt wird.** Ein Sprung der Systemuhr (NTP,
 * manuelles Stellen) verfälscht dieselbe Rechnung, wird hier aber nicht
 * abgefangen: Ihn vom Energiesparmodus zu unterscheiden verlangt eine zweite,
 * monotone Zeitquelle, deren Verhalten im Schlaf je nach Plattform abweicht –
 * und beide Quellen zugleich zu korrigieren führte zur Doppelgutschrift. Der
 * Bearbeitungsdauer ist damit weiterhin nur bis zur Grenze der
 * Kappung zu trauen, wenn jemand während eines Laufs die Uhr stellt.
 *
 * **Die Bildschirmsperre zählt mit.** `lock-screen` wird nicht abgehört: Ein
 * gesperrter, laufender Rechner ist etwas anderes als ein schlafender. Wer den
 * Bildschirm sperrt und weggeht, bearbeitet den Bogen nicht – aber er könnte
 * ihn auf einem zweiten Gerät nachschlagen. Die Uhr läuft dort weiter.
 */

import { BrowserWindow, powerMonitor } from 'electron';

import { KANAL_ENERGIESPAREN_ENDE } from '../shared/ipc';

/**
 * Kürzeste Schlafdauer, die gemeldet wird.
 *
 * Zwischen `suspend` und `resume` liegt selbst bei einem sofort abgebrochenen
 * Standby ein Rest an Zeit. Eine Gutschrift von Bruchteilen einer Sekunde
 * verschöbe den Zeitursprung ohne erkennbaren Anlass und ließe die Ansage
 * grundlos erscheinen.
 */
const MINDESTDAUER_MS = 2000;

/** Zeitpunkt des Einschlafens; `null`, solange der Rechner wach ist. */
let eingeschlafenUm: number | null = null;

/** Ob die Zuhörer hängen – ein zweites Anmelden meldete doppelt. */
let angemeldet = false;

function beiSuspend(): void {
  eingeschlafenUm = Date.now();
}

function beiResume(): void {
  const beginn = eingeschlafenUm;
  eingeschlafenUm = null;
  if (beginn === null) {
    return;
  }

  const schlafMs = Date.now() - beginn;
  /* Auch gegen einen Uhrsprung während des Schlafs abgesichert: Eine negative
     oder winzige Dauer wird nicht gemeldet, statt den Ursprung zu verschieben. */
  if (schlafMs < MINDESTDAUER_MS) {
    return;
  }

  for (const fenster of BrowserWindow.getAllWindows()) {
    if (!fenster.isDestroyed()) {
      fenster.webContents.send(KANAL_ENERGIESPAREN_ENDE, schlafMs);
    }
  }
}

/**
 * Beginnt, den Energiesparmodus zu beobachten.
 *
 * Mehrfaches Aufrufen ist folgenlos.
 */
export function energieUeberwachen(): void {
  if (angemeldet) {
    return;
  }
  powerMonitor.on('suspend', beiSuspend);
  powerMonitor.on('resume', beiResume);
  angemeldet = true;
}

/** Gegenstück für Tests und sauberes Herunterfahren. */
export function energieVergessen(): void {
  if (!angemeldet) {
    return;
  }
  powerMonitor.off('suspend', beiSuspend);
  powerMonitor.off('resume', beiResume);
  angemeldet = false;
  eingeschlafenUm = null;
}
