package Other;

import com.google.gson.Gson;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.opencv.opencv_core.UMat;
import org.bytedeco.opencv.opencv_java;
import org.jetbrains.annotations.NotNull;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.tinylog.Logger;

import java.io.*;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Properties;

@SuppressWarnings("unused")
public class SomeCodes {
    static{
        Loader.load(opencv_java.class);
    }

    public final static String currentDirectory = System.getProperty("user.dir");
    public final static Path audioPath = Path.of(currentDirectory, "audiofiles");
    private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final OpenCVFrameConverter.ToMat matConverter = new OpenCVFrameConverter.ToMat();
    public static final OpenCVFrameConverter.ToOrgOpenCvCoreMat CoreMatConverter = new OpenCVFrameConverter.ToOrgOpenCvCoreMat();

    public static final Java2DFrameConverter frameConverter = new Java2DFrameConverter();
    public static final Path logsPath = Path.of(currentDirectory, "logs");
    public static final boolean haveOpenCL = opencv_core.haveOpenCL();
    public static boolean useOpenCL;
    private static final Base64.Encoder base64encoder = java.util.Base64.getEncoder();
    public static final Gson gson = new Gson();
    public static final double KB_threshold = 1024.0;
    public static final double MB_threshold = 1024.0 * 1024.0;
    public static final double GB_threshold = 1024.0 * 1024.0 * 1024.0;

    public static String[] GetAudioFiles(){
        try{
            return Files.list(audioPath).map(f -> f.getFileName().toString()).toArray(String[]::new);
        } catch (Exception e){
            Logger.error("Error getting audio files: "+e.getMessage());
        }

        return new String[0];
    }

    public static String LocalDateTimeToString(LocalDateTime x){
        return x.format(dtf);
    }

    public static String ExtractResource(String filename, String targetdirectory){
        try {
            File destination = new File(targetdirectory, filename);
            if (destination.exists()) {
                Logger.info("Resource File already exists: " + filename);
                return destination.getAbsolutePath();
            }

            InputStream is = SomeCodes.class.getResourceAsStream(filename);
            if (is!=null){
                Files.copy(is, destination.toPath());
                Logger.info("Resource File extracted: "+filename);
                return destination.getAbsolutePath();
            } else {
                Logger.error("Resource File not found: "+filename);
            }

        } catch (Exception e){
            Logger.error("Error extracting resource: "+filename+", Message : "+e.getMessage());
        }
        return  null;

    }




    public static boolean ValidDirectory(String path){
        if (ValidString(path)){
            File ff = new File(path);
            return ff.isDirectory();
        }
        return false;
    }

    public static boolean ValidFile(String filename){
        if (ValidString(filename)){
            File ff = new File(filename);
            return ff.isFile();
        }
        return false;
    }

    public static boolean ValidString(String x){
        if (x!=null){
            return !x.isEmpty();
        }
        return false;
    }

    public static byte GetPanTiltSpeed(String data, byte defaultspeed){
        if (ValidInteger(data)){
            int speed = Integer.parseInt(data);
            if (speed<0) speed = 0;
            if (speed>0x3F) speed = 0x3F;
            return (byte)speed;
        }
        return defaultspeed;
    }

    public static boolean ValidInteger(String x){
        try{
            Integer.parseInt(x);
            return true;
        } catch (Exception e){
            return false;
        }
    }

    public static boolean ValidPortNumber(int port){
        return port>0 && port<65536;
    }

    public static boolean ValidPortNumber(String port){
        try{
            int portx = Integer.parseInt(port);
            return ValidPortNumber(portx);
        } catch (Exception e){
            return false;
        }
    }

    public static boolean ValidIPV4(String ipaddress){
        if (ValidString(ipaddress)){
            try{
                InetAddress inet = InetAddress.getByName(ipaddress);
                if (inet instanceof Inet4Address){
                    if (inet.getHostAddress().equals(ipaddress)){
                        return true;
                    }
                }
            } catch (Exception ignored) {
            }
        }
        return false;
    }

    public static boolean ValidIPV6(String ipaddress){
        if (ValidString(ipaddress)){
            try{
                InetAddress inet = InetAddress.getByName(ipaddress);
                if (inet instanceof Inet6Address){
                    if (inet.getHostAddress().equals(ipaddress)){
                        return true;
                    }
                }
            } catch (Exception ignored) {
            }
        }
        return false;
    }

    public static String GetFileName(String filepath){
        if (ValidString(filepath)){
            File ff = new File(filepath);
            if (ff.isFile()){
                return ff.getName();
            }
        }
        return "";
    }


    // Function ini pakai opencv, bukan javacv, jadi perlu Loader.load(opencv_java.class) di awal
    // lebih optimal untuk konversi frame ke base64
    public static String FrameToBase64(Frame frame){
        if (frame!=null){
            org.opencv.core.Mat converted = CoreMatConverter.convert(frame);
            if (converted!=null){
                if (!converted.empty()){
                    MatOfByte mob = new MatOfByte();
                    Imgcodecs.imencode(".jpg", converted, mob);
                    byte[] jpgdata = mob.toArray();
                    mob.release();
                    converted.release();
                    return base64encoder.encodeToString(jpgdata);
                }
            }
        }
        return "";
    }


    /**
     * Load properties file
     * @param filename properties file name
     * @return Properties object loaded, or empty new Properties object if error
     */
    public static @NotNull Properties LoadProperties(String filename){
        try{
            File ff = new File(currentDirectory, filename);
            InputStream is = new FileInputStream(ff);
            Properties prop = new Properties();
            prop.load(is);
            return prop;
        } catch (Exception e){
            Logger.error("Error loading properties file: "+e.getMessage());
        }
        return new Properties();
    }

    /**
     * Save properties file
     * @param prop Properties object to save
     * @param filename properties file name
     * @return true if success, false otherwise
     */
    public static boolean SaveProperties(Properties prop, String filename){
        try{
            File ff = new File(currentDirectory, filename);
            OutputStream os = new FileOutputStream(ff);
            prop.store(os, null);
            return true;
        } catch (Exception e){
            Logger.error("Error saving properties file: "+e.getMessage());
        }
        return false;
    }

    public static String GetConfigAudioFile(int index){
        Properties config = LoadProperties("config.properties");
        String key = String.format("AudioFile%02d", index);
        return config.getProperty(key, null);
    }

    public static Mat ResizeMat(Mat source, int width, int height){
        Size sz = new Size(width, height);
        Mat dest = new Mat();
        if (useOpenCL){
            UMat src = new UMat();
            source.copyTo(src);
            UMat dst = new UMat();

            opencv_imgproc.resize(src, dst, sz);
            dst.copyTo(dest);
        } else {
            opencv_imgproc.resize(source, dest, sz);
        }
        return dest;
    }

    public static Frame ResizeFrame(Frame source, int width, int height){
        Mat mat = matConverter.convertToMat(source);
        Mat resized = ResizeMat(mat, width, height);
        return matConverter.convert(resized);
    }

    /**
     * check if an ip address is reachable
     * @param ipaddress ip address to check
     * @return true if valid and reachable, false otherwise
     */
    public static boolean IpIsReachable(String ipaddress){
        try{
            InetAddress inet = InetAddress.getByName(ipaddress);
            return inet.isReachable(1000);
        } catch (Exception e){
            Logger.error("Error checking ip address: "+e.getMessage());
        }
        return false;
    }
}
