package Camera;

import lombok.Getter;
import org.tinylog.Logger;

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static Other.SomeCodes.ValidInteger;
import static Other.SomeCodes.ValidString;

@SuppressWarnings("unused")
public class VapixProtocol {

    private final String url;
    private final HttpClient client;
    private String parameters;

    private String ProductNumberValue = "";
    private String SerialNumberValue = "";
    private int CameraIDValue = 0;
    private int MaxZoomValue = 0;
    private int MinZoomValue = 0;
    private boolean PTZEnabledValue = false;
    private String[] ImageResolutions = new String[0];
    private String[] ImageFormats = new String[0];
    private int CurrentZoomValue = 0;
    @Getter private boolean successQuery = false;
    /**
     * Create a new VapixProtocol object
     * @param ip The IP address of the camera
     * @param port The HTTP port of the camera
     * @param username The username to access the camera (optional)
     * @param password The password to access the camera (optional)
     */
    public VapixProtocol(String ip, int port, String username, String password) {
        url = "http://" + ip + ":" + port+"/axis-cgi/";
        String auth = username + ":" + password;
        //encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
        client = HttpClient.newHttpClient();
        GetParameters(null);
        if (ValidString(parameters)){
            Pattern ProductNumber = Pattern.compile(".*ProdNbr=(.*)");
            Matcher m1 = ProductNumber.matcher(parameters);
            if (m1.find()) ProductNumberValue = m1.group(1);
            Pattern SerialNumber = Pattern.compile(".*SerialNumber=(.*)");
            Matcher m2 = SerialNumber.matcher(parameters);
            if (m2.find()) SerialNumberValue = m2.group(1);
            Pattern PTZCamId = Pattern.compile(".*CamId=(.*)");
            Matcher m3 = PTZCamId.matcher(parameters);
            if (m3.find()) {
                String value = m3.group(1);
                if (ValidInteger(value)) CameraIDValue = Integer.parseInt(value);
            }
            Pattern PTZMaxZoom = Pattern.compile(".*MaxZoom=(.*)");
            Matcher m4 = PTZMaxZoom.matcher(parameters);
            if (m4.find()) {
                String value = m4.group(1);
                if (ValidInteger(value)) MaxZoomValue = Integer.parseInt(value);
            }
            Pattern PTZMinZoom = Pattern.compile(".*MinZoom=(.*)");
            Matcher m5 = PTZMinZoom.matcher(parameters);
            if (m5.find()) {
                String value = m5.group(1);
                if (ValidInteger(value)) MinZoomValue = Integer.parseInt(value);
            }
            Pattern PTZEnabled = Pattern.compile(".*PTZ.PTZ=(.*)");
            Matcher m6 = PTZEnabled.matcher(parameters);
            if (m6.find()) PTZEnabledValue = m6.group(1).contains("yes");
            Pattern Resolution = Pattern.compile(".*Image.Resolution=(.*)");
            Matcher m7 = Resolution.matcher(parameters);
            if (m7.find()) {
                String value = m7.group(1);
                if (ValidString(value)) ImageResolutions = value.split(",");
                //Logger.info("Resolution: "+value);
            }
            Pattern Format = Pattern.compile(".*Image.Format=(.*)");
            Matcher m8 = Format.matcher(parameters);
            if (m8.find()) {
                String value = m8.group(1);
                if (ValidString(value)) ImageFormats = value.split(",");
                //Logger.info("Format: "+value);
            }
            successQuery = true;
        }
    }

