/**
 *  Copyright © 2025, Luis Andrés Lange <https://javacomm.net>
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Exhibit B - "Incompatible With Secondary Licenses" Notice
 *
 *  This Source Code Form is "Incompatible With Secondary Licenses",
 *  as defined by the Mozilla Public License, v. 2.0.
 *
 *  In short:
 *  - This file may be used, modified, and distributed under MPL 2.0 only.
 *  - It may NOT be relicensed under GPL, LGPL, AGPL, or any other Secondary License.
 *
 *  Rationale:
 *  - Ensures that the code remains MPL-2.0.
 *  - Avoids legal conflicts with GPL-licensed libraries (e.g., VideoLAN).
 *  - Maximizes usability for commercial and security-critical applications.
 *
 */
package net.javacomm.client.screencaster;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.javacomm.window.manager.Control;



public class ImageCaster {

  private final static Logger log = LogManager.getLogger(ImageCaster.class);

  private static ImageCaster instance;
  static {
    instance = new ImageCaster();
  }
  private FrameBuffer buffer;
//Die eigentliche Logik-Klasse (als Runnable) halten
  private SendPicture sendPictureRunnable;
  private Thread createPictureThread;
  private Thread sendPictureThread;
  private Thread mousepointerThread;
  private final AtomicLong lastTakenProductionTimestamp = new AtomicLong(-1L);
  private FpsListener fpsListener;
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);

  private ImageCaster() {
    fpsListener = new FpsListener() {
      @Override
      public void fpsUpdated(FpsEvent event) {
        changes.firePropertyChange("imageCaster", Control.NULL, event.getFps());
      }
    };
  }



  public static ImageCaster getInstance() {
    return instance;
  }



  public void addSendListener(PropertyChangeListener listener) {
    changes.addPropertyChangeListener(listener);
  }



  /**
   * Findet gerade eine Aufzeichnung statt?
   * 
   * @return {@code true}, wenn Bilder gesendet werden
   */
  public boolean hasStarted() {
    if (sendPictureThread == null) return false;
    return sendPictureThread.isAlive();
  }



  /**
   * Die Bildschirmaufzeichnung beginnt. Diese Methode wird von der 1:1
   * Bildschirmübertragung und der Videokonferenz aufgerufen. Die Videokonferenz
   * ist die Telefonkonferenz.
   * 
   * @param pseudoid
   *                      das Bild stammt von diesem Anwender
   * @param conferencekey
   *                      der Conferencekey löst die Empfänger auf
   */
  public void recordScreenshots(String pseudoid, String conferencekey) {
    stopRecording();
    buffer = new FrameBuffer();

    // 1. CreatePicture starten (angenommen es ist jetzt ein Runnable)
    createPictureThread = Thread.ofVirtual().name("v-create-picture")
        .start(new CreatePicture(buffer, lastTakenProductionTimestamp));

    // 2. SendPicture initialisieren, Listener binden und im Virtual Thread starten
    sendPictureRunnable = new SendPicture(buffer, lastTakenProductionTimestamp, pseudoid, conferencekey);
    sendPictureRunnable.addFpsListener(fpsListener); // Das geht jetzt wieder, da der Typ stimmt!

    sendPictureThread = Thread.ofVirtual().name("v-send-picture").start(sendPictureRunnable);

    // 3. Mousepointer starten (angenommen es ist jetzt ein Runnable)
    mousepointerThread = Thread.ofVirtual().name("v-mousepointer")
        .start(new Mousepointer(pseudoid, conferencekey));
  }



  public void removeSendListener(PropertyChangeListener listener) {
    changes.removePropertyChangeListener(listener);
  }



  /**
   * 
   * Screenshots werden nicht mehr aufgezeichnet und versendet. Ein mehrmaliger
   * Aufruf bewirkt nichts Neues.
   * 
   * 
   */
  public void stopRecording() {
    // 1. Interrupts an die virtuellen Threads senden
    if (mousepointerThread != null) mousepointerThread.interrupt();
    if (createPictureThread != null) createPictureThread.interrupt();
    if (sendPictureThread != null) sendPictureThread.interrupt();

    // 2. Auf das Ende der Threads warten (Joins)
    try {
      if (createPictureThread != null) createPictureThread.join();
    }
    catch (InterruptedException e) {
      log.error(e.getMessage());
    }

    try {
      if (sendPictureThread != null) sendPictureThread.join();
    }
    catch (InterruptedException e) {
      log.error(e.getMessage());
    }

    // 3. Listener sauber über die Runnable-Referenz entfernen
    if (sendPictureRunnable != null) {
      sendPictureRunnable.removeFpsListener(fpsListener); // Typ stimmt wieder!
    }

    try {
      if (mousepointerThread != null) mousepointerThread.join();
    }
    catch (InterruptedException e) {
      log.info(e.getMessage(), e);
    }
  }
}
