package Camera;
import lombok.Getter;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.jetbrains.annotations.NotNull;
import org.tinylog.Logger;

import java.util.concurrent.atomic.AtomicBoolean;

import static Other.SomeCodes.gson;

@SuppressWarnings("unused")
public class RtspGrabber {
    private final String rtspUrl;
    private FFmpegFrameGrabber grabber;
    private final AtomicBoolean isGrabbing = new AtomicBoolean(false);
    private Frame lastHQFrame = null;
    private Frame lastLQFrame = null;
    private String lastHQBase64 = null;
    private String lastLQBase64 = null;
    private @Getter int HQWidth = 0;
    private @Getter int HQHeight = 0;
    private @Getter int LQWidth = 0;
    private @Getter int LQHeight = 0;
    private GrabbingTask grabbingTask;

    public RtspGrabber(String ip, String path) {
        this.rtspUrl = "rtsp://" + ip + path;
        Logger.info("RtspGrabber created with url: " + rtspUrl);
    }

    private synchronized void setLastHQFrame(Frame frame){
        lastHQFrame = frame;
    }

    public synchronized Frame getLastHQFrame(){
        return lastHQFrame;
    }

    private synchronized void setLastLQFrame(Frame frame){
        lastLQFrame = frame;
    }

    public synchronized Frame getLastLQFrame(){
        return lastLQFrame;
    }

    private synchronized void setLastHQBase64(String base64){
        lastHQBase64 = base64;
    }

    public synchronized String getLastHQBase64(){
        return lastHQBase64;
    }

    private synchronized void setLastLQBase64(String base64){
        lastLQBase64 = base64;
    }

    public synchronized String getLastLQBase64(){
        return lastLQBase64;
    }

    /**
     * Start grabbing frames from rtsp
     * @param useTcp Use tcp instead of udp
     */
    public void Start(boolean useTcp, final int width, final int height){

        try{
            grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
            if (useTcp) grabber.setOption("rtsp_transport", "tcp");

            grabber.setPixelFormat(avutil.AV_PIX_FMT_BGR24);
            grabber.start();

            avutil.av_log_set_level(avutil.AV_LOG_ERROR);

            Logger.info("Grabber started");
            GrabbingTask tt = getGrabbingTask();
            grabbingTask = tt;
            new Thread(tt).start();

        } catch (Exception e){
            Logger.error("Error starting grabber: " + e.getMessage());
        }
    }



    /**
     * Stop grabbing frames
     */
    public void Stop(){
        isGrabbing.set(false);
        if (grabbingTask!=null){
            grabbingTask.Stop();
        }
        grabbingTask = null;
        if (grabber!=null) {
            try{
                grabber.stop();
                grabber.releaseUnsafe();
                Logger.info("Grabber stopped");
            } catch (Exception e){
                Logger.error("Error stopping grabber: " + e.getMessage());
            }
        }
        grabber = null;
    }

    public String LQStreamingStatus(){
        return gson.toJson(new String[]{String.valueOf(LQWidth), String.valueOf(LQHeight) , String.valueOf(grabbingTask!=null ? grabbingTask.getCaptureFPS():"0")});
    }

    public String HQStreamingStatus(){
        return gson.toJson(new String[]{String.valueOf(HQWidth), String.valueOf(HQHeight) , String.valueOf(grabbingTask!=null ? grabbingTask.getCaptureFPS(): "0")});
    }

    @NotNull
    private GrabbingTask getGrabbingTask() {
        GrabbingTask tt = new GrabbingTask(isGrabbing, grabber,10);
        tt.setOnMessageUpdate(Logger::info);
        tt.setOnHQFrameUpdate(value -> {
            if (value!=null){
                if (value.imageWidth>0 && value.imageHeight>0){
                    setLastHQFrame(value);
                    HQWidth = value.imageWidth;
                    HQHeight = value.imageHeight;
                }

            }
        });
        tt.setOnLQFrameUpdate(value -> {
            if (value!=null){
                if (value.imageWidth>0 && value.imageHeight>0){
                    setLastLQFrame(value);
                    LQWidth = value.imageWidth;
                    LQHeight = value.imageHeight;
                }
            }
        });
        tt.setOnHQBase64Update(this::setLastHQBase64);
        tt.setOnLQBase64Update(this::setLastLQBase64);
        return tt;
    }
}
