/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.teltonikaServer.lib;

import java.io.File;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.GregorianCalendar;
import java.util.TimeZone;

/**
 *
 * @author Administrador
 */
public class Utils {

    public static final int DLE = 0x10;
    public static final int EOT = 0x03;

    public static byte[] bufferConcat(byte b1[], byte b2[]) {
        byte buffer[];
        if (b1 != null) {
            buffer = new byte[b1.length + b2.length];
            System.arraycopy(b1, 0, buffer, 0, b1.length);
            System.arraycopy(b2, 0, buffer, b1.length, b2.length);
        } else {
            buffer = b2;
        }
        return buffer;
    }

    public static long getTimeStamp(byte message[]) {
        int year = Utils.getUnsignedbyte(message[0]) * 256 + Utils.getUnsignedbyte(message[1]);
        int month = Utils.getUnsignedbyte(message[2]);
        int day = Utils.getUnsignedbyte(message[3]);
        int hour = Utils.getUnsignedbyte(message[4]);
        int minute = Utils.getUnsignedbyte(message[5]);
        int second = Utils.getUnsignedbyte(message[6]);

        TimeZone utc = TimeZone.getTimeZone("Zulu");

        //System.out.println(year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second);
        GregorianCalendar cal = new GregorianCalendar(year, month - 1, day, hour, minute, second);
        cal.setTimeZone(utc);
        // System.out.println();
        return cal.getTimeInMillis() / 1000;
    }

    public static double getQueclinkHourCount(String queclinkHours) {
        double hours;
        try {
            String hParts[] = queclinkHours.split(":");
            hours = Integer.valueOf(hParts[0]);
            hours += (double) (Integer.valueOf(hParts[1])) / 60;
            hours += (double) (Integer.valueOf(hParts[2])) / 3600;
        } catch (NumberFormatException e) {
            hours = -1;
        }
        return hours;
    }

    public static long getTimestamp(String aDate) {
        try {
            LocalDateTime formatted = LocalDateTime.parse(aDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            return (long) formatted.atZone(ZoneId.of("GMT")).toInstant().getEpochSecond();
        } catch (Exception e) {
            return -1;
        }
    }

    public static byte[] DLEPadding(byte message[]) {

        byte aux[] = new byte[2048];
        aux[0] = message[0];
        int j = 0;
        int i;
        for (i = 1; i < message.length - 2; i++) {
            aux[i + j] = message[i];
            if (message[i] == DLE) {
                j++;
                aux[i + j] = DLE;
            }
        }
        aux[i + j] = DLE;
        aux[i + j + 1] = EOT;
        byte finalArray[] = new byte[i + j + 3];
        finalArray = Arrays.copyOfRange(aux, 0, finalArray.length - 1);
        return finalArray;
    }

    public static String zeroPad(String anHexNumber, int len) {
        String paddedString = anHexNumber;
        while (paddedString.length() < len) {
            paddedString = "0" + paddedString;
        }
        return paddedString.toUpperCase();
    }

    /* 
    private static String zeroPad(int length, String pad, String zp) {

        while (zp.length() < length) {
            zp = "0" + zp;
        }
        return zp;
    }
    */

    public static String LongToHex(Long l, int bytes) {
        return zeroPad(Long.toHexString(l).toUpperCase(), 2 * bytes);
    }

    public static long HexToLong(String aString) {
        return Long.valueOf(endianReverse(aString), 16);
    }

    public static String endianReverse(String aString) {
        String inLittleEndian = "";
        for (int i = 0; i < aString.length() - 1; i = i + 2) {
            inLittleEndian = aString.substring(i, i + 2) + inLittleEndian;
        }
        return inLittleEndian;
    }

    public static byte[] getBytesFromLong(long aLong, int bytes) {

        byte byteArray[] = new byte[bytes];
        long resto = aLong;
        int divisor;

        for (int i = 0; i < bytes; i++) {
            divisor = (int) Math.pow(256, bytes - i - 1);
            byteArray[bytes - i - 1] = (byte) (resto / divisor);
            resto = resto % divisor;
        }

        return byteArray;
    }

    static public String getHexString(byte message[]) {

        String hex = "";

        for (int i = 0; i < message.length; i++) {
            hex += getHexByte(Utils.getUnsignedbyte(message[i]));
        }
        return hex.toUpperCase();
    }

    static public void debugByteArray(byte message[]) {

        //String hex = "";
        for (int i = 0; i < message.length; i++) {
            if (i % 18 == 0) {
                System.out.println("");
            }
            System.out.print(getHexByte(Utils.getUnsignedbyte(message[i])) + " ");
        }
    }

    static public String getString(byte message[], int offset, int length) {
        String str = "";
        for (int i = offset; i < offset + length; i++) {
            str += (char) message[i];
        }
        return str;
    }

    static public byte[] getSubArray(byte message[], int start) {
        return getSubArray(message, start, message.length - start);
    }

    static public byte[] getSubArray(byte message[], int start, int length) {

        byte subArray[] = new byte[length];
        for (int i = start; i < start + length; i++) {
            subArray[i - start] = message[i];
        }
        return subArray;
    }

    static public int getCharOffset(byte message[], char aChar, byte offsetCount) {

        byte instanceCount = 0;
        int i;
        for (i = 0; i < message.length; i++) {
            if ((char) message[i] == aChar) {
                instanceCount++;
            }
            if (instanceCount == offsetCount) {
                break;
            }
        }
        return (instanceCount == offsetCount) ? i : -1;
    }

    // Convierte un número decimal a su correspondiente hexadecimal
    static public String hex2String(char c) {
        return Integer.toHexString((int) c);
    }

    // Rellena un string con un caracter dado hasta obtener el largo deseado
    static public String leftPad(String aString, String paddingChar, int finalLen) {
        while (aString.length() < finalLen) {
            aString = paddingChar + aString;
        }
        return aString;
    }

    // Convierte un signed Byte a un int realizando el complemento a 2.
    static public int getUnsignedbyte(byte aByte) {

        int anInt = aByte;

        if (aByte < 0) {
            anInt = aByte & 0x80;
            anInt += aByte & 0x7F;
        }

        return anInt;
    }

    static public float calculateDistance(double lat1, double lng1, double lat2, double lng2) {
        int R = 6371;   // Radio de la tierra expresado en km
        double dLat = (float) Math.toRadians(lat1 - lat2);
        double dLon = (float) Math.toRadians(lng1 - lng2);
        double a = (Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(Math.toRadians(lat2)) * Math.cos(Math.toRadians(lat1)) * Math.sin(dLon / 2) * Math.sin(dLon / 2));
        double c = (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)));

        float distance = (float) Math.abs(R * c);
        return distance;
    }

    static public String getCurrentDateAndTime() {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        java.util.Date date = new java.util.Date();
        return dateFormat.format(date);
    }

    static public char[] getBytes(int anInt) {

        char crcArray[] = new char[2];

        crcArray[1] = (char) (anInt % 256);
        crcArray[0] = (char) ((anInt - crcArray[1]) / 256);

        return crcArray;
    }

    static public String getHexByte(int anInt) {
        return leftPad(Integer.toHexString(anInt), "0", 2);
    }

    public static long getLongFromMessageArray(byte message[]) {
        return getLongFromMessageArray(message, 0, message.length);
    }

    public static long getLongFromMessageArray(byte[] message, int from, int bytes) {

        byte auxArray[] = Arrays.copyOfRange(message, from, from + bytes);
        long result = 0;

        for (int i = 0; i < auxArray.length; i++) {
            result += Utils.getUnsignedbyte(auxArray[i]) * Math.pow(256, bytes - i - 1);
        }
        return result;
    }

    public static String getTextFromMessageArray(byte[] message, int from, int bytes) {
        return new String(Arrays.copyOfRange(message, from, from + bytes));
    }

    public static byte[] getByteArrayFromDefinitionString(String aDefinitionString) {

        byte byteArray[] = new byte[aDefinitionString.length() / 2];
        //System.out.println("Converting: "+aDefinitionString);
        for (int i = 0; i < aDefinitionString.length() - 1; i = i + 2) {
            String c = aDefinitionString.substring(i, i + 2);
            //System.out.println(c + ">>" +Integer.parseInt(c,16)+">>"+(byte)Integer.parseInt(c,16));
            byteArray[i / 2] = (byte) Integer.parseInt(c, 16);
        }
        return byteArray;
    }

    public static int getChecksum(byte aByteArray[]) {

        int checksum = 0;
        for (int i = 0; i < aByteArray.length; i++) {
            checksum += Utils.getUnsignedbyte(aByteArray[i]);
        }
        checksum = 0xff & checksum;
        checksum = 0xff - checksum + 1;
        return checksum;
    }

    /*
    public static byte[] getByteArrayFromDefinitionString(String aDefinitionString) {

        byte byteArray[] = new byte[aDefinitionString.length() / 2];
        //System.out.println("Converting: "+aDefinitionString);
        for (int i = 0; i < aDefinitionString.length() - 1; i = i + 2) {
            String c = aDefinitionString.substring(i, i + 2);
            //System.out.println(c + ">>" +Integer.parseInt(c,16)+">>"+(byte)Integer.parseInt(c,16));
            byteArray[i / 2] = (byte) Integer.parseInt(c, 16);
        }
        return byteArray;
    }
     * */
    //2021-04-14T14:04:32-07:00
    //2022-04-25T13:25:41.721235+02:00
    public static long getTimeStampFromISO(String aStanfordAsciiTime) {

        // anAsciiTimestamp expected format is 20150318132926
        //2022-04-25T13:25:41.721235+02:00
        TemporalAccessor ta = DateTimeFormatter.ISO_DATE_TIME.parse(aStanfordAsciiTime);
        Instant i = Instant.from(ta);
        //Date d = Date.from(i);
        return i.getEpochSecond();

    }

    public static byte[] getNullStringBytes(String text) {
        byte nullString[] = new byte[text.length() + 1];
        System.arraycopy(text.getBytes(), 0, nullString, 0, text.length());
        nullString[text.length()] = 0x00;
        return nullString;
    }

    public static String getUniqueId(byte anId[], boolean asDeviceName) {

        String uniqueId = "";
        try {
            if (asDeviceName) {
                uniqueId = new String(anId);
            } else {
                System.out.print("ID: ");
                int i = 0;
                for (byte b : anId) {
                    i++;
                    int anInt = getUnsignedbyte(b);
                    System.out.print(anInt + "-");
                    uniqueId += leftPad(String.valueOf(anInt), "0", (i == anId.length) ? 1 : 2);
                }
                //System.out.println();
            }
        } catch (Exception e) {
            uniqueId = "";
        }
        return uniqueId;
    }

    static public ArrayList<File> getFiles(final File folder) {

        ArrayList<File> list = new ArrayList<File>();
        for (final File fileEntry : folder.listFiles()) {
            //System.out.println(fileEntry.getAbsolutePath());
            if (!fileEntry.isDirectory()) {
                list.add((File) fileEntry);
            }
        }
        return list;
    }

    static public ArrayList<String> getFileNames(final File folder) {

        ArrayList<String> list = new ArrayList<>();
        for (final File fileEntry : folder.listFiles()) {
            //System.out.println(fileEntry.getAbsolutePath());
            if (!fileEntry.isDirectory()) {
                list.add((String) fileEntry.getName());
            }
        }
        return list;
    }

    static public ArrayList<File> getDirectories(final File folder) {

        ArrayList<File> list = new ArrayList<File>();
        for (final File fileEntry : folder.listFiles()) {
            if (fileEntry.isDirectory()) {
                list.add((File) fileEntry);
            }
        }
        return list;
    }

    public static String getCurrentDateAndTime(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.now();
        return dtf.format(now);
    }

    public static void addLogEntry(String logFile, String logEntry) {
        try {
            File errorLog = new File(logFile);
            FileOutputStream os = new FileOutputStream(errorLog, true);
            logEntry = Utils.getCurrentDateAndTime() + "," + logEntry;
            logEntry += "\r\n";
            os.write(logEntry.getBytes());
            os.flush();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static public double getDouble(String doubleString) {
        double aDouble;
        try {
            aDouble = Double.valueOf(doubleString);
        } catch (NumberFormatException e) {
            aDouble = -1;
        }
        return aDouble;
    }

    static public int getInt(String intString) {
        int anInt;
        try {
            anInt = Integer.valueOf(intString);
        } catch (NumberFormatException e) {
            anInt = -1;
        }
        return anInt;
    }

    static public long getLong(String longString) {
        long aLong;
        try {
            aLong = Integer.valueOf(longString);
        } catch (NumberFormatException e) {
            aLong = -1;
        }
        return aLong;
    }

    /*
    public static long twoComplement(String hexNumber) {
       
        String binaryNumber = new BigInteger(hexNumber, 16).toString(2);

        // Extender el binario a 32 bits si es necesario
        while (binaryNumber.length() < 32) {
            binaryNumber = "0" + binaryNumber;
        }

        // Calcular el complemento a uno invirtiendo todos los bits
        StringBuilder onesComplement = new StringBuilder();
        for (char bit : binaryNumber.toCharArray()) {
            onesComplement.append(bit == '0' ? '1' : '0');
        }

        // Sumar 1 al complemento a uno para obtener el complemento a dos
        BigInteger twosComplement = new BigInteger(onesComplement.toString(), 2).add(BigInteger.ONE);
        
        return 0;
    }
     */
    public static double calculateTwosComplement(String hexVal) {

        try {
            long longVal = Long.valueOf(hexVal, 16);
            boolean southern = (longVal & 0x80000000) != 0;
            longVal = longVal & 0x7FFFFFFF;
            longVal = ~longVal & 0x7FFFFFFF;
            longVal++;
            if (southern) {
                longVal = -1 * longVal;
            }
            return (double) longVal / 10000000;
        } catch (NumberFormatException anyException) {
            return -180;
        }
    }

    public static byte[] concatenateByteArrays(byte[] array1, byte[] array2) {
        byte[] result = new byte[array1.length + array2.length];

        System.arraycopy(array1, 0, result, 0, array1.length);
        System.arraycopy(array2, 0, result, array1.length, array2.length);

        return result;
    }

    public static String reverseHexPairs(String hex) {
        StringBuilder reversed = new StringBuilder();

        // Verifica si la longitud de la cadena es par
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException("La longitud de la cadena debe ser par.");
        }

        // Itera sobre la cadena en bloques de 2 caracteres y agrégalos al inicio del StringBuilder
        for (int i = hex.length() - 2; i >= 0; i -= 2) {
            reversed.append(hex.substring(i, i + 2));
        }

        return reversed.toString();
    }
}
