/**
 *  Copyright © 2025, Luis Andrés Lange <https://javacomm.net>
 *
 *  Previously released under Apache License 2.0; now licensed under MPL 2.0.
 *
 *  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.restserver;

import static net.javacomm.protocol.HEADER.REQUEST;
import com.google.gson.Gson;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.media.multipart.FormDataParam;
import net.javacomm.database.DatabaseException;
import net.javacomm.database.WebdatabaseImpl;
import net.javacomm.protocol.Command;
import net.javacomm.protocol.NEWIMAGE;
import net.javacomm.protocol.NEWMOUSEPOINTER;
import net.javacomm.protocol.Protocol;
import net.javacomm.server.Webserver;



@Path("/screenshot")
public class Screenshot {

  // Cache: userid → letzte Bilddaten als AtomicReference
  private static final Map<String, AtomicReference<byte[]>> imageCache = new ConcurrentHashMap<>();

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

  private final WebdatabaseImpl database;

  public Screenshot() {
    database = (WebdatabaseImpl) WebdatabaseImpl.getInstance();
  }



  public static void removeScreenshot(String userid) {
    imageCache.remove(userid);
    log.info("Screenshot von {} gelöscht", userid);
  }



  @GET
  @Path("/running/{receiver}")
  @Produces(MediaType.TEXT_PLAIN)
  public int isTransmissionRunning(@PathParam("receiver") String receiverNickname) {
    try {
      String receiverUserid = database.fetchUserByNickname(receiverNickname);
      return database.isVideoRunning(receiverUserid);
    }
    catch (DatabaseException e) {
      log.info(e.getMessage());
      throw new WebApplicationException();
    }
  }



  /**
   * Lädt den aktuellsten Screenshot. Der Leseprozess arbeitet auf einer Kopie,
   * neue Bilder überschreiben nur die Referenz.
   */
  @GET
  @Path("/load/{userid}")
  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  @Deprecated
  public Response loadScreenshot(@PathParam("userid") String userid) {
    AtomicReference<byte[]> ref = imageCache.get(userid);
    if (ref == null || ref.get() == null) {
      log.warn("Kein Screenshot für {} gefunden", userid);
      return Response.status(Response.Status.NOT_FOUND).build();
    }

    // Kopie erstellen, damit spätere Updates den laufenden Leseprozess nicht stören
    byte[] imageCopy = ref.get();
    return Response.ok(imageCopy).build();
  }



  /**
   * Lädt den aktuellsten Screenshot. Der Leseprozess arbeitet auf einer Kopie,
   * neue Bilder überschreiben nur die Referenz.
   */
  @GET
  @Path("/load")
  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  public Response loadScreenshotHeader(@HeaderParam("userid") String userid) {
    AtomicReference<byte[]> ref = imageCache.get(userid);
    if (ref == null || ref.get() == null) {
      log.warn("Kein Screenshot für {} gefunden", userid);
      return Response.status(Response.Status.NOT_FOUND).build();
    }

    // Kopie erstellen, damit spätere Updates den laufenden Leseprozess nicht stören
    byte[] imageCopy = ref.get();
    return Response.ok(imageCopy).build();
  }



  /**
   * Speichert ein neues Bild. Überschreibt die alte Referenz, ohne laufende
   * Leseprozesse zu beeinflussen.
   */
  @POST
  @Path("/save")
  public Response saveScreenshot(@FormDataParam("userid") String userId,
      @FormDataParam("receivers") String receiversJson, @FormDataParam("image") InputStream imageStream) {

    if (userId == null || userId.isEmpty()) {
      return Response.status(Status.BAD_REQUEST).entity("userid fehlt").build();
    }

    try(InputStream in = imageStream) {
      // InputStream vollständig in ein byte[] laden
      byte[] pngBytes = in.readAllBytes();

      // AtomicReference erzeugen oder aktualisieren
      imageCache.computeIfAbsent(userId, k -> new AtomicReference<>()).set(pngBytes);

      // Benachrichtigung an Empfänger senden
      String[] receivers = new Gson().fromJson(receiversJson != null ? receiversJson : "[]", String[].class);
      if (receivers.length > 0) {
        NEWIMAGE newimage = new NEWIMAGE();
        newimage.setCommand(Command.NEWIMAGE);
        newimage.setHeader(REQUEST);
        newimage.setDataset(Protocol.DATASET);
        newimage.setUserid(userId);
        Webserver.sendMessage(newimage, java.util.List.of(receivers));
      }

      return Response.ok().build();
    }
    catch (IOException e) {
      log.error("Fehler beim Verarbeiten des PNG von {}", userId, e);
      throw new WebApplicationException();
    }
  }



  /**
   * Die Mausposition wurde an den Server übertragen. Diese Position wird den
   * anderen Konferenzteilnehmern mitgeteilt.
   * 
   * @param x
   *                      Koordinate
   * @param y
   *                      Koordinate
   * @param receiversJson
   *                      alle Empfänger
   */
  @POST
  @Path("/mousepointer")
  public Response mousepointer(@FormDataParam("x") int x, @FormDataParam("y") int y,
      @FormDataParam("receivers") String receiversJson) {

//    log.info("({}/{})", x, y);
    // Benachrichtigung an Empfänger senden
    String[] receivers = new Gson().fromJson(receiversJson != null ? receiversJson : "[]", String[].class);
    if (receivers.length > 0) {
      NEWMOUSEPOINTER mousepointer = new NEWMOUSEPOINTER();
      mousepointer.setCommand(Command.NEWMOUSEPOINTER);
      mousepointer.setHeader(REQUEST);
      mousepointer.setDataset(Protocol.DATASET);
      mousepointer.setX(x);
      mousepointer.setY(y);
      Webserver.sendMessage(mousepointer, java.util.List.of(receivers));
    }

    return Response.noContent().build();
  }
}
