package code;

import com.google.gson.Gson;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import org.jetbrains.annotations.NotNull;
import org.pmw.tinylog.Logger;
import peers.sip.syntaxencoding.SipHeaderFieldName;
import peers.sip.syntaxencoding.SipHeaderFieldValue;
import peers.sip.syntaxencoding.SipHeaders;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;

public class common {
    public static String currentDir = System.getProperty("user.dir");
    public static Gson gson = new Gson();
    public static final Path gpioPath = Paths.get("/sys/class/gpio") ;
    public static final Path gpioExportPath = Paths.get("/sys/class/gpio/export");
    public static final Path gpioUnexportPath = Paths.get("/sys/class/gpio/unexport");
    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 GetProperties_StringValue(Properties prop, String key, String defaultavalue){
        if (prop!=null){
            if (ValidString(key)){
                return prop.getProperty(key,defaultavalue);
            }
        }
        return defaultavalue;
    }

    public static int GetProperties_IntValue(Properties prop, String key, int defaultavalue){
        if (prop!=null){
            if (ValidString(key)){
                return ParseInt(prop.getProperty(key),defaultavalue);
            }
        }
        return defaultavalue;
    }



    /**
     * Parse integer from string
     * @param value string to parse
     * @param defaultvalue default value if parsing failed
     * @return parsed integer
     */
    public static int ParseInt(String value, int defaultvalue){
        try {
            return Integer.parseInt(value);
        } catch (Exception e) {
            return defaultvalue;
        }
    }

    /**
     * Extract properties file from resource path
     * @param directory destination directory
     * @param filename destination filename
     * @param overwrite overwrite if file already exists
     */
    public static void ExtractProperties(String directory, String filename, boolean overwrite){
        try{
            File dest = new File(directory,filename);
            if (dest.isFile() && !overwrite){
                Logger.info("Properties file already exists: {}",filename);
                return;
            }
            File source = Native.extractFromResourcePath(filename);
            if (source==null){
                Logger.error("Failed to extract properties file: {}",filename);
                return;
            }
            if (source.renameTo(dest))
                Logger.info("Extracted properties file: {}",filename);
            else Logger.error("Failed to rename properties file: {}",filename);
        } catch (Exception e) {
            Logger.error("Failed to extract properties file: {}",filename);
        }
    }

    /**
     * Load properties file from directory
     * @param directory destination directory
     * @param filename destination filename
     * @return properties object
     */
    @SuppressWarnings("IOStreamConstructor")
    public static Properties LoadProperties(String directory, String filename){
        Properties prop = new Properties();
        try {
            File file = new File(directory,filename);
            if (file.isFile()){
                InputStream is = new FileInputStream(file);
                prop.load(is);
                is.close();
                Logger.info("Loaded properties file: {}",filename);
            } else Logger.info("Properties file not found: {}",filename);

        } catch (Exception e) {
            Logger.error("Failed to load properties file: {}",filename);
        }
        return prop;
    }

    public static boolean SaveProperties(String directory, String filename, Properties prop){
        try {
            File file = new File(directory,filename);
            file.createNewFile();
            prop.store(Files.newOutputStream(file.toPath()),"");
            Logger.info("Saved properties file: {}",filename);
            return true;
        } catch (Exception e) {
            Logger.error("Failed to save properties file: {}",filename);
        }
        return false;
    }

    /**
     * Check if String is not null and have value
     * @param value string to check
     * @return true if string is not null and have value
     */
    public static boolean ValidString(String value){
        if (value!=null){
            return !value.isEmpty();
        }
        return false;
    }

    /**
     * Check if port number is valid
     * @param value port number to check
     * @return true if port number is valid
     */
    public static boolean ValidPortNumber(int value){
        return value>0 && value<65536;
    }

    /**
     * Get SIP header value from SIP headers
     * @param head SIP headers
     * @param headername header name
     * @param defaultvalue default value if header not found
     * @return header value
     */
    public static String GetSIPHeaderValue(SipHeaders head, String headername, String defaultvalue) {
        if (head!=null) {
            SipHeaderFieldName _fn = new SipHeaderFieldName(headername);
            if (head.contains(_fn)) {
                SipHeaderFieldValue _fv = head.get(_fn);
                if (_fv!=null) {
                    return _fv.getValue();
                }
            }
        }
        return defaultvalue;
    }

    /**
     * Check if GPIO is available
     * @return true if GPIO is available
     */
    public static boolean HaveGPIO(){
        if (Platform.isLinux()){
            if (Files.exists(gpioPath)){
                if (Files.exists(gpioExportPath)) {
                    return Files.exists(gpioUnexportPath);
                }
            }
        }
        return false;
    }

    /**
     * Write value to file
     * @param path file path
     * @param value value to write
     * @return true if write success
     */
    public static boolean WriteFile(Path path, String value){
        try {
            Files.write(path, value.getBytes());
            return true;
        } catch (Exception e) {
            Logger.error("Failed to write file: {}",path);
            return false;
        }
    }

    /**
     * Write integer value to file
     * @param path file path
     * @param value integer value to write
     * @return true if write success
     */
    public static boolean WriteFile(Path path, int value){
        return WriteFile(path, String.valueOf(value));
    }

    /**
     * Read file content
     * @param path file path
     * @return file content, or empty string if failed to read
     */
    public static @NotNull String ReadFile(Path path){
        try {
            byte[] data = Files.readAllBytes(path);
            return new String(data);
        } catch (Exception e) {
            Logger.error("Failed to read file: {}",path);
        }
        return "";
    }
}