    @SuppressWarnings("SameParameterValue")
    private void GetParameters(String group){
        String command = url+"param.cgi?action=list";
        if (ValidString(group)) command+="&group="+group;
        HttpRequest request = HttpRequest.newBuilder()
                .uri(java.net.URI.create(command))
                .build();
        parameters = "";
        try{
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode()==200){
                parameters = response.body();
            } else Logger.info("Error getting parameters: "+response.statusCode());
        } catch (Exception e){
            Logger.error("Error getting parameters: "+e.getMessage());
        }
    }

    /**
     * Get Product Number of the camera
     * @return Product Number, or empty string if not available
     */
    public String GetProductNumber(){
        return ProductNumberValue;
    }

    /**
     * Get Serial Number of the camera
     * @return Serial Number, or empty string if not available
     */
    public String GetSerialNumber(){
        return SerialNumberValue;
    }


    /**
     * Get PTZ Camera ID
     * @return Camera ID
     */
    public int GetPTZCameraID(){
        return CameraIDValue;
    }

    /**
     * Get Maximum Zoom Value
     * @return Maximum Zoom Value
     */
    public int GetPTZMaxZoom(){
        return MaxZoomValue;
    }

    /**
     * Get Minimum Zoom Value
     * @return Minimum Zoom Value
     */
    public int GetPTZMinZoom(){
        return MinZoomValue;
    }

    /**
     * Check if PTZ Control enabled for this camera
     * @return true if PTZ Control enabled, false otherwise
     */
    public boolean PTZEnabled() {
        return PTZEnabledValue;
    }

    /**
     * Get Available Image Resolutions
     * @return array of String, in format "widthxheight"
     */
    public String[] GetImageResolutions(){
        return ImageResolutions;
    }

    /**
     * Get Available Image Formats
     * @return array of String, example "jpeg","mjpeg","h264","bitmap"
     */
    public String[] GetImageFormats(){
        return ImageFormats;
    }

    /**
     * Close VapiX Protocol
     */
    public void Close(){
        if (client!=null) client.close();
    }

    /**
     * Zoom In / Out
     * @param cameraid Camera ID, get it from GetPTZCameraID()
     * @param zoom Zoom value, value between GetPTZMinZoom and GetPTZMaxZoom()
     * @return true if successful, false otherwise
     */
    public boolean Zoom(int cameraid, int zoom){
        if (zoom < MinZoomValue) zoom = MinZoomValue;
        if (zoom > MaxZoomValue) zoom = MaxZoomValue;
        String command = url+"com/ptz.cgi?zoom="+zoom+"&camera="+cameraid;
        HttpRequest request = HttpRequest.newBuilder()
                .uri(java.net.URI.create(command))
                .build();
        try{
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode()==200){
                Logger.info("Zoom: "+response.body());
                return true;
            }
        } catch (Exception e){
            Logger.error("Error zooming: "+e.getMessage());
        }
        return false;
    }

    /**
     * Get Current Zoom Value
     * @return Current Zoom Value
     */
    public int GetCurrentZoomValue(){
        update_ptz_query_position();
        return CurrentZoomValue;
    }

    /**
     * Get Current Image Resolution
     * @param cameraid Camera ID, get it from GetPTZCameraID()
     * @return array of int, [0] = width, [1] = height
     */
    public int[] GetCurrentResolution(int cameraid){
        int[] result = new int[]{0,0};
        String command = url+"imagesize.cgi?camera="+cameraid;
        HttpRequest request = HttpRequest.newBuilder()
                .uri(java.net.URI.create(command))
                .build();
        try{
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode()==200){
                String values = response.body();
                //Something like this:
                //image width = 1920
                //image height = 1080
                for (String s : values.split("\n")) {
                    String[] parts = s.split("=");
                    if (parts.length==2){
                        String part0 = parts[0].trim();
                        String part1 = parts[1].trim();
                        if (part0.contains("width")){
                            if (ValidInteger(part1)) result[0] = Integer.parseInt(part1);
                        }
                        if (part0.contains("height")) {
                            if (ValidInteger(part1)) result[1] = Integer.parseInt(part1);
                        }
                    }
                }
            }
        } catch (Exception e){
            Logger.error("Error getting resolution: "+e.getMessage());
        }
        return result;
    }

    /**
     * Set Image Resolution
     * @param cameraid Camera ID, get it from GetPTZCameraID()
     * @param width Width of the image
     * @param height Height of the image
     * @return true if successful, false otherwise
     */
    public boolean SetResolution(int cameraid, int width, int height){
        String resolution = width+"x"+height;
        boolean supported = false;
        for(String r: ImageResolutions){
            if (r.equals(resolution)){
                supported = true;
                break;
            }
        }

        if (supported){
            String command = url+"imagesize.cgi?camera="+cameraid+"&resolution="+resolution;
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(java.net.URI.create(command))
                    .build();
            try{
                var response = client.send(request, HttpResponse.BodyHandlers.ofString());
                if (response.statusCode()==200){

                    Logger.info("Resolution set: "+response.body());
                    String values = response.body();
                    //Something like this:
                    //image width = 1920
                    //image height = 1080
                    boolean width_ok = false;
                    boolean height_ok = false;
                    for (String s : values.split("\n")) {
                        String[] parts = s.split("=");
                        if (parts.length==2){
                            String part0 = parts[0].trim();
                            String part1 = parts[1].trim();
                            if (part0.contains("width")){
                                if (ValidInteger(part1)) {
                                    if (Integer.parseInt(part1)==width)
                                        width_ok = true;
                                    else Logger.error("Width failed, target: "+width+" actual: "+part1);
                                }
                            }
                            if (part0.contains("height")) {
                                if (ValidInteger(part1)) {
                                    if (Integer.parseInt(part1)==height)
                                        height_ok = true;
                                    else Logger.error("Height failed, target: "+height+" actual: "+part1);
                                }
                            }
                        }
                    }

                    return width_ok && height_ok;
                }
            } catch (Exception e){
                Logger.error("Error setting resolution: "+e.getMessage());
            }
        } else Logger.info("Resolution not supported: "+resolution);


        return false;
    }

    private void update_ptz_query_position(){
        String command = url+"com/ptz.cgi?query=position";
        HttpRequest request = HttpRequest.newBuilder()
                .uri(java.net.URI.create(command))
                .build();
        try{
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode()==200){
                String values = response.body();

                //Something like this:
                //pan=0.0000
                //tilt=0.0000
                //zoom=1
                //iris=5000
                //focus=9574
                //brightness=5000
                //autofocus=on
                //autoiris=off
                for (String s : values.split("\n")) {
                    String[] parts = s.split("=");
                    if (parts.length==2){
                        if (parts[0].equals("zoom")) CurrentZoomValue = Integer.parseInt(parts[1].trim());
                    }
                }
            }
        } catch (Exception e){
            Logger.error("Error getting PTZ position: "+e.getMessage());
        }
    }
}
