using Foundation;
using System.Runtime.InteropServices;

namespace AsReaderSDK4MAUIDemo.Platforms.iOS
{
    public static class EpcConverter
    {
        static NSString bytesToBinaryString(this NSData data)
        {
            NSMutableString str = new NSMutableString();

            byte[] ptr = data.ToArray();

            Marshal.Copy(data.Bytes, ptr, 0, Convert.ToInt32(data.Length));


            for (int i = 0; i < (int)data.Length; i++)
            {
                switch (ptr[i] & 0x0F)
                {
                    case 0x00: str.Append(new NSString("0000")); break;
                    case 0x01: str.Append(new NSString("0001")); break;
                    case 0x02: str.Append(new NSString("0010")); break;
                    case 0x03: str.Append(new NSString("0011")); break;
                    case 0x04: str.Append(new NSString("0100")); break;
                    case 0x05: str.Append(new NSString("0101")); break;
                    case 0x06: str.Append(new NSString("0110")); break;
                    case 0x07: str.Append(new NSString("0111")); break;
                    case 0x08: str.Append(new NSString("1000")); break;
                    case 0x09: str.Append(new NSString("1001")); break;
                    case 0x0A: str.Append(new NSString("1010")); break;
                    case 0x0B: str.Append(new NSString("1011")); break;
                    case 0x0C: str.Append(new NSString("1100")); break;
                    case 0x0D: str.Append(new NSString("1101")); break;
                    case 0x0E: str.Append(new NSString("1110")); break;
                    case 0x0F: str.Append(new NSString("1111")); break;
                }
            }

            return str;

        }

        public static long binStringToLong(this string str)
        {
            long data = 0;
            int idx;

            for (idx = 0; idx < str.Length; idx++)
            {
                string binStr = str.Substring(idx, 1);
                data |= (int.Parse(binStr) & 0x01) << (str.Length - idx - 1);
            }
            return data;
        }

        static NSString stringByPaddingLeftToLength(this string str, int newLength, string padString)
        {
            string returnStr = str;
            if (returnStr.Length < newLength)
            {
                int length = newLength - returnStr.Length;
                string appendingStr = "";
                for (int i = 0; appendingStr.Length < newLength; i++)
                {
                    if (i == padString.Length) i = 0;
                    string s = padString.Substring(i, 1);
                    appendingStr += s;
                }
                returnStr = appendingStr + returnStr;
            }
            return new NSString(returnStr);
        }


        public static string toTypeString(int type)
        {
            switch (type)
            {
                case 0:
                    return @"HEX";
                case 1:
                    return @"ASCII";
                case 2:
                    return @"SGTIN96";
                case 3:
                    return @"EAN13";
                default:
                    return null;
            }
        }
        public static int getEpcStartDigit(NSData data)
        {
            int digit = 2; // PC

            byte[] ptr = data.ToArray();
            Marshal.Copy(data.Bytes, ptr, 0, Convert.ToInt32(data.Length));

            if ((ptr[0] & 0x02) == 0x02)
            {
                if ((ptr[2] & 0x08) == 0x08)
                {
                    digit = 6; // PC + XPC_W1 + XPC_W2
                }
                else
                {
                    digit = 4; // PC + XPC_W1
                }
            }
            return digit;
        }
        public static string toHexString(NSData data)
        {
            string str = "";
            byte[] ptr = data.ToArray();

            Marshal.Copy(data.Bytes, ptr, 0, Convert.ToInt32(data.Length));
            for (int i = 0; i < (int)data.Length; i++) str += (ptr[i] & 0xFF).ToString("X2");
            return str;
        }
        public static int getPartition(string bin)
        {
            return int.Parse(bin.Substring(3, 3));
        }

        public static int getCompanyPrefixBitLen(int partition)
        {
            int[] gCompanyPrefixBitLen = { 40, 37, 34, 30, 27, 24, 20 };
            if (partition < 0 || partition > 6)
                return 0;
            return gCompanyPrefixBitLen[partition];
        }
        public static string getCompanyPrefix(string bin)
        {
            int nPartition = getPartition(bin);
            int nCompanyPrefixLen = getCompanyPrefixBitLen(nPartition);
            string binCompanyPrefix = bin.Substring(6, nCompanyPrefixLen);
            long nCompanyPrefix = binCompanyPrefix.binStringToLong();
            int digitCompanyPrefixLen = 12 - nPartition;
            string ret = nCompanyPrefix.ToString();
            return ret.stringByPaddingLeftToLength(digitCompanyPrefixLen, "0");
        }
        public static string getItemReference(string bin)
        {
            int nPartition = getPartition(bin);
            int nCompanyPrefixLen = getCompanyPrefixBitLen(nPartition);
            int nItemReferenceLen = 44 - nCompanyPrefixLen;
            string binItemReference = bin.Substring(6 + nCompanyPrefixLen, nItemReferenceLen);
            long nItemReference = binItemReference.binStringToLong();
            int digitItemReferenceLen = 1 + nPartition;
            string ret = nItemReference.ToString();
            return ret.stringByPaddingLeftToLength(digitItemReferenceLen, "0");
        }
        public static string getSerial(string bin)
        {
            int nPartition = getPartition(bin);
            int nCompanyPrefixLen = getCompanyPrefixBitLen(nPartition);
            int nItemReferenceLen = 44 - nCompanyPrefixLen;
            string binSerial = bin.Substring(6 + nCompanyPrefixLen + nItemReferenceLen, 38);
            long nSerial = binSerial.binStringToLong();
            return nSerial.ToString();
        }
        public static string getFilter(string bin)
        {
            string binFilter = bin.Substring(0, 3);
            long nFilt = binFilter.binStringToLong();
            return nFilt.ToString();
        }
        public static string toSGTIN96(NSData data)
        {
            string bin = toBinString(data);
            if (bin.Length == 0) return "NON-SGTIN96";

            string str = "";
            str += getFilter(bin);
            str += ".";
            str += getCompanyPrefix(bin);
            str += ".";
            str += getItemReference(bin);
            str += ".";
            str += getSerial(bin);
            return str;
        }
        public static string toEAN13(NSData data)
        {
            string bin = toBinString(data);
            if (bin.Length == 0) return "NON-SGTIN96";

            string str = "";
            str += getCompanyPrefix(bin);
            str += getItemReference(bin);
            str = str.Substring(1);
            return appendParity(str);
        }
        public static string toAscii(NSData data)
        {
            int len = (int)data.Length - getEpcStartDigit(data);
            byte[] ptr = data.ToArray();

            Marshal.Copy(data.Bytes, ptr, 0, Convert.ToInt32(data.Length));
            byte[] newArray = new byte[len];
            int idx = 0;
            for (int i = getEpcStartDigit(data); i < (int)data.Length; i++)
            {
                byte temp = (byte)ptr[i];
                if (temp > 0 && temp < 127)
                {
                    newArray[idx++] = temp;
                }
                else
                {
                    newArray[idx++] = 127;
                }
            }

            string str = System.Text.Encoding.Default.GetString(newArray);
            return str.ToString();
        }
        public static string toBinString(NSData data)
        {
            if (data.Length < 14) return @"";
            int startDigit = getEpcStartDigit(data);
            if (data.Length < 12 + (nuint)startDigit) return @"";
            byte[] ptr = data.ToArray();
            Marshal.Copy(data.Bytes, ptr, 0, Convert.ToInt32(data.Length));
            if (ptr[startDigit] != 0x30) return @"";
            NSData newData = data.Subdata(new NSRange(startDigit + 1, (int)data.Length - startDigit - 1));
            return newData.bytesToBinaryString();

        }
        public static string appendParity(string message)
        {
            int sum = 0;
            int parity = 0;
            int[] gWeighting = { 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3 };
            for (int i = 0; i < 12; i++)
            {
                string str = message.Substring(i, 1);
                sum += int.Parse(str) * gWeighting[i];
            }
            parity = 10 - (sum % 10);
            if (parity == 10)
            {
                parity = 0;
            }
            return message + parity.ToString();
        }


        public static string toString(int type, NSData data)
        {
            switch (type)
            {
                case 1:
                    return toAscii(data);
                case 2:
                    return toSGTIN96(data);
                case 3:
                    return toEAN13(data);
                default:
                    return toHexString(data);
            }
        }

    }
}
