import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.videoio.VideoCapture;
import org.opencv.imgproc.Imgproc;
import org.opencv.highgui.HighGui;
import org.opencv.videoio.Videoio;

import java.util.LinkedList;

public class CamPuffer implements Runnable {

    static {
        try {
            System.load("C:\\Users\\david\\Documents\\Uni\\GTCar\\opencv\\build\\java\\x64\\opencv_java4100.dll");
            System.out.println("OpenCV manuell geladen!");
        } catch (UnsatisfiedLinkError e) {
            System.err.println("Fehler beim Laden der OpenCV-Bibliothek: " + e.getMessage());
        }
    }

    private final int CAM; // Kamera-ID (0 für Webcam, 1 für andere Cams)
    private final int EXP; // Belichtungszeit
    private final int FPS;
    private final int THV;

    private volatile boolean running = true;
    private final Object lock = new Object(); // Lock für Thread-Sicherheit
    private Mat currentMat = new Mat();

    private final Rect roi = new Rect(200, 150, 240, 180); // Region of Interest

    private boolean hasStartedCapturing = false;

    private final LinkedList<Mat> frameBuffer = new LinkedList<>();
    private final int MAX_BUFFER_SIZE = 10;  // Maximale Anzahl an Bildern im Puffer

    public CamPuffer(int CAM, int EXP, int FPS, int THV) {
        this.CAM = CAM;
        this.EXP = EXP;
        this.FPS = FPS;
        this.THV = THV;
    }

    @Override
    public void run() {
        VideoCapture capture = setUp();

        if (!capture.isOpened()) {
            System.out.println("Fehler: Die Webcam konnte nicht geöffnet werden.");
            return;
        }

        Mat frame = new Mat();
        capture.read(frame);

        hasStartedCapturing = true;

        long lastGcTime = System.currentTimeMillis();

        try {
            while (running) {
                //long startTime = System.currentTimeMillis();
                capture.read(frame); // Bild einlesen
                currentMat = frame;

                if (frame.empty()) {
                    System.out.println("Fehler: Kein Bild von der Kamera erhalten.");
                    break;
                }

                updateCurrentMat(frame);

                // Speicherbereinigung alle 1000 ms (1 Sekunde)
                if (System.currentTimeMillis() - lastGcTime > 1000) {
                    System.gc();  // Triggern des Garbage Collectors
                    lastGcTime = System.currentTimeMillis();
                    System.out.println("Garbage Collection durchgeführt.");
                }
            }
        } finally {
            // Matrizen explizit freigeben
            frame.release();
            capture.release();
        }
    }

    public void updateCurrentMat(Mat img) {
        Mat frameCopy = img.clone();
        synchronized (frameBuffer) {
            if (frameBuffer.size() >= MAX_BUFFER_SIZE) {
                frameBuffer.poll().release(); // Ältesten Frame entfernen
            }
            frameBuffer.add(frameCopy); // Neuen Frame hinzufügen
            currentMat = frameCopy;
        }
    }

    public Mat getCurrentMat() {
        synchronized (frameBuffer) {
            if (!frameBuffer.isEmpty()) {
                Mat resizedFrame = new Mat();
                Imgproc.resize(frameBuffer.pollLast().clone(), resizedFrame, new Size(1792, 1024));
                return resizedFrame; // Rückgabe des neuesten Frames
            }
        }
        return new Mat(); // Rückgabe eines leeren Frames, falls der Puffer leer ist
    }


    public void stop() {
        running = false;
    }

    private VideoCapture setUp() {
        System.out.println("Laden von OpenCV");
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        VideoCapture capture = new VideoCapture(CAM);

        if (!capture.isOpened()) {
            System.out.println("Fehler: Die Webcam konnte nicht geöffnet werden.");
        }

        capture.set(Videoio.CAP_PROP_BUFFERSIZE, 3);
        capture.set(Videoio.CAP_PROP_EXPOSURE, EXP);
        capture.set(Videoio.CAP_PROP_FPS, FPS);
        System.out.println("Kamera geöffnet und eingerichtet");
        return capture;
    }
}