package com.appstrax.cordova.plugin;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.nio.charset.StandardCharsets;
public class Format {
    //Polynomial: x^8 + x^5 + x^4 + 1 (0x31)<br>
    //Initial value: 0x0
    //Residue: 0x0
    //The following is the equivalent functionality written in c#.
    public static byte crc8(byte[] paramBytes) {
        int crcInit = 0;
        int bitCount;

        if (paramBytes == null) return 0;

        for (int j = 0; j < paramBytes.length; j++) {
            bitCount = 8;
            crcInit ^= paramBytes[j] & 0xFF;
            do {
                if ((crcInit & 0x80) != 0) crcInit = (crcInit << 1) ^ 0x31;
                else crcInit <<= 1;
                bitCount--;
            } while (bitCount > 0);
        }

        return (byte)crcInit;
    }

    //Polynomial: x^16 + x^12 + x^5 + 1 (0x1021)<br>
    //Initial value: 0xFFFF
    //Residue: 0x1D0F
    //The following is the equivalent functionality(ISO/IEC 13239) written in java.
    public static int crc16(byte[] paramBytes) {
        int crc16 = 0xFFFF;
        int polynomial = 0x1021;   // 0001 0000 0010 0001  (0, 5, 12)
        int x=0,y=0;

        if (paramBytes[0] == 0) {
            x=8; y=1;
        }
        for(int i = x, j = y ; i < paramBytes.length*8; i++) {
            if((i%8) == 0){
                crc16 ^= (paramBytes[j++] << 8) & 0xFF00;
            }
            if((crc16 & 0x8000) != 0)
                crc16 = (((crc16<<1) & 0xFFFE) ^ polynomial);
            else {
                crc16 <<= 1;
                crc16 &= 0xFFFE;
            }
        }
        crc16 &= 0xFFFF;
        return crc16;
    }


