/**
 *  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.awt.AWTException;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicLong;
import javax.imageio.ImageIO;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.javacomm.client.screencaster.FrameBuffer.FrameData;



/**
 * Producer: erstellt Screenshots und versieht sie mit einer fortlaufenden
 * Nummer. Feedback-Loop: passt sleepTime an basierend auf Capture-Time und
 * beobachtetem Consumer-Lag (via lastTakenProductionTimestamp).
 */
class CreatePicture extends Thread {

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

  private static final Robot robot;
  /**
   * Shared feedback: zuletzt vom Consumer "getroffene" Produktions-Timestamp
   * (nanoTime). Producer liest das, um die Pipeline-Latenz abzuschätzen.
   */

  static {
    try {
      robot = new Robot();
    }
    catch (AWTException e) {
      throw new RuntimeException("Robot konnte nicht erzeugt werden", e);
    }
  }
  private final FrameBuffer buffer;
  private final AtomicLong lastTakenProductionTimestamp;
  private final Rectangle screenSize;
//  private BufferedImage duke = null;

  private long counter = 0;
  // Feedback-Loop Parameter
  // Du kannst hier den Startwert anpassen — das ist bewusst sichtbar gehalten.
  private long sleepTime = 100; // Startwert: 100ms (10 FPS)
  private final long minSleep = 20; // max. 50 FPS

  private final long maxSleep = 500; // min. 2 FPS
  // Sensitivitäten für Anpassung
  private final long captureAdjustStep = 10; // ms
  private final long consumerLagThresholdNanos = 200_000_000L; // 200 ms als Beispiel-Schwelle

  private final long consumerLagAdjustStep = 20; // ms step, falls Consumer stark hinten liegt

  CreatePicture(FrameBuffer buffer, AtomicLong timestamp) {
    this.buffer = buffer;
    lastTakenProductionTimestamp = timestamp;
    screenSize = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
  }



  private BufferedImage captureWithMouse() throws InvocationTargetException, InterruptedException {
    final BufferedImage[] result = new BufferedImage[1];
    EventQueue.invokeAndWait(() -> result[0] = robot.createScreenCapture(screenSize));
    Graphics2D g2 = result[0].createGraphics();
    g2.dispose();
    return result[0];
  }



  /**
   * Die Bildauflösung beim Capturen.
   * 
   * @return diese Auflösung
   */
  public Rectangle getScreenSize() {
    return screenSize;
  }



  @Override
  public void run() {

    try {

      while (!Thread.currentThread().isInterrupted()) {
        long frameStart = System.nanoTime();

        // Capture + Encode
        BufferedImage image = captureWithMouse();
        buffer.setFrame(new FrameData(toPng(image), ++counter, frameStart));
//        log.debug("Producer: Screenshot {} erzeugt", counter);

        // Kein Sleep -> maximale Geschwindigkeit
      }
    }
    catch (InterruptedException e) {
      log.info("Producer sauber beendet");
      Thread.currentThread().interrupt();
    }
    catch (IOException | InvocationTargetException e) {
      log.error(e.getMessage(), e);
    }
  }



  private byte[] toPng(BufferedImage image) throws IOException {
    try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
//      long t0 = System.nanoTime();
      ImageIO.write(image, "png", outputStream);
//      long t1 = System.nanoTime();
//      double encodeMillis = (t1 - t0) / 1_000_000.0;
//      log.info("Producer: PNG-Encoding dauerte {} ms", String.format("%.2f", encodeMillis));
      return outputStream.toByteArray();
    }
  }
}