using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace WinRT.BxlComponent.Plugin.BxlMpXamarinSDK.Abstractions
{
    internal class MPosEscPosCmdBuilderHelper
    {
        internal byte[] getSecurityCode()
        {
            byte[] SECURITY_COMMAND = { 0x1B, 0x1D, 0x00, 0x1C, 0x7A, 0x00 };
            byte[] PRINTMODE_COMMAND = { 0x1B, 0x21, 0x10 };
            byte[] JUSTIFICATION_COMMAND = { 0x1B, 0x61, 0x02 };
            byte[] CHARACTERSIZE_COMMAND = { 0x1D, 0x21, 0x10 };
            byte[] CUTTING_COMMAND = { 0x1D, 0x56, 0x42 };
            byte[] NVDOWNLOAD_COMMAND = { 0x1C, 0x71, 0x01 };
            byte[] SECURITY_DATA = new byte[30];

            System.Random rnd = new System.Random(DateTime.Now.Millisecond);
            for (int j = 0; j < SECURITY_DATA.Length; j++)
                SECURITY_DATA[j] = (byte)rnd.Next(0, 255);

            byte[] securityCode = new byte[SECURITY_COMMAND.Length +
                                    PRINTMODE_COMMAND.Length +
                                    JUSTIFICATION_COMMAND.Length +
                                    CHARACTERSIZE_COMMAND.Length +
                                    CUTTING_COMMAND.Length +
                                    NVDOWNLOAD_COMMAND.Length +
                                    SECURITY_DATA.Length];

            byte m = (byte)rnd.Next(81, 99);
            byte n = (byte)rnd.Next(1, 29);
            while ((m % n) != 0)
                n = (byte)rnd.Next(1, 29);

            byte T = (byte)(m % n);
            byte X = (byte)(T / 5);
            byte Y = (byte)(T % 5);
            byte k = (byte)(X + Y);

            if (k < 10)
                k = (byte)(20 - k);

            SECURITY_COMMAND[2] = m;
            SECURITY_COMMAND[5] = n;

            byte cmdLength = 0;
            byte count = 0;
            byte i = 0;

            for (i = 0; i < 6; i++)
                securityCode[cmdLength++] = SECURITY_COMMAND[i];
            for (i = 0; i < Y + 2; i++)
                securityCode[cmdLength++] = SECURITY_DATA[count++];

            switch (Y)
            {
                case 0: for (i = 0; i < 3; i++) securityCode[cmdLength++] = PRINTMODE_COMMAND[i]; break;
                case 1: for (i = 0; i < 3; i++) securityCode[cmdLength++] = JUSTIFICATION_COMMAND[i]; break;
                case 2: for (i = 0; i < 3; i++) securityCode[cmdLength++] = CHARACTERSIZE_COMMAND[i]; break;
                case 3: for (i = 0; i < 3; i++) securityCode[cmdLength++] = CUTTING_COMMAND[i]; break;
                case 4: for (i = 0; i < 3; i++) securityCode[cmdLength++] = NVDOWNLOAD_COMMAND[i]; break;
                default: for (i = 0; i < 3; i++) securityCode[cmdLength++] = PRINTMODE_COMMAND[i]; break;
            }

            for (i = (byte)(count + 3); i < k; i++)
                securityCode[cmdLength++] = SECURITY_DATA[count++];

            return securityCode.Take(cmdLength).ToArray();
        }
        internal byte[] getPrint1DBarcodeCmd(string data, int symbology, int width, int height, int alignment, int textPosition)
        {
            data = data.Trim();

            byte[] attributeCmd = new byte[] {
                0x1b, 0x61, 0x00,	    // Alignment
                0x1d, 0x77, 0x02,	    // Width
                0x1d, 0x68, 0x32,	    // Height
                0x1d, 0x48, 0x00,	    // HRI Position
                0x1d, 0x66, 0x00,	    // HRI Font
                0x1d, 0x6b, 0x00, 0x00,	// Print a bar-code
            };

            switch ((MPos1dCodeType)symbology)
            {
                case MPos1dCodeType.MPOS_BARCODE_UPCA: attributeCmd[17] = 65; break;
                case MPos1dCodeType.MPOS_BARCODE_UPCE: attributeCmd[17] = 66; break;
                case MPos1dCodeType.MPOS_BARCODE_JAN13: attributeCmd[17] = 67; break;
                case MPos1dCodeType.MPOS_BARCODE_JAN8: attributeCmd[17] = 68; break;
                case MPos1dCodeType.MPOS_BARCODE_CODE39: attributeCmd[17] = 69; break;
                case MPos1dCodeType.MPOS_BARCODE_ITF: attributeCmd[17] = 70; break;
                case MPos1dCodeType.MPOS_BARCODE_CODABAR: attributeCmd[17] = 71; break;
                case MPos1dCodeType.MPOS_BARCODE_CODE93: attributeCmd[17] = 72; break;
                case MPos1dCodeType.MPOS_BARCODE_CODE128: attributeCmd[17] = 73; break;
                //case MPos1dCodeType.MPOS_BARCODE_GS1_128:                  attributeCmd[17] = 74; break;
                //case MPos1dCodeType.MPOS_BARCODE_GS1DATABAR_OMNIDIRECTION: attributeCmd[17] = 75; break;
                //case MPos1dCodeType.MPOS_BARCODE_GS1DATABAR_TRUNCATED:     attributeCmd[17] = 76; break;
                //case MPos1dCodeType.MPOS_BARCODE_GS1DATABAR_LIMITED:       attributeCmd[17] = 77; break;
                default:
                    return null;
            }

            switch ((MPosAlignment)alignment)
            {
                case MPosAlignment.MPOS_ALIGNMENT_LEFT: attributeCmd[2] = 0x00; break;
                case MPosAlignment.MPOS_ALIGNMENT_CENTER: attributeCmd[2] = 0x01; break;
                case MPosAlignment.MPOS_ALIGNMENT_RIGHT: attributeCmd[2] = 0x02; break;
                default:
                    attributeCmd[0] = 0x00;
                    attributeCmd[1] = 0x00;
                    break;
            }

            // Barcode Width
            width = width < 1 ? 1 : width;
            width = width > 7 ? 6 : width;
            attributeCmd[5] = (byte)width;

            // Barcode Height
            height = height < 1 ? 1 : height;
            height = height > 255 ? 255 : height;
            attributeCmd[8] = (byte)height;

            switch ((MPosBarcodeTextPosition)textPosition)
            {
                case MPosBarcodeTextPosition.MPOS_BARCODE_TEXT_NONE:
                case MPosBarcodeTextPosition.MPOS_BARCODE_TEXT_ABOVE:
                case MPosBarcodeTextPosition.MPOS_BARCODE_TEXT_BELOW:
                case MPosBarcodeTextPosition.MPOS_BARCODE_TEXT_BOTH:
                    attributeCmd[11] = (byte)textPosition;
                    break;
            }

            byte[] codeData = null;
            byte[] dataWithAttributeCmd = null;

            try
            {
                if ((MPos1dCodeType)symbology == MPos1dCodeType.MPOS_BARCODE_CODE128 && data.Contains("{C"))
                    codeData = getCode128SubsetData(data);
                else
                    codeData = Encoding.UTF8.GetBytes(data);

                // Barcode data length
                attributeCmd[18] = (byte)codeData.Length;

                dataWithAttributeCmd = new byte[attributeCmd.Length + codeData.Length];
                System.Buffer.BlockCopy(attributeCmd, 0, dataWithAttributeCmd, 0, attributeCmd.Length);
                System.Buffer.BlockCopy(codeData, 0, dataWithAttributeCmd, attributeCmd.Length, codeData.Length);
            }
            catch (Exception ex){
                MPosDebug.WriteLine("getPrint1DBarcodeCmd : Exception = " + ex.Message);
            }

            MPosDebug.WriteLine("getPrint1DBarcodeCmd : END");
            return dataWithAttributeCmd;
        }
        private byte[] getCode128SubsetData(string data)
        {
            uint numericData = 0;
            int dataIndex = 0;
            byte[] ConvertedcodeData = null;
            byte[] barCodeData = null;

            // Code128 Subset를 사용할 경우, 식별 문자({A, {B, {C)가 반드시 포함되어 있다고 가정하며, 생략하는 경우는 고려하지 않는다.
            // Subset C 경우, 짝수개의 숫자만 가능하며, 이 조건이 만족되지 않을 경우 NULL를 리턴한다.
            string[] splitResult = data.Split('{');

            foreach (string splitString in splitResult)
            {
                if (splitString.Length == 0)
                    continue;

                if (splitString.Substring(0, 1).CompareTo("C") != 0)
                {
                    // Subset A, B
                    barCodeData = Encoding.UTF8.GetBytes("{" + splitString);
                }
                else
                {
                    // Subset C
                    var charsWithoutIndentifier = splitString.Substring(1, splitString.Length - 1);
                    //if (!charsWithoutIndentifier.ToCharArray().Any(char.IsDigit) || (charsWithoutIndentifier.Length % 2) != 0)
                    if (!charsWithoutIndentifier.ToCharArray().Any(char.IsDigit))
                        return null;

                    string lastChar = "";
                    barCodeData = new byte[(charsWithoutIndentifier.Length / 2) + 2];
                    if (charsWithoutIndentifier.Length % 2 != 0)
                    {
                        lastChar = charsWithoutIndentifier.Substring(charsWithoutIndentifier.Length - 1, 1);
                        charsWithoutIndentifier = charsWithoutIndentifier.Substring(0, charsWithoutIndentifier.Length - 1);
                        barCodeData = new byte[(charsWithoutIndentifier.Length / 2) + 2 /*{C*/ + 3 /*{B + Last Number*/];
                    }

                    barCodeData[dataIndex++] = (byte)'{';
                    barCodeData[dataIndex++] = (byte)'C';
                    for (var i = 0; i < charsWithoutIndentifier.Length; i = i + 2)
                    {
                        numericData = (uint)(charsWithoutIndentifier[i] * 10) - (0x30 * 10);    // tens place
                        numericData += (uint)(charsWithoutIndentifier[i + 1] - 0x30);           // unit(')s place 
                        barCodeData[dataIndex++] = (byte)numericData;
                    }
                    // insert '{B' to print the rest of data with subset B
                    if (lastChar.Length > 0)
                    {
                        barCodeData[dataIndex++] = (byte)'{';
                        barCodeData[dataIndex++] = (byte)'B';
                        barCodeData[dataIndex++] = (byte)lastChar[0];
                    }

                    if (ConvertedcodeData == null)
                    {
                        ConvertedcodeData = new byte[barCodeData.Length];
                        System.Buffer.BlockCopy(barCodeData, 0, ConvertedcodeData, 0, barCodeData.Length);
                    }
                    else
                    {
                        byte[] tempData = new byte[ConvertedcodeData.Length + barCodeData.Length];
                        System.Buffer.BlockCopy(ConvertedcodeData, 0, tempData, 0, ConvertedcodeData.Length);
                        System.Buffer.BlockCopy(barCodeData, 0, tempData, ConvertedcodeData.Length, barCodeData.Length);
                        ConvertedcodeData = tempData;
                    }
                }
            }

            return ConvertedcodeData;
        }
        internal byte[] getPrintTextCmd(byte[] data, MPosFontAttribute attribute)
        {
            byte[] resultBytes = null;

            try
            {
                if (attribute == null && data != null){
                    resultBytes = data;
                }
                else if (attribute != null && data != null){
                    byte[] attributeCmd = getFontAttributeCommand(attribute);
                    byte[] dataWithAttributeCmd = new byte[attributeCmd.Length + data.Length];
                    System.Buffer.BlockCopy(attributeCmd, 0, dataWithAttributeCmd, 0, attributeCmd.Length);
                    System.Buffer.BlockCopy(data, 0, dataWithAttributeCmd, attributeCmd.Length, data.Length);
                    resultBytes = dataWithAttributeCmd;
                }
            }
            catch (Exception){
                resultBytes = null;
            }

            return resultBytes;
        }
        internal byte[] getPrintFarsiTextCmd(string data, MPosFontAttribute attribute)
        {
            try
            {
                byte[] textData = null;
                byte[] attributeCmd = null;
                byte[] dataWithAttributeCmd = null;
                byte[] totalData = null;

                if (attribute == null)
                {
                    // if there is a escape character, it means that data to be parsed for font' attributes exist.
                    if (data.ToCharArray().Any(x => x == 0x1b))
                    {
                        string[] result = data.Split('\x1b');
                        foreach (string part in result)
                        {
                            if (part.Length == 0)
                                continue;

                            string parseData = part.Substring(0, 1).CompareTo("|") == 0 ? part.Substring(1, part.Length - 1) : part;
                            attributeCmd = getEscapeCharCmd(ref parseData);
                            textData = getEncodedTextData(parseData, null);

                            if (totalData == null)
                            {
                                var size = (attributeCmd == null ? 0 : attributeCmd.Length);
                                size += (textData == null ? 0 : textData.Length);
                                totalData = new byte[size];

                                if (attributeCmd != null)
                                    System.Buffer.BlockCopy(attributeCmd, 0, totalData, 0, attributeCmd.Length);
                                if (textData != null)
                                    System.Buffer.BlockCopy(textData, 0, totalData, (attributeCmd == null ? 0 : attributeCmd.Length), textData.Length);
                            }
                            else
                            {
                                var size = (totalData == null ? 0 : totalData.Length);
                                size += (attributeCmd == null ? 0 : attributeCmd.Length);
                                size += (textData == null ? 0 : textData.Length);
                                byte[] tempData = new byte[size];

                                System.Buffer.BlockCopy(totalData, 0, tempData, 0, totalData.Length);
                                if (attributeCmd != null)
                                    System.Buffer.BlockCopy(attributeCmd, 0, tempData, totalData.Length, attributeCmd.Length);
                                if (textData != null)
                                    System.Buffer.BlockCopy(textData, 0, tempData, totalData.Length + (attributeCmd == null ? 0 : attributeCmd.Length), textData.Length);
                                totalData = tempData;
                            }
                        }
                        return totalData;
                    }
                    else{
                        return getEncodedTextData(data, null);
                    }
                }
                else{
                    textData = getEncodedTextData(data, attribute);
                    attributeCmd = getFontAttributeCommand(attribute);
                    dataWithAttributeCmd = new byte[attributeCmd.Length + textData.Length];
                    System.Buffer.BlockCopy(attributeCmd, 0, dataWithAttributeCmd, 0, attributeCmd.Length);
                    System.Buffer.BlockCopy(textData, 0, dataWithAttributeCmd, attributeCmd.Length, textData.Length);
                    return dataWithAttributeCmd;
                }
            }
            catch (Exception){
                
            }

            return null;
        }
        private byte[] getFontAttributeCommand(MPosFontAttribute attribute)
        {
            if (attribute == null)
                return null;

            byte[] fontAttCmd = new byte[]
            {
                0x1b, 0x61, 0x00,	// Alignment
		        0x1b, 0x4d, 0x00,	// FontA, B, C
		        0x1b, 0x45, 0x00,	// Emphasize
		        0x1b, 0x2d, 0x00,	// Underline
		        0x1d, 0x42, 0x00,	// Reverse
		        0x1d, 0x21, 0x00,	// Font Size
	        };

            switch (attribute.Alignment)
            {
                case MPosAlignment.MPOS_ALIGNMENT_LEFT: fontAttCmd[2] = 0x00; break;
                case MPosAlignment.MPOS_ALIGNMENT_CENTER: fontAttCmd[2] = 0x01; break;
                case MPosAlignment.MPOS_ALIGNMENT_RIGHT: fontAttCmd[2] = 0x02; break;
                default:
                    fontAttCmd[0] = 0x00;
                    fontAttCmd[1] = 0x00;
                    break;
            }

            switch (attribute.FontType)
            {
                case MPosFontType.MPOS_FONT_TYPE_A: fontAttCmd[5] = 0x00; break;
                case MPosFontType.MPOS_FONT_TYPE_B: fontAttCmd[5] = 0x01; break;
                case MPosFontType.MPOS_FONT_TYPE_C: fontAttCmd[5] = 0x02; break;
            }

            if (attribute.Bold)
                fontAttCmd[8] = 0x01;

            switch (attribute.Underline)
            {
                case MPosFontUnderline.MPOS_FONT_UNDERLINE_1: fontAttCmd[11] = 0x01; break;
                case MPosFontUnderline.MPOS_FONT_UNDERLINE_2: fontAttCmd[11] = 0x02; break;
            }

            if (attribute.Reverse)
                fontAttCmd[14] = 0x01;

            fontAttCmd[17] = (byte)(Convert.ToInt32(attribute.Width) | Convert.ToInt32(attribute.Height));

            return fontAttCmd;
        }
        private byte[] getEscapeCharCmd(ref string data)
        {
            if (data.Length >= 2 && char.IsDigit(data[0]))
            {
                //System.Text.RegularExpressions.Regex
                //returns a string containing the first occurrence of a number in subjectString.
                string numericValue = Regex.Match(data, @"\d+").Value;
                //string numericValue = new string(data.Where(char.IsDigit).ToArray());

                if (numericValue.Length + 2 <= data.Length)
                {
                    switch (data.Substring(numericValue.Length, 2))
                    {
                        case "lF": // Line Feeds
                            data = data.Substring(numericValue.Length + 2, data.Length - (numericValue.Length + 2));
                            byte[] lineFeed = new byte[int.Parse(numericValue)];
                            for (int i = 0; i < lineFeed.Length; i++)
                                lineFeed[i] = 0x0a;
                            return lineFeed;

                        case "fP":  // Feed to cutting position & Partial Cut
                            data = data.Substring(numericValue.Length + 2, data.Length - (numericValue.Length + 2));
                            return new byte[] { 0x1d, 0x56, 0x42, 0x00 };

                        case "fT":  // Font Type
                            data = data.Substring(numericValue.Length + 2, data.Length - (numericValue.Length + 2));
                            switch (int.Parse(numericValue))
                            {
                                case 0: return new byte[] { 0x08, 0x4d, 0x00, 0x41 };   // Font A
                                case 1: return new byte[] { 0x08, 0x4d, 0x00, 0x42 };   // Font B
                                case 2: return new byte[] { 0x08, 0x4d, 0x00, 0x43 };   // Font C
                                default:
                                    return new byte[] { 0x08, 0x4d, 0x00, 0x41 };       // Font A
                            }

                        case "hC":  // Horizontal Character Size
                            data = data.Substring(numericValue.Length + 2, data.Length - (numericValue.Length + 2));
                            switch (int.Parse(numericValue))
                            {
                                case 1: return new byte[] { 0x1d, 0x21, 0x00 };     // Scale 1 time horizontally
                                case 2: return new byte[] { 0x1d, 0x21, 0x10 };     // Scale 2 time horizontally
                                case 3: return new byte[] { 0x1d, 0x21, 0x20 };     // Scale 3 time horizontally
                                case 4: return new byte[] { 0x1d, 0x21, 0x30 };     // Scale 4 time horizontally
                                case 5: return new byte[] { 0x1d, 0x21, 0x40 };     // Scale 5 time horizontally
                                case 6: return new byte[] { 0x1d, 0x21, 0x50 };     // Scale 6 time horizontally
                                case 7: return new byte[] { 0x1d, 0x21, 0x60 };     // Scale 7 time horizontally
                                case 8: return new byte[] { 0x1d, 0x21, 0x70 };     // Scale 8 time horizontally
                                default:
                                    return new byte[] { 0x1d, 0x21, 0x00 };         // Scale 1 time horizontally
                            }

                        case "vC":  // Vertical Character Size
                            data = data.Substring(numericValue.Length + 2, data.Length - (numericValue.Length + 2));
                            switch (int.Parse(numericValue))
                            {
                                case 1: return new byte[] { 0x1d, 0x21, 0x00 };     // Scale 1 time vertically
                                case 2: return new byte[] { 0x1d, 0x21, 0x01 };     // Scale 2 time vertically
                                case 3: return new byte[] { 0x1d, 0x21, 0x02 };     // Scale 3 time vertically
                                case 4: return new byte[] { 0x1d, 0x21, 0x03 };     // Scale 4 time vertically
                                case 5: return new byte[] { 0x1d, 0x21, 0x04 };     // Scale 5 time vertically
                                case 6: return new byte[] { 0x1d, 0x21, 0x05 };     // Scale 6 time vertically
                                case 7: return new byte[] { 0x1d, 0x21, 0x06 };     // Scale 7 time vertically
                                case 8: return new byte[] { 0x1d, 0x21, 0x07 };     // Scale 8 time vertically
                                default:
                                    return new byte[] { 0x1d, 0x21, 0x00 };         // Scale 1 time horizontally
                            }

                        case "uC":  // Underline
                            data = data.Substring(numericValue.Length + 2, data.Length - (numericValue.Length + 2));
                            switch (int.Parse(numericValue))
                            {
                                case 1: return new byte[] { 0x1b, 0x2d, 0x01 };         // Underline (1 dot thick)
                                case 2: return new byte[] { 0x1b, 0x2d, 0x02 };         // Underline (2 dot thick)
                                default:
                                    return new byte[] { 0x1b, 0x2d, 0x01 };             // Underline (1 dot thick)
                            }
                    }
                }

                if (numericValue.Length + 1 <= data.Length)
                {
                    switch (data.Substring(numericValue.Length, 1))
                    {
                        case "P":
                            data = data.Substring(numericValue.Length + 1, data.Length - (numericValue.Length + 1));
                            return new byte[] { 0x1d, 0x56, 0x31 };

                        case "C":
                            data = data.Substring(numericValue.Length + 1, data.Length - (numericValue.Length + 1));
                            switch (int.Parse(numericValue))
                            {
                                case 1: return new byte[] { 0x1d, 0x21, 0x00 };         // Single height and width
                                case 2: return new byte[] { 0x1d, 0x21, 0x10 };         // double width
                                case 3: return new byte[] { 0x1d, 0x21, 0x01 };         // double height
                                case 4: return new byte[] { 0x1d, 0x21, 0x11 };         // double width & double height
                                default:
                                    return new byte[] { 0x1d, 0x21, 0x00 };             // Single height and width
                            }
                    }
                }
            }

            if (data.Substring(0, data.Length >= 4 ? 4 : data.Length).CompareTo("!rvC") == 0)
            {
                data = data.Length == 4 ? "" : data.Substring(4, data.Length - 4);
                return new byte[] { 0x1d, 0x42, 0x00 };         // Clear Reverse
            }
            else if (data.Substring(0, data.Length >= 3 ? 3 : data.Length).CompareTo("!uC") == 0)
            {
                data = data.Length == 3 ? "" : data.Substring(3, data.Length - 3);
                return new byte[] { 0x1b, 0x2d, 0x00 };         // Clear Underline 
            }
            else if (data.Substring(0, data.Length >= 3 ? 3 : data.Length).CompareTo("!bC") == 0)
            {
                data = data.Length == 3 ? "" : data.Substring(3, data.Length - 3);
                return new byte[] { 0x1b, 0x45, 0x00 };         // Clear Bold
            }
            else if (data.Substring(0, data.Length >= 3 ? 3 : data.Length).CompareTo("rvC") == 0)
            {
                data = data.Length == 3 ? "" : data.Substring(3, data.Length - 3);
                return new byte[] { 0x1d, 0x42, 0x01 };         // Reverse
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("lA") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x1b, 0x61, 0x00 };         // Left Alignment
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("cA") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x1b, 0x61, 0x01 };         // Center Alignment
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("rA") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x1b, 0x61, 0x02 };         // Right Alignment
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("bC") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x1b, 0x45, 0x01 };         // Bold
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("aM") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x08, 0x4d, 0x00, 0x41 };   // Font A
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("bM") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x08, 0x4d, 0x00, 0x42 };   // Font B
            }
            else if (data.Substring(0, data.Length >= 2 ? 2 : data.Length).CompareTo("cM") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x08, 0x4d, 0x00, 0x43 };   // Font C
            }
            else if (data.Substring(0, data.Length >= 1 ? 1 : data.Length).CompareTo("N") == 0)
            {
                data = data.Length == 1 ? "" : data.Substring(1, data.Length - 1);
                return new byte[] { 0x08, 0x4d, 0x00, 0x41,     // Font A
                                    0x1b, 0x61, 0x00,           // Left Alignment 
                                    0x1b, 0x45, 0x00,           // Clear Bold
                                    0x1d, 0x42, 0x00,           // Clear Reverse
                                    0x1b, 0x2d, 0x00,           // Clear Underline
                                    0x1d, 0x21, 0x00,           // Default Size
                                    };
            }
            return null;
        }
        private byte[] getCharactersetCmd(int characterset)
        {
            bool isMember = Enum.GetValues(typeof(MPosCodePage)).Cast<int>().Any(x => x == (int)characterset);
            isMember |= Enum.GetValues(typeof(MPosEastAsiaCodePage)).Cast<int>().Any(x => x == (int)characterset);

            // MPosCodePage에 정의된 상수는 아니지만, 확장성을 고려하여 100이하의 값은 true로 처리한다.
            if (characterset >= 0 && characterset <= 100)
                isMember = true;

            if (isMember != true){
                return null;
            }

            byte[] command = new byte[] { 0x1b, 0x74, 0x00 };

            switch ((MPosCodePage)characterset)
            {
                case MPosCodePage.MPOS_CODEPAGE_CP437: command[2] = 0; break;
                case MPosCodePage.MPOS_CODEPAGE_KATAKANA: command[2] = 1; break;
                case MPosCodePage.MPOS_CODEPAGE_CP850: command[2] = 2; break;
                case MPosCodePage.MPOS_CODEPAGE_CP860: command[2] = 3; break;
                case MPosCodePage.MPOS_CODEPAGE_CP863: command[2] = 4; break;
                case MPosCodePage.MPOS_CODEPAGE_CP865: command[2] = 5; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1252: command[2] = 16; break;
                case MPosCodePage.MPOS_CODEPAGE_CP866: command[2] = 17; break;
                case MPosCodePage.MPOS_CODEPAGE_CP852: command[2] = 18; break;
                case MPosCodePage.MPOS_CODEPAGE_CP858: command[2] = 19; break;
                case MPosCodePage.MPOS_CODEPAGE_CP862: command[2] = 21; break;
                case MPosCodePage.MPOS_CODEPAGE_CP864: command[2] = 22; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI42: command[2] = 23; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1253: command[2] = 24; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1254: command[2] = 25; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1257: command[2] = 26; break;
                case MPosCodePage.MPOS_CODEPAGE_FARSI: command[2] = 27; break;
                case MPosCodePage.MPOS_CODEPAGE_FARSI_II: command[2] = 27; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1251: command[2] = 28; break;
                case MPosCodePage.MPOS_CODEPAGE_CP737: command[2] = 29; break;
                case MPosCodePage.MPOS_CODEPAGE_CP775: command[2] = 30; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI14: command[2] = 31; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1255: command[2] = 33; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI11: command[2] = 34; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI18: command[2] = 35; break;
                case MPosCodePage.MPOS_CODEPAGE_CP855: command[2] = 36; break;
                case MPosCodePage.MPOS_CODEPAGE_CP857: command[2] = 37; break;
                case MPosCodePage.MPOS_CODEPAGE_CP928: command[2] = 38; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI16: command[2] = 39; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1256: command[2] = 40; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1258: command[2] = 41; break;
                case MPosCodePage.MPOS_CODEPAGE_KHMER: command[2] = 42; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1250: command[2] = 47; break;
                case MPosCodePage.MPOS_CODEPAGE_LATIN9: command[2] = 48; break;
                case MPosCodePage.MPOS_CODEPAGE_TCVN: command[2] = 49; break;
                case MPosCodePage.MPOS_CODEPAGE_TCVN_CAPTIAL: command[2] = 50; break;
                case MPosCodePage.MPOS_CODEPAGE_VISCII: command[2] = 51; break;
                default:
                    command[2] = (byte)characterset;
                    break;
            }

            switch ((MPosEastAsiaCodePage)characterset)
            {
                case MPosEastAsiaCodePage.MPOS_CODEPAGE_SHIFTJIS:
                    command[2] = 1;
                    break;
                case MPosEastAsiaCodePage.MPOS_CODEPAGE_KSC5601:
                case MPosEastAsiaCodePage.MPOS_CODEPAGE_BIG5:
                case MPosEastAsiaCodePage.MPOS_CODEPAGE_GB2312:
                case MPosEastAsiaCodePage.MPOS_CODEPAGE_GB18030:
                    command = null;
                    break;
            }
            return command;
        }
        private byte[] getEncodedTextData(string data, MPosFontAttribute attribute = null)
        {
            byte[] encodedData = null;

            if (data.Length <= 0)
                return null;

            byte[] codepageCmd = null;

            if (attribute == null)
            {
                codepageCmd = getCharactersetCmd((int)MPosCodePage.MPOS_CODEPAGE_FARSI);
                encodedData = Encoding.UTF8.GetBytes(data);
                if (codepageCmd != null)
                {
                    var totalData = new byte[codepageCmd.Length + encodedData.Length];
                    System.Buffer.BlockCopy(codepageCmd, 0, totalData, 0, codepageCmd.Length);
                    System.Buffer.BlockCopy(encodedData, 0, totalData, codepageCmd.Length, encodedData.Length);
                    return totalData;
                }
                return encodedData;
            }

            codepageCmd = getCharactersetCmd(attribute.CodePage);

            switch ((MPosCodePage)attribute.CodePage)
            {
                case MPosCodePage.MPOS_CODEPAGE_FARSI:
                case MPosCodePage.MPOS_CODEPAGE_FARSI_II:
                    encodedData = MPosCharacterEncoder.getFarsiEncodingData(data, (MPosCodePage)attribute.CodePage);
                    break;
                case MPosCodePage.MPOS_CODEPAGE_TCVN:
                case MPosCodePage.MPOS_CODEPAGE_TCVN_CAPTIAL:
                case MPosCodePage.MPOS_CODEPAGE_VISCII:
                    encodedData = MPosCharacterEncoder.getVietnamEncodingData(data, (MPosCodePage)attribute.CodePage);
                    break;
                default:
                    encodedData = Encoding.UTF8.GetBytes(data);
                    break;
            }

            if (codepageCmd != null)
            {
                var totalData = new byte[codepageCmd.Length + encodedData.Length];
                System.Buffer.BlockCopy(codepageCmd, 0, totalData, 0, codepageCmd.Length);
                System.Buffer.BlockCopy(encodedData, 0, totalData, codepageCmd.Length, encodedData.Length);
                return totalData;
            }

            return encodedData;
        }
    }
}