    /**
     * Makes up 0 to target string
     * @param str target string
     * @param lenSize the length of string to convert.
     * @return the {#lenSize} of string convert.
     */
    public static String makesUpZero(String str, int lenSize) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < lenSize; i++) sb.append("0");
        String returnValue = sb.toString() + str;

        return returnValue.substring(returnValue.length() - lenSize);
    }


    /**
     * Converts the value of an 8-bit unsigned integer to
     * its equivalent string representation in a specified base.
     * @param bytes the 8-bit unsigned integer to convert.
     * @return The string representation of bytes
     */
    public static String bytesToString(byte[] bytes) {
        return (bytes == null) ? "" : AsciiOctets2String(bytes , false);
    }

    public static String bytesToString(byte[] bytes, boolean b) {
        return (bytes == null) ? "" : AsciiOctets2String(bytes, b);
    }

    static String AsciiOctets2String(byte[] bytes, boolean b) {
        StringBuilder sb = new StringBuilder(bytes.length);

        for(int i = 0; i < bytes.length; i++) {
            char c = (char) (bytes[i] & 0xFF);
            switch (c) {
                case '\u0000': sb.append("<NUL>"); break;
                case '\u0001': sb.append("<SOH>"); break;
                case '\u0002': sb.append("<STX>"); break;
                case '\u0003': sb.append("<ETX>"); break;
                case '\u0004': sb.append("<EOT>"); break;
                case '\u0005': sb.append("<ENQ>"); break;
                case '\u0006': sb.append("<ACK>"); break;
                case '\u0007': sb.append("<BEL>"); break;
                case '\u0008': sb.append("<BS>"); break;
                case '\u0009': sb.append("<HT>"); break;

                case '\u000B': sb.append("<VT>"); break;
                case '\u000C': sb.append("<FF>"); break;

                case '\u000E': sb.append("<SO>"); break;
                case '\u000F': sb.append("<SI>"); break;
                case '\u0010': sb.append("<DLE>"); break;
                case '\u0011': sb.append("<DC1>"); break;
                case '\u0012': sb.append("<DC2>"); break;
                case '\u0013': sb.append("<DC3>"); break;
                case '\u0014': sb.append("<DC4>"); break;
                case '\u0015': sb.append("<NAK>"); break;
                case '\u0016': sb.append("<SYN>"); break;
                case '\u0017': sb.append("<ETB>"); break;
                case '\u0018': sb.append("<CAN>"); break;
                case '\u0019': sb.append("<EM>"); break;
                case '\u001A': sb.append("<SUB>"); break;
                case '\u001B': sb.append("<ESC>"); break;
                case '\u001C': sb.append("<FS>"); break;
                case '\u001D': sb.append("<GS>"); break;
                case '\u001E': sb.append("<RS>"); break;
                case '\u001F': sb.append("<US>"); break;
                case '\u007F': sb.append("<DEL>"); break;
                default:
                    if (c > '\u007F')
                        sb.append(String.format("\\u{0:X4}", c));
                    else
                        sb.append(c);
                    break;
            }
        }
        return sb.toString();
    }

    /// <summary>
    /// ASCII bytes to hexadecimal string
    /// </summary>
    /// <param name="b">ASCII bytes</param>
    /// <returns>hexadecimal string</returns>
    protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
    public static String bytesToHexString(@NonNull byte[] b) {
        char[] hexChars = new char[b.length * 2];
        int v;

        for (int j = 0; j < b.length; j++) {
            v = b[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    /// <summary>
    /// ASCII byte to hexadecimal string
    /// </summary>
    /// <param name="b">ASCII byte</param>
    /// <returns>hexadecimal string</returns>
    public static String byteToHexString(byte b) {
        char[] hexChars = new char[2];
        int v = b & 0xFF;

        hexChars[0] = hexArray[v >> 4];
        hexChars[1] = hexArray[v & 0x0F];
        return new String(hexChars);
    }

    /// <summary>
    /// hexadecimal string to ASCII bytes
    /// </summary>
    /// <param name="hex">hexadecimal string</param>
    /// <returns>ASCII bytes</returns>
    public static byte[] hexStringToBytes(String hex) throws ArithmeticException {
        byte[] b = null;

        if (hex.length() % 2 != 0)
            throw new ArithmeticException("字串長度非2倍數");
        byte[] data = new byte[hex.length()/2];

        for(int i = 0; i < hex.length(); i+=2) {
            data[i/2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i+1), 16));
        }
        return data;
    }

    /// <summary>
    /// hexadecimal string to ASCII byte
    /// </summary>
    /// <param name="s">hexadecimal string</param>
    /// <returns>ASCII byte</returns>
    public static byte hexStringToByte(String s) throws ArrayIndexOutOfBoundsException {
        int len = s.length();
        if (len == 2) {
            return Byte.parseByte(s, 16);
        }
        else
            throw new  ArrayIndexOutOfBoundsException("字串長度非等於2");
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static int hexStringToInt(String s) {
        return Integer.parseInt(s, 16);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static byte[] stringToBytes(@NonNull String s) {
        return s.getBytes(StandardCharsets.US_ASCII);
    }

    /// <summary>
    /// string to hexadecimal string
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static String stringToHexString(String s) {
        byte[] bs = s.getBytes(StandardCharsets.US_ASCII);
        return bytesToHexString(bs);
    }

    /// <summary>
    /// remove \r, \n
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static String removeCRLF(String s) {
        String str = s.replace("\r", "").replace("\n", "");
        return str;
    }

    public static String removeCRLFandTarget(String s, String target) {
        String str = s.replace("\r", "").replace("\n", "").replace(target, "");
        return str;
    }

    /// <summary>
    /// show \r, \n to &lt;CR>, &lt;LF>
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static String showCRLF(String s) {
        String str = s.replace("\r", "<CR>").replace("\n", "<LF>");
        return str;
    }
}