using System;
using WinRT.BxlComponent.Plugin.BxlMpXamarinSDK.Abstractions;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Windows.UI.Xaml.Media.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Data.Pdf;
using Microsoft.ReactNative.Managed;
using Microsoft.ReactNative;

namespace WinRT.BxlComponent.Plugin.BxlMpXamarinSDK
{
    /// <summary>
    /// Implementation for Feature
    /// </summary>
    internal partial class MPosDevicePrinterImplementation : MPosControllerPrinter
    {
        public MPosDeviceType DeviceType { get; private set; }
        public string _ModelName { get; private set; }
        public string _FirmwareVersion { get; private set; }
        public string _CodePageName { get; private set; }
        public int DeviceID { get; private set; }
        public MPosInterfaceType InterfaceType { get; private set; } = MPosInterfaceType.MPOS_INTERFACE_BLUETOOTH;
        public MPosCommandMode CommandMode { get; private set; } = MPosCommandMode.MPOS_COMMAND_MODE_BGATE;
        public bool IsOpen { get; private set; } = false;
        public int _TextEncoding { get; private set; } = (int)MPosCodePage.MPOS_CODEPAGE_WPC1252;
        public bool _AsbEnable { get; private set; } = false;
        public bool _PageModeInOut { get; private set; }
        public bool _TransactionModeInOut { get; private set; }
        public string _ConnectionString { get; private set; }
        public IMPosConnection _MPosConnection { get; private set; } = null;
        private MPosDeviceDataProcessor _DeviceDataProcessor = new MPosDeviceDataProcessor();
        private string _DebugId;
        public MPosDevicePrinterImplementation(int deviceType, int deviceId)
        {
            MPosDebug.WriteLine("DevicePrinterImplementation Constructor : START", _DebugId);
            DeviceID = deviceId;
            DeviceType = (MPosDeviceType)deviceType;
            _DebugId = DebugHelper.GetId(this);
            MPosDebug.WriteLine("DevicePrinterImplementation Constructor : DeviceID = " + DeviceID, _DebugId);
            MPosDebug.WriteLine("DevicePrinterImplementation Constructor : DeviceType = " + DeviceType, _DebugId);
            MPosDebug.WriteLine("DevicePrinterImplementation Constructor : END", _DebugId);
        }

        // IMPosDeviceControllerDevice Methods
        public async Task<int> openService(uint deviceId = 0, uint timeout = 3 /*second*/)
        {
            if (deviceId == 0xffffffff)
                deviceId = (uint)DeviceID;

            int result = (int)MPosResult.MPOS_SUCCESS;
            MPosDebug.WriteLine("PrinterImplementation openService : START", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation openService : deviceId = " + deviceId + ", timeout = " + timeout, _DebugId);
            MPosDebug.WriteLine("PrinterImplementation openService : InterfaceType = " + InterfaceType + ", _ConnectionString = " + _ConnectionString, _DebugId);
            MPosDebug.WriteLine("PrinterImplementation openService : CommandMode = " + CommandMode, _DebugId);

            if (IsOpen)
            {
                MPosDebug.WriteLine("PrinterImplementation openService : MPOS_ALREADY_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation openService : END", _DebugId);
                return (int)MPosResult.MPOS_ALREADY_OPEN;
            }

            _MPosConnection = MPosConnectionBuilderImplementation.Build(InterfaceType, _ConnectionString, (int)CommandMode);

            if (_MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation openService : MPosConnectionBuilder.Current.Build failed.", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation openService : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NOT_CONNECT;
            }

            try
            {
                _DeviceDataProcessor.initDataStream();
                _MPosConnection.addDataReadEvent(onDataReadEvent);
                _MPosConnection.addDisconnectedEvent(onDisconnectedEvent);
                _MPosConnection.addConnectedEvent(onConnectedEvent);
                _MPosConnection.addStatusUpdateEvent(this.onStatusUpdateEvent);

                if (await _MPosConnection.openAsync(DeviceType, timeout))
                {
                    IsOpen = true;
                    if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
                    {
                        if (deviceId <= 0 || deviceId >= 255)
                            deviceId = (uint)await _MPosConnection.getLowestDeviceId(1500, this);
                        if (!await _MPosConnection.isAvailableDeviceId(this, (uint)deviceId))
                            IsOpen = false;
                        else
                            DeviceID = (int)deviceId;
                    }
                    else
                        DeviceID = (int)deviceId;

                    onConnectedEvent();

                    if (IsOpen)
                    {
                        _ModelName = "";
                        if (await getModelName(null) == (int)MPosResult.MPOS_SUCCESS)
                        {
                            MPosDebug.WriteLine("PrinterImplementation openService : _ModelName = " + _ModelName, _DebugId);
                            if (_ModelName.Contains("SRP") || _ModelName.Contains("SPP") || _ModelName.Contains("STP") || _ModelName.Contains("BGT"))
                            {
                                IsOpen = true;
                            }
                        }
                        else
                        {
                            IsOpen = false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation openService : ex.Message = " + ex.Message, _DebugId);
                IsOpen = false;
            }

            if (!IsOpen)
            {
                await _MPosConnection.closeAsync();
                _MPosConnection = null;
                result = (int)MPosResult.MPOS_FAIL_NOT_CONNECT;
            }
            else
            {
                result = (int)MPosResult.MPOS_SUCCESS;
            }

            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE && result == (int)MPosResult.MPOS_SUCCESS)
                MPosConnectionBuilderImplementation.AddDevice(DeviceID);

            MPosDebug.WriteLine("PrinterImplementation openService : END", _DebugId);
            return result;
        }

        public async Task<int> openService(uint timeout = 3 /*second*/)
        {
            if (IsOpen)
            {
                MPosDebug.WriteLine("PrinterImplementation openService : MPOS_ALREADY_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation openService : END", _DebugId);
                return (int)MPosResult.MPOS_ALREADY_OPEN;
            }

            return await openService((uint)DeviceID, timeout);
        }
        public async Task<int> closeService(uint closeTimeout = 0 /*second*/)
        {
            MPosDebug.WriteLine("PrinterImplementation closeService : START", _DebugId);

            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            {
                MPosConnectionBuilderImplementation.RemoveDevice(DeviceID);
            }

            IsOpen = false;
            _AsbEnable = false;
            statusUpdateEvent = null;
            statusUpdateEvent2 = null;
            dataEvent = null;

            _DeviceDataProcessor.clearDataStream();

            if (_MPosConnection != null)
            {
                _MPosConnection.removeStatusUpdateEvent(this.onStatusUpdateEvent);
                _MPosConnection.removeDataReadEvent(this.onDataReadEvent);
                _MPosConnection.removeDisconnectedEvent(this.onDisconnectedEvent);
                _MPosConnection.removeConnectedEvent(this.onConnectedEvent);
                await _MPosConnection.closeAsync(closeTimeout);
                _MPosConnection = null;
            }

            MPosDebug.WriteLine("PrinterImplementation closeService : END", _DebugId);
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public int selectInterface(int interfaceType, string address)
        {
            MPosDebug.WriteLine("PrinterImplementation selectInterface : START", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation selectInterface : interfaceType = " + interfaceType.ToString(), _DebugId);
            MPosDebug.WriteLine("PrinterImplementation selectInterface : address = " + address.ToString(), _DebugId);
            /*
            if (IsOpen){
                MPosDebug.WriteLine("PrinterImplementation selectInterface : MPOS_FAIL_OPENED", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation selectInterface : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_OPENED;
            }*/

            bool isMember = Enum.GetValues(typeof(MPosInterfaceType)).Cast<int>().Any(x => x == (int)interfaceType);

            if (isMember)
            {
                InterfaceType = (MPosInterfaceType)interfaceType;
                _ConnectionString = address;
                MPosDebug.WriteLine("PrinterImplementation selectInterface : _ConnectionString = " + _ConnectionString, _DebugId);
                MPosDebug.WriteLine("PrinterImplementation selectInterface : END", _DebugId);
                return (int)MPosResult.MPOS_SUCCESS;
            }

            MPosDebug.WriteLine("PrinterImplementation selectInterface : MPOS_FAIL_INVALID_PARAMETER", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation selectInterface : END", _DebugId);
            return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
        }
        public int selectCommandMode(int commandMode)
        {
            MPosDebug.WriteLine("PrinterImplementation selectCommandMode : START", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation selectCommandMode : commandMode = " + commandMode.ToString(), _DebugId);

            bool isMember = Enum.GetValues(typeof(MPosCommandMode)).Cast<int>().Any(x => x == (int)commandMode);

            if (isMember)
            {
                CommandMode = (MPosCommandMode)commandMode;
                MPosDebug.WriteLine("PrinterImplementation selectCommandMode : CommandMode = " + CommandMode.ToString(), _DebugId);
                MPosDebug.WriteLine("PrinterImplementation selectCommandMode : END", _DebugId);
                return (int)MPosResult.MPOS_SUCCESS;
            }

            MPosDebug.WriteLine("PrinterImplementation selectCommandMode : MPOS_FAIL_INVALID_PARAMETER", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation selectCommandMode : END", _DebugId);
            return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
        }
        public bool isOpen()
        {
            return IsOpen;
        }
        public async Task<int> directIO(byte[] data)
        {
            MPosDebug.WriteLine("PrinterImplementation directIO : START", _DebugId);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation directIO : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation directIO : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            int writtenBytes = 0;

            if (data != null)
            {
                MPosDebug.WriteLine("PrinterImplementation directIO : data.Length = " + data.Length, _DebugId);
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, data);
                MPosDebug.WriteLine("PrinterImplementation directIO : written bytes = " + writtenBytes, _DebugId);

                var expectedDataLength = MPosByteArrayHelper.searchBytePattern(new byte[] { 0x1d, 0x49 }, data).Count;
                expectedDataLength += MPosByteArrayHelper.searchBytePattern(new byte[] { 0x10, 0x04 }, data).Count;
                if (expectedDataLength > 0)
                {
                    // in case 'GS I' or 'DLE EOT' pattern found, call this method without 'await' keyword.
                    var getDataTask = _DeviceDataProcessor.getDataFromMemoryStream(0, (dataArray) => { dataEvent?.Invoke(dataArray); });
                }
            }

            MPosDebug.WriteLine("PrinterImplementation directIO : END", _DebugId);
            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }

    }
    internal partial class MPosDevicePrinterImplementation : MPosControllerPrinter
    {
        private byte[] getEscapeCharCmd(ref string data)
        {
            MPosDebug.WriteLine("PrinterImplementation getEscapeCharCmd : data = " + data + ", length = " + data.Length, _DebugId);

            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("uC") == 0)
            {
                data = data.Length == 2 ? "" : data.Substring(2, data.Length - 2);
                return new byte[] { 0x1b, 0x2d, 0x02 };         // Underline 2dot
            }
            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("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 >= 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 >= 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
                                    };
            }

            MPosDebug.WriteLine("PrinterImplementation getEscapeCharCmd : NO MATCH", _DebugId);
            return null;
        }
        private byte[] getCode128SubsetData(string data, byte[] attributeCmd)
        {
            int numericData = 0;
            var codeIndex = 2;
            byte[] codeData = null;
            byte[] parsedCodeData = null;

            // Code128 Subset C
            if (data.Substring(0, 2).CompareTo("{C") == 0 &&
                int.TryParse(data.Substring(2, data.Length - 2), out numericData) && (data.Length % 2) == 0)
            {
                codeData = Encoding.ASCII.GetBytes(data);
                parsedCodeData = new byte[(codeData.Length / 2) + 1];

                System.Buffer.BlockCopy(codeData, 0, parsedCodeData, 0, 2);

                for (var i = 2; i < data.Length; i = i + 2)
                {
                    numericData = (codeData[i] * 10) - (0x30 * 10);    // tens place
                    numericData += (codeData[i + 1] - 0x30);             // unit(')s place 
                    parsedCodeData[codeIndex++] = (byte)numericData;
                }

                MPosDebug.WriteLine("PrinterImplementation getCode128SubsetData : parsedCodeData.Length = " + parsedCodeData.Length);
                attributeCmd[18] = (byte)parsedCodeData.Length;
            }
            else
            {
                parsedCodeData = Encoding.ASCII.GetBytes(data);
            }

            return parsedCodeData;
        }
        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[] getCharactersetCmd(int characterset)
        {
            MPosDebug.WriteLine("PrinterImplementation getCharactersetCmd : START", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation getCharactersetCmd : characterset = " + characterset, _DebugId);

            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)
            {
                MPosDebug.WriteLine("PrinterImplementation getCharactersetCmd : END", _DebugId);
                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;
            }

            if (command != null)
                MPosDebug.WriteLine("PrinterImplementation getCharactersetCmd : command[2] = " + command[2].ToString(), _DebugId);
            MPosDebug.WriteLine("PrinterImplementation getCharactersetCmd : END", _DebugId);
            return command;
        }
        private byte[] getEncodedTextData(string data, MPosFontAttribute attribute = null)
        {
            byte[] encodedData = null;
            int chIndex = 0;
            int codePageNumber = 0;

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

            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            byte[] codepageCmd = null;

            if (attribute == null)
            {
                codePageNumber = _TextEncoding;
            }
            else
            {
                codePageNumber = attribute.CodePage;
            }

            codepageCmd = getCharactersetCmd(codePageNumber);

            switch ((MPosCodePage)codePageNumber)
            {
                // Arabic, KHMER
                case MPosCodePage.MPOS_CODEPAGE_CP864:
                case MPosCodePage.MPOS_CODEPAGE_WPC1256:
                case MPosCodePage.MPOS_CODEPAGE_KHMER:
                    encodedData = Encoding.GetEncoding(437).GetBytes("NOT SUPPORTED CODEPAGE");
                    break;
                // Farsi
                case MPosCodePage.MPOS_CODEPAGE_FARSI:
                case MPosCodePage.MPOS_CODEPAGE_FARSI_II:
                    encodedData = MPosCharacterEncoder.getFarsiEncodingData(data, (MPosCodePage)codePageNumber);
                    break;
                // Thai
                case MPosCodePage.MPOS_CODEPAGE_THAI11:
                case MPosCodePage.MPOS_CODEPAGE_THAI14:
                case MPosCodePage.MPOS_CODEPAGE_THAI16:
                case MPosCodePage.MPOS_CODEPAGE_THAI18:
                case MPosCodePage.MPOS_CODEPAGE_THAI42:
                    encodedData = Encoding.GetEncoding(874).GetBytes(data);
                    if (_ModelName.Contains("SPP"))
                    {
                        // BSW: 3-5  (POS PRINTERS, 3 PASS ENABLE)
                        // Mobile Printer: the command for Thai 3pass exist.
                        var dataWith3PassEnable = new byte[encodedData.Length + 8];
                        System.Buffer.BlockCopy(new byte[] { 0x1f, 0x1f, 0x09, 0x01 }, 0, dataWith3PassEnable, 0, 4);
                        System.Buffer.BlockCopy(encodedData, 0, dataWith3PassEnable, 4, encodedData.Length);
                        System.Buffer.BlockCopy(new byte[] { 0x1f, 0x1f, 0x09, 0x00 }, 0, dataWith3PassEnable, 4 + encodedData.Length, 4);
                        encodedData = dataWith3PassEnable;
                    }
                    break;
                // Vietnam
                //case MPosCodePage.MPOS_CODEPAGE_WPC1258:
                case MPosCodePage.MPOS_CODEPAGE_TCVN:
                case MPosCodePage.MPOS_CODEPAGE_TCVN_CAPTIAL:
                case MPosCodePage.MPOS_CODEPAGE_VISCII:
                    //encodedData = MPosCharacterEncoding.GetTcvnCharacterBytes(data);
                    encodedData = MPosCharacterEncoder.getVietnamEncodingData(data, (MPosCodePage)codePageNumber);
                    break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1251:
                    chIndex = 0;
                    encodedData = new byte[data.Length + data.Count(c => c == 'Ү' || c == 'ү' || c == 'Ө' || c == 'ө')];
                    foreach (var ch in data)
                    {
                        switch ((ushort)ch)
                        {
                            // Characters below are not included in WCP1251 codepage, so just put it into the byte-array as it is.
                            case 0x04AE:    // 'Ү' 
                            case 0x04AF:    // 'ү'
                            case 0x04E8:    // 'Ө'
                            case 0x04E9:    // 'ө'
                                // Order : Big endian 
                                encodedData[chIndex++] = (byte)(Convert.ToUInt16(ch) >> 8);     // Upper Byte
                                encodedData[chIndex++] = (byte)(Convert.ToUInt16(ch) & 0xFF);   // Lower Byte
                                break;
                            default:
                                encodedData[chIndex++] = Encoding.GetEncoding(1251).GetBytes(ch.ToString()).First();
                                break;
                        }
                    }
                    break;
                default:
                    encodedData = Encoding.GetEncoding(codePageNumber).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;
        }
        private async Task<byte[]> getPrintImageData(WriteableBitmap wb, int width, int alignment, int threshold, bool isDithering = true, int compressType = 1)
        {
            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : START");
            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : WriteableBitmap");

            uint[] imgSize = new uint[] { 0, 0 };
            byte[] pixelImgData = null;
            byte[] imgPrintCmd = null;
            StorageFile file = null;
            Guid encoderId = BitmapEncoder.BmpEncoderId;
            Guid decoderId = BitmapDecoder.BmpDecoderId;

            if (wb == null)
            {
                MPosDebug.WriteLine("PrinterImplementation getPrintImageData : END");
                return null;
            }

            for (int i = 0; i < 6; i++)
            {
                switch (i)
                {
                    case 0: encoderId = BitmapEncoder.BmpEncoderId; decoderId = BitmapDecoder.BmpDecoderId; break;
                    case 1: encoderId = BitmapEncoder.PngEncoderId; decoderId = BitmapDecoder.PngDecoderId; break;
                    case 2: encoderId = BitmapEncoder.JpegEncoderId; decoderId = BitmapDecoder.JpegDecoderId; break;
                    case 3: encoderId = BitmapEncoder.GifEncoderId; decoderId = BitmapDecoder.GifDecoderId; break;
                    case 4: encoderId = BitmapEncoder.TiffEncoderId; decoderId = BitmapDecoder.TiffDecoderId; break;
                    case 5: encoderId = BitmapEncoder.JpegXREncoderId; decoderId = BitmapDecoder.JpegXRDecoderId; break;
                }

                file = await MPosImagePrintCommand.writeableBitmapToStorageFile(wb, encoderId);

                if (file != null)
                {
                    pixelImgData = await MPosImagePrintCommand.getResizedPixelData(file, decoderId, width, imgSize);
                    file = null;
                    await MPosImagePrintCommand.deleteTempImageFile();
                }

                // cannot print image with RLE in page mode.
                compressType = _PageModeInOut ? 0 : compressType;

                if (pixelImgData != null)
                {
                    if ((MPosCompressType)compressType == MPosCompressType.MPOS_IMAGE_COMPRESS_RLE)
                        imgPrintCmd = MPosImagePrintCommand.getRleCmd(pixelImgData, imgSize[0], imgSize[1], (uint)50, alignment, isDithering, threshold);
                    else if ((MPosCompressType)compressType == MPosCompressType.MPOS_IMAGE_COMPRESS_LZMA)
                        imgPrintCmd = MPosImagePrintCommand.getLZMACmd(pixelImgData, imgSize[0], imgSize[1], (uint)50, alignment, isDithering, threshold);
                    else
                        imgPrintCmd = MPosImagePrintCommand.getGSv0Cmd(pixelImgData, imgSize[0], imgSize[1], (uint)50, alignment, isDithering, threshold);
                    break;
                }

                pixelImgData = null;
            }

            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : END");
            return imgPrintCmd;
        }
        private async Task<byte[]> getPrintImageData(WriteableBitmap wb, int width, int alignment, int brightness, bool isDithering = true, bool isCompress = true)
        {
            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : START");
            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : WriteableBitmap");

            uint[] imgSize = new uint[] { 0, 0 };
            byte[] pixelImgData = null;
            byte[] imgPrintCmd = null;
            StorageFile file = null;
            Guid encoderId = BitmapEncoder.BmpEncoderId;
            Guid decoderId = BitmapDecoder.BmpDecoderId;

            if (wb == null)
            {
                MPosDebug.WriteLine("PrinterImplementation getPrintImageData : END");
                return null;
            }

            for (int i = 0; i < 6; i++)
            {
                switch (i)
                {
                    case 0: encoderId = BitmapEncoder.BmpEncoderId; decoderId = BitmapDecoder.BmpDecoderId; break;
                    case 1: encoderId = BitmapEncoder.PngEncoderId; decoderId = BitmapDecoder.PngDecoderId; break;
                    case 2: encoderId = BitmapEncoder.JpegEncoderId; decoderId = BitmapDecoder.JpegDecoderId; break;
                    case 3: encoderId = BitmapEncoder.GifEncoderId; decoderId = BitmapDecoder.GifDecoderId; break;
                    case 4: encoderId = BitmapEncoder.TiffEncoderId; decoderId = BitmapDecoder.TiffDecoderId; break;
                    case 5: encoderId = BitmapEncoder.JpegXREncoderId; decoderId = BitmapDecoder.JpegXRDecoderId; break;
                }

                file = await MPosImagePrintCommand.writeableBitmapToStorageFile(wb, encoderId);

                if (file != null)
                {
                    pixelImgData = await MPosImagePrintCommand.getResizedPixelData(file, decoderId, width, imgSize);
                    file = null;
                    await MPosImagePrintCommand.deleteTempImageFile();
                }

                // cannot print image with RLE in page mode.
                isCompress = _PageModeInOut ? false : isCompress;

                if (pixelImgData != null)
                {
                    if (isCompress)
                        imgPrintCmd = MPosImagePrintCommand.getRleCmd(pixelImgData, imgSize[0], imgSize[1], (uint)brightness, alignment, isDithering);
                    else
                        imgPrintCmd = MPosImagePrintCommand.getGSv0Cmd(pixelImgData, imgSize[0], imgSize[1], (uint)brightness, alignment, isDithering);
                    break;
                }

                pixelImgData = null;
            }

            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : END");
            return imgPrintCmd;
        }
        private async Task<byte[]> getPrintImageData(BitmapDecoder decoder, int width, int alignment, int threshold, bool isDithering = true, int compressType = 1)
        {
            byte[] printImageBytes = null;

            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : START");

            try
            {
                uint resizeWidth = 0;
                uint resizeHeight = 0;

                if (width == (int)MPosImageWidth.MPOS_IMAGE_WIDTH_ASIS)
                {
                    resizeWidth = decoder.PixelWidth;
                    resizeHeight = decoder.PixelHeight;
                }
                else
                {
                    resizeWidth = (uint)width;
                    resizeHeight = ((decoder.PixelHeight * resizeWidth) / decoder.PixelWidth);
                }

                BitmapTransform resizeForm = new BitmapTransform()
                {
                    ScaledWidth = resizeWidth,
                    ScaledHeight = resizeHeight
                };

                PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
                                      BitmapPixelFormat.Bgra8,    // WriteableBitmap uses BGRA format
                                      BitmapAlphaMode.Straight,
                                      resizeForm,
                                      ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation
                                      ColorManagementMode.DoNotColorManage);

                byte[] pixelImg = pixelData.DetachPixelData();

                // cannot print image with RLE in page mode.
                compressType = _PageModeInOut ? 0 : compressType;

                // ESC * 커맨드는 검정색만 인쇄할 수 있도록 구현되어 있음. (2017-01-05)
                //printImageBytes = MPosImagePrintCommand.getEscStarCmd(pixelImg, resizeWidth, resizeHeight, (uint)brightness, alignment, isDithering);
                if ((MPosCompressType)compressType == MPosCompressType.MPOS_IMAGE_COMPRESS_RLE)
                    printImageBytes = MPosImagePrintCommand.getRleCmd(pixelImg, resizeWidth, resizeHeight, (uint)50, alignment, isDithering, threshold);
                else if ((MPosCompressType)compressType == MPosCompressType.MPOS_IMAGE_COMPRESS_LZMA)
                    printImageBytes = MPosImagePrintCommand.getLZMACmd(pixelImg, resizeWidth, resizeHeight, (uint)50, alignment, isDithering, threshold);
                else
                    printImageBytes = MPosImagePrintCommand.getGSv0Cmd(pixelImg, resizeWidth, resizeHeight, (uint)50, alignment, isDithering, threshold);

            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation getPrintImageData : ex = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : END");
            return printImageBytes;
        }
        private async Task<byte[]> getPrintImageData(BitmapDecoder decoder, int width, int alignment, int brightness, bool isDithering = true, bool isCompress = true)
        {
            byte[] printImageBytes = null;

            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : START");

            try
            {
                uint resizeWidth = 0;
                uint resizeHeight = 0;

                if (width == (int)MPosImageWidth.MPOS_IMAGE_WIDTH_ASIS)
                {
                    resizeWidth = decoder.PixelWidth;
                    resizeHeight = decoder.PixelHeight;
                }
                else
                {
                    resizeWidth = (uint)width;
                    resizeHeight = ((decoder.PixelHeight * resizeWidth) / decoder.PixelWidth);
                }

                BitmapTransform resizeForm = new BitmapTransform()
                {
                    ScaledWidth = resizeWidth,
                    ScaledHeight = resizeHeight
                };

                PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
                                      BitmapPixelFormat.Bgra8,    // WriteableBitmap uses BGRA format
                                      BitmapAlphaMode.Straight,
                                      resizeForm,
                                      ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation
                                      ColorManagementMode.DoNotColorManage);

                byte[] pixelImg = pixelData.DetachPixelData();

                // cannot print image with RLE in page mode.
                isCompress = _PageModeInOut ? false : isCompress;

                // ESC * 커맨드는 검정색만 인쇄할 수 있도록 구현되어 있음. (2017-01-05)
                //printImageBytes = MPosImagePrintCommand.getEscStarCmd(pixelImg, resizeWidth, resizeHeight, (uint)brightness, alignment, isDithering);
                if (isCompress)
                    printImageBytes = MPosImagePrintCommand.getRleCmd(pixelImg, resizeWidth, resizeHeight, (uint)brightness, alignment, isDithering);
                else
                    printImageBytes = MPosImagePrintCommand.getGSv0Cmd(pixelImg, resizeWidth, resizeHeight, (uint)brightness, alignment, isDithering);

            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation getPrintImageData : ex = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation getPrintImageData : END");
            return printImageBytes;
        }
    }
    internal partial class MPosDevicePrinterImplementation : MPosControllerPrinter
    {
        //private event EventHandler<Object> ToastCompletedEvent;
        private uint _lastStatus = (uint)MPosPrinterStatus.MPOS_STATUS_NORMAL;
        public event Action<byte[]> dataEvent;
        public event Action<uint> statusUpdateEvent;
        public event Action<int, int> statusUpdateEvent2;
        private uint interpreteStatusData(byte[] statusBufferArray)
        {
            uint status = (uint)MPosPrinterStatus.MPOS_STATUS_NORMAL;

            // FIRST
            //if ((statusBufferArray[0] & 0x08) != 0)
            //status |= (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;        // TPH Overheat
            if ((statusBufferArray[0] & 0x20) != 0)
                status |= (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_COVEROPEN;
            if ((statusBufferArray[0] & 0x04) != 0)
                status |= (uint)MPosPrinterStatus.MPOS_STATUS_DRAWER_HIGH;
            else
                status |= (uint)MPosPrinterStatus.MPOS_STATUS_DRAWER_LOW;

            // SECOND
            //if ((statusBufferArray[1] & 0x20) != 0)
            //status |= (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_OFFLINE;
            if ((statusBufferArray[1] & 0x01) != 0)
                status |= (uint)MPosPrinterStatus.MPOS_STATUS_BATT_LOW;

            if ((statusBufferArray[1] & 0x08) != 0)
            {
                // AUTOCUTTER_ERROR
            }
            // THIRD
            if ((statusBufferArray[2] & 0x0C) != 0)
            {
                // If Cover-Open & Paper-Empty both are detected, ignore Paper-Empty.
                if ((status & (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_COVEROPEN) == 0)
                    status |= (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_PAPEREMPTY;
            }
            else
            {
                //if ((statusBufferArray[2] & 0x01) != 0 && (statusBufferArray[2] & 0x02) != 0)
                if ((statusBufferArray[2] & 0x08) != 0)
                    status |= (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_PAPER_NEAREND;
            }

            MPosDebug.WriteLine("PrinterImplementation interpreteStatusData : status + 0x" + status.ToString("X2"));
            return status;
        }
        private void fireStatusUpdateEvent(uint status, bool forceFireEvent = false)
        {
            MPosDebug.WriteLine("PrinterImplementation: status = " + status);
            _lastStatus = status;
            removeCashDrawerStatus();
            if (!_AsbEnable && !forceFireEvent)
                return;
            if (statusUpdateEvent2 == null)
                MPosDebug.WriteLine("PrinterImplementation statusUpdateEvent is null");
            MPosDebug.WriteLine("PrinterImplementation fireStatusUpdateEvent");
            statusUpdateEvent2?.Invoke(DeviceID, (int)_lastStatus);
        }
        private async void onStatusUpdateEvent(int deviceId, byte[] data)
        {
            MPosDebug.WriteLine("PrinterImplementation onStatusUpdateEvent : deviceId = " + deviceId + ", DeviceID = " + DeviceID);

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

            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            {
                if (deviceId != DeviceID)
                    return;
            }

            var status = interpreteStatusData(data);

            if (_lastStatus != status)
            {
                // Javascript에서 이벤트 발생시키려면, 아래 링크 참조
                // 참조 : https://docs.microsoft.com/en-us/windows/uwp/winrt-components/raising-events-in-windows-runtime-components
                // Fire the event from a background thread to allow this thread to continue
                await Task.Run(() =>
                {
                    this.fireStatusUpdateEvent(status);
                });
            }
        }
        private async void onConnectedEvent()
        {
            MPosDebug.WriteLine("PrinterImplementation onConnectedEvent");
            byte[] commandToWrite = null;
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
                commandToWrite = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            else if (DeviceID < 0xff) //주변기기로 연결된 경우에는 ReadMode Enable 커맨드를 전송한다.
                commandToWrite = new byte[] { 0x08, 0x7e, 0x52, (byte)DeviceID, 0x01 };
            await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false);

            // MAUI
            //_lastStatus &= ~((uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR);
            await asbEnable(_AsbEnable);
        }
        private void onDisconnectedEvent()
        {
            // MAUI
            //_lastStatus |= (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
            MPosDebug.WriteLine("PrinterImplementation onDisconnectedEvent");
            fireStatusUpdateEvent((uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR);
        }
        private void onDataReadEvent(int deviceId, byte[] data)
        {
            MPosDebug.WriteLine("PrinterImplementation onDataReadEvent : deviceId = " + deviceId + ", DeviceID = " + DeviceID);
            if (data == null)
                return;
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            {
                if (deviceId != DeviceID)
                    return;
            }
            _DeviceDataProcessor.writeData(data);
        }
    }
    internal partial class MPosDevicePrinterImplementation : MPosControllerPrinter
    {
        public async Task<int> cutPaper(bool feed, bool fullCut)
        {
            MPosDebug.WriteLine("PrinterImplementation cutPaper : START");
            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation cutPaper : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation cutPaper : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            var commandToWrite = new byte[] { 0x08, 0x56, 0x00, 0x00 };
            // feeds paper to cutting position and cuts it
            if (feed)
            {
                // Full Cut or Partial Cut
                if (fullCut == true) commandToWrite[2] = 0x42;
                //else if (fullCut == false) commandToWrite[2] = 0x41;
                else if (fullCut == false)
                {
                    commandToWrite[0] = 0x1d;
                    commandToWrite[2] = 0x41;
                }
            }
            else
            {
                // Full Cut or Partial Cut
                if (fullCut == true) commandToWrite[2] = 0x01;
                //else if (fullCut == false) commandToWrite[2] = 0x00;
                else if (fullCut == false)
                {
                    commandToWrite[0] = 0x1d;
                    commandToWrite[2] = 0x00;
                }
            }
            var writtenBytes = await _MPosConnection.writeAsync(DeviceID, commandToWrite);
            MPosDebug.WriteLine("PrinterImplementation cutPaper : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation cutPaper : END", _DebugId);
            if (writtenBytes == commandToWrite.Length)
                return (int)MPosResult.MPOS_SUCCESS;
            return (int)MPosResult.MPOS_FAIL;
        }
        public async Task<int> asbEnable(bool enable)
        {
            MPosDebug.WriteLine("PrinterImplementation asbEnable : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation asbEnable : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation asbEnable : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            var commandToWrite = new byte[] { 0x1d, 0x61, 0xff };

            _AsbEnable = enable;

            if (enable)
            {
                //if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) != commandToWrite.Length){
                if (await _MPosConnection.writeAsync(DeviceID, commandToWrite) == 0)
                {
                    _AsbEnable = false;
                    MPosDebug.WriteLine("PrinterImplementation asbEnable : failed to write ASB-Enable data");
                    MPosDebug.WriteLine("PrinterImplementation asbEnable : END");
                    return (int)MPosResult.MPOS_FAIL;
                }
                fireStatusUpdateEvent(_lastStatus, true);
            }

            MPosDebug.WriteLine("PrinterImplementation asbEnable : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<uint> checkBattStatus()
        {
            uint status = (uint)MPosResult.MPOS_FAIL;
            byte[] readData = null;

            MPosDebug.WriteLine("PrinterImplementation checkBattStatus : START");

            if ((_lastStatus & (uint)MPosPrinterStatus.MPOS_STATUS_BATT_LOW) != 0)
            {
                MPosDebug.WriteLine("PrinterImplementation checkBattStatus : MPOS_STATUS_BATT_LOW");
                MPosDebug.WriteLine("PrinterImplementation checkBattStatus : END");
                return _lastStatus;
            }

            var commandToWrite = new byte[] { 0x1d, 0x49, 0x62, 0x1d, 0x49, 0x42 };
            //if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            //{
            //    var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            //    var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
            //    commandToWrite = MPosByteArrayHelper.concat(data1, data2);
            //}

            int indexStart = -1;

            //if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length){
            if (await _MPosConnection.writeAsync(DeviceID, commandToWrite) != 0)
            {
                //MPosDebug.WriteLine("PrinterImplementation checkBattStatus : writeAsync failed.");
                //MPosDebug.WriteLine("PrinterImplementation checkBattStatus : END");
                //return status;

                var readDataArray = await _DeviceDataProcessor.getDataFromMemoryStream(11, null, 2000);
                if (readDataArray == null)
                {
                    MPosDebug.WriteLine("PrinterImplementation checkBattStatus : writeAsync failed.");
                    MPosDebug.WriteLine("PrinterImplementation checkBattStatus : END");
                    return status;
                }

                readData = readDataArray;
                // 0x37, 0x45로 시작하는 인덱스 찾기
                for (int i = 0; i < readDataArray.Length; i++)
                {
                    if (readDataArray[i] == 0x37)
                    {
                        if (i + 1 >= readDataArray.Length)
                        {
                            break;
                        }
                        else
                        {
                            if (readDataArray[i + 1] == 0x45)
                            {
                                indexStart = i;
                                break;
                            }
                        }
                    }
                }
            }

            //readData = await _DeviceDataProcessor.getDataFromMemoryStream(4);
            //if (readData == null)
            //{
            //    MPosDebug.WriteLine("PrinterImplementation checkBattStatus : NO READ DATA");
            //    return status;
            //}

            MPosDebug.WriteLine("PrinterImplementation checkBattStatus : readData.Length = " + readData.Length);
            if (readData.Length < 4)
            {
                MPosDebug.WriteLine("PrinterImplementation checkBattStatus : Less than 4 bytes");
                return status;
            }

            if (indexStart == -1)
            {
                MPosDebug.WriteLine("0x37, 0x45 not found. indexStart : " + indexStart);
                return (uint)MPosPrinterStatus.MPOS_STATUS_NORMAL;  //배터리가 없는 모델이므로 MPOS_STATUS_NORMAL 리턴
            }

            if (readData[indexStart] == 0x37 &&
                readData[indexStart + 1] == 0x45 &&
                readData[indexStart + 3] == 0x00)
            {
                MPosDebug.WriteLine("PrinterImplementation checkBattStatus : readData = " + readData[indexStart + 2]);
                switch (readData[indexStart + 2])
                {
                    case 0x30:
                        // BATTERY LEVEL : FULL
                        status = 0;
                        break;
                    case 0x31:
                        status = (uint)MPosPrinterStatus.MPOS_STATUS_BATT_HIGH;
                        fireStatusUpdateEvent(status);
                        break;
                    case 0x32:
                        status = (uint)MPosPrinterStatus.MPOS_STATUS_BATT_MID;
                        fireStatusUpdateEvent(status);
                        break;
                    case 0x33:
                        status = (uint)MPosPrinterStatus.MPOS_STATUS_BATT_LOW;
                        fireStatusUpdateEvent(status);
                        break;
                }
            }

            MPosDebug.WriteLine("PrinterImplementation checkBattStatus : END");
            return status;
        }
        private void removeCashDrawerStatus()
        {
            if (!string.IsNullOrWhiteSpace(_ModelName) && _ModelName.Contains("SPP"))
            {
                _lastStatus = (_lastStatus & ~(uint)MPosPrinterStatus.MPOS_STATUS_DRAWER_HIGH);
                _lastStatus = (_lastStatus & ~(uint)MPosPrinterStatus.MPOS_STATUS_DRAWER_LOW);
            }
        }
        public async Task<uint> checkPrinterStatus()
        {
            MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : START");

            if (_AsbEnable)
            {
                fireStatusUpdateEvent(_lastStatus);
                MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : _AsbEnable");
                //MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : END");
                //return _lastStatus;
            }

            MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : _lastStatus = 0x" + _lastStatus.ToString("X2"));
            MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : END");

            statusUpdateEvent?.Invoke(_lastStatus);
            return _lastStatus;
            /*
            uint status = (uint)MPosPrinterStatus.MPOS_STATUS_NORMAL;

            if (IsOpen == false || _MPosConnection == null)
            {
                status = (uint)MPosResult.MPOS_FAIL_NO_OPEN;
                MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : END");
                return status;
            }

            var commandToWrite = new byte[] { 0x10, 0x04, 0x04 };
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            {
                var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
                var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
                commandToWrite = MPosByteArrayHelper.concat(data1, data2);
            }

            // DLE EOT 커맨드로 한번에 모든 상태값을 읽으려고 하면, 응답 값의 순서가 보장되지 않는다.
            if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length)
            {
                var dleEotResponse = await _DeviceDataProcessor.getDataFromMemoryStream(1);
                if (dleEotResponse != null)
                {
                    MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : bytes read = " + dleEotResponse.Length + ", dleEotResponse = 0x" + dleEotResponse[0].ToString("X2"));
                    if ((dleEotResponse[0] & 0x04) != 0 && (dleEotResponse[0] & 0x08) != 0){
                        MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : PAPER_NEAR_END");
                        status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_PAPER_NEAREND;
                    }
                    if ((dleEotResponse[0] & 0x20) != 0 && (dleEotResponse[0] & 0x40) != 0){
                        MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : PAPER_EMPTY");
                        status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_PAPEREMPTY;
                    }
                }
                else{
                    status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
                }
            }else{
                status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
            }

            if (status != (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR)
            {
                commandToWrite = new byte[] { 0x10, 0x04, 0x02 };
                if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE){
                    var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
                    var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
                    commandToWrite = MPosByteArrayHelper.concat(data1, data2);
                }

                if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length)
                {
                    var dleEotResponse = await _DeviceDataProcessor.getDataFromMemoryStream(1);
                    if (dleEotResponse != null){
                        MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : dleEotResponse = 0x" + dleEotResponse[0].ToString("X2"));
                        if ((dleEotResponse[0] & 0x04) != 0){
                            MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : COVER_OPEN");
                            status = status &( ~(uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_PAPEREMPTY);
                            status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_COVEROPEN;
                        }
                    }
                    else{
                        status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
                    }
                }else{
                    status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
                }
            }

            if (_ModelName.Contains("SRP") || _ModelName.Contains("STP") || _ModelName.Contains("BGT"))
            {
                if (status != (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR)
                {
                    commandToWrite = new byte[] { 0x10, 0x04, 0x01 };
                    if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE){
                        var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
                        var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
                        commandToWrite = MPosByteArrayHelper.concat(data1, data2);
                    }

                    if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length)
                    {
                        var dleEotResponse = await _DeviceDataProcessor.getDataFromMemoryStream(1);
                        if (dleEotResponse != null){
                            MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : dleEotResponse = 0x" + dleEotResponse[0].ToString("X2"));
                            status = status | (uint)MPosPrinterStatus.MPOS_STATUS_DRAWER_LOW;
                            if ((dleEotResponse[0] & 0x04) != 0){
                                MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : DRAWER HIGH");
                                status = status | (uint)MPosPrinterStatus.MPOS_STATUS_DRAWER_HIGH;
                            }
                        }else{
                            status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
                        }
                    }else{
                        status = status | (uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR;
                    }
                }
            }

            fireStatusUpdateEvent(status);

            MPosDebug.WriteLine("PrinterImplementation checkPrinterStatus : END");
            return status;
            */
        }

        public async Task<int> openDrawer(int pinNumber, int onTimePulse, int offTimePulse)
        {
            MPosDebug.WriteLine("PrinterImplementation openDrawer : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation openDrawer : MPosResult.MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation openDrawer : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            // 데이터 파싱 문제로 인하여, 모델명 + 제조사명까지 같이 요청.
            var commandToWrite = new byte[] { 0x1b, 0x70, (byte)pinNumber, (byte)onTimePulse, (byte)offTimePulse };
            //if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            //{
            //    var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            //    var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
            //    commandToWrite = MPosByteArrayHelper.concat(data1, data2);
            //}

            var writtenBytes = await _MPosConnection.writeAsync(DeviceID, commandToWrite);
            MPosDebug.WriteLine("PrinterImplementation openDrawer : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation openDrawer : END", _DebugId);
            if (writtenBytes == commandToWrite.Length)
                return (int)MPosResult.MPOS_SUCCESS;
            return (int)MPosResult.MPOS_FAIL;
        }

        public async Task<int> getModelName(Action<string> modelDelegate)
        {
            MPosDebug.WriteLine("PrinterImplementation getModelName : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation getModelName : MPosResult.MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation getModelName : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            // 데이터 파싱 문제로 인하여, 모델명 + 제조사명까지 같이 요청.
            //var commandToWrite = new byte[] { 0x1d, 0x49, 0x43, 0x1d, 0x49, 0x42 };
            // ASB Enable, Model Name, Manufacturer name
            var commandToWrite = new byte[] { 0x1d, 0x61, 0xff, 0x1d, 0x49, 0x43, 0x1d, 0x49, 0x42 };
            //if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE){
            //    var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            //    var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
            //    commandToWrite = MPosByteArrayHelper.concat(data1, data2);
            //}

            //if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length){
            if (await _MPosConnection.writeAsync(DeviceID, commandToWrite) != 0)
            {
                //var readDataArray = await _DeviceDataProcessor.getDataFromMemoryStream(new byte[] { 0x00 }, 2000);
                //if (readDataArray != null)
                //    _ModelName = Encoding.ASCII.GetString(readDataArray).Trim();

                try
                {
                    var readDataArray = await _DeviceDataProcessor.getDataFromMemoryStream(new byte[] { 0x00 });
                    if (readDataArray != null)
                    {
                        _ModelName = Encoding.ASCII.GetString(readDataArray).Trim();
                        MPosDebug.WriteLine("PrinterImplementation getModelName : _ModelName = " + _ModelName, _DebugId);
                    }
                }
                catch (Exception ex)
                {
                    MPosDebug.WriteLine("PrinterImplementation getModelName : ex = " + ex.Message);
                    return (int)MPosResult.MPOS_FAIL;
                }
            }

            MPosDebug.WriteLine("PrinterImplementation getModelName : _ModelName = " + _ModelName, _DebugId);
            MPosDebug.WriteLine("PrinterImplementation getModelName : END");
            if (string.IsNullOrEmpty(_ModelName))
                return (int)MPosResult.MPOS_FAIL;
            modelDelegate?.Invoke(_ModelName);
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> getFirmwareVersion(Action<string> versionDelegate)
        {
            MPosDebug.WriteLine("PrinterImplementation getFirmwareVersion : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation getFirmwareVersion : MPosResult.MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation getFirmwareVersion : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            // 데이터 파싱 문제로 인하여, 모델명 + 제조사명까지 같이 요청.
            var commandToWrite = new byte[] { 0x1d, 0x49, 0x41, 0x1d, 0x49, 0x42 };
            //if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE){
            //    var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            //    var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
            //    commandToWrite = MPosByteArrayHelper.concat(data1, data2);
            //}

            //if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length){
            if (await _MPosConnection.writeAsync(DeviceID, commandToWrite) != 0)
            {
                var readDataArray = await _DeviceDataProcessor.getDataFromMemoryStream(new byte[] { 0x00 });
                if (readDataArray != null)
                {
                    _FirmwareVersion = Encoding.ASCII.GetString(readDataArray).Trim();
                    //_FirmwareVersion = Encoding.ASCII.GetString(readDataArray.Take(Array.FindIndex(readDataArray, d => d == 0x00)).ToArray());
                }
            }

            MPosDebug.WriteLine("PrinterImplementation getFirmwareVersion : _FirmwareVersion = " + _FirmwareVersion, _DebugId);
            MPosDebug.WriteLine("PrinterImplementation getFirmwareVersion : END");
            if (string.IsNullOrEmpty(_FirmwareVersion))
                return (int)MPosResult.MPOS_FAIL;
            versionDelegate?.Invoke(_FirmwareVersion);
            return (int)MPosResult.MPOS_SUCCESS;
        }

        public async Task<int> getPrintBufferSize(Action<int> bufferSizeDelegate)
        {
            return (int)MPosResult.MPOS_FAIL_NOT_SUPPORT;
        }
        public async Task<int> clearPrintBuffer(Action<int> clearBufferResultDelegate)
        {
            return (int)MPosResult.MPOS_FAIL_NOT_SUPPORT;
        }

        public async Task<int> getCodePageName(Action<string> cpNameDelegate)
        {
            MPosDebug.WriteLine("PrinterImplementation getCodePageName : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation getCodePageName : MPosResult.MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation getCodePageName : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            // 데이터 파싱 문제로 인하여, 모델명 + 제조사명까지 같이 요청.
            var commandToWrite = new byte[] { 0x1d, 0x49, 0x45, 0x1d, 0x49, 0x42 };
            //if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            //{
            //    var data1 = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            //    var data2 = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
            //    commandToWrite = MPosByteArrayHelper.concat(data1, data2);
            //}

            //if (await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false) == commandToWrite.Length)
            if (await _MPosConnection.writeAsync(DeviceID, commandToWrite) != 0)
            {
                var readDataArray = await _DeviceDataProcessor.getDataFromMemoryStream(new byte[] { 0x00 });
                if (readDataArray != null)
                    _CodePageName = Encoding.ASCII.GetString(readDataArray).Trim();
            }

            MPosDebug.WriteLine("PrinterImplementation getCodePageName : _CodePageName = " + _CodePageName, _DebugId);
            MPosDebug.WriteLine("PrinterImplementation getCodePageName : END");
            if (string.IsNullOrEmpty(_CodePageName))
                return (int)MPosResult.MPOS_FAIL;
            cpNameDelegate?.Invoke(_CodePageName);
            return (int)MPosResult.MPOS_SUCCESS;
        }
    }
    internal partial class MPosDevicePrinterImplementation : MPosControllerPrinter
    {
        public async Task<int> printCompositeSymbology(string data1d, int symbol1d, string data2d, int symbol2d, int moduleSize, int alignment)
        {
            /*
             * 모바일 프린터 경우, GS1 Databar에 해당하는 것으로, 별도의 커맨드가 있음. 
             * 이 메소드는 POS 프린터 전용
             */
            MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : START");
            MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : data1d = " + data1d + ", symbol1d = " + symbol1d);
            MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : data2d = " + data2d + ", symbol2d = " + symbol2d);
            MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : moduleSize = " + moduleSize + ", alignment = " + alignment);

            byte[] func480 = { 0x1d, 0x28, 0x6B, 0x00, 0x00, 0x34, 0x50, 0x30, 0x30, 0x00 };   // <Function 480>	    // COMPOSITE : Store the data in the symbol storage area
            byte[] func481 = { 0x1d, 0x28, 0x6B, 0x03, 0x00, 0x34, 0x51, 0x30 };               // <Function 481>	    // COMPOSITE : Print the data in the symbol storage area.
            byte[] attributeCmd = { 0x1d, 0x28, 0x6B, 0x03, 0x00, 0x34, 0x43, 0x02,			        // <Function 467>	    // COMPOSITE : Select the size of module.
							        0x1b, 0x61, 0x00 };                                             // Alignment

            // moduleSize: Function 467
            attributeCmd[7] = (byte)moduleSize;
            if (attributeCmd[7] < 1) attributeCmd[7] = 1;
            else if (attributeCmd[7] > 8) attributeCmd[7] = 8;

            bool isMember = Enum.GetValues(typeof(MPosComposite1dSymbol)).Cast<int>().Any(x => x == (int)symbol1d);
            if (isMember != true)
            {
                MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }
            isMember = Enum.GetValues(typeof(MPosComposite2dSymbol)).Cast<int>().Any(x => x == (int)symbol2d);
            if (isMember != true)
            {
                MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

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

            int writtenBytes = 0;

            try
            {
                int index = 0;
                byte[] code1dData = Encoding.UTF8.GetBytes(data1d);
                byte[] code2dData = Encoding.UTF8.GetBytes(data2d);
                byte[] func480_481 = new byte[(func480.Length * 2) + code1dData.Length + code2dData.Length + func481.Length];
                // 480 for 1d
                func480[3] = (byte)((code1dData.Length + 5) % 256);
                func480[4] = (byte)((code1dData.Length + 5) / 256);
                func480[8] = 48;
                func480[9] = (byte)symbol1d;
                System.Buffer.BlockCopy(func480, 0, func480_481, index, func480.Length);
                index += func480.Length;
                System.Buffer.BlockCopy(code1dData, 0, func480_481, index, code1dData.Length);
                index += code1dData.Length;
                // 480 for 2d
                func480[3] = (byte)((code2dData.Length + 5) % 256);
                func480[4] = (byte)((code2dData.Length + 5) / 256);
                func480[8] = 49;
                func480[9] = (byte)symbol2d;
                System.Buffer.BlockCopy(func480, 0, func480_481, index, func480.Length);
                index += func480.Length;
                System.Buffer.BlockCopy(code2dData, 0, func480_481, index, code2dData.Length);
                index += code2dData.Length;
                // 481
                System.Buffer.BlockCopy(func481, 0, func480_481, index, func481.Length);
                // Attribute
                byte[] func480_481WithAtt = new byte[func480_481.Length + attributeCmd.Length];
                System.Buffer.BlockCopy(attributeCmd, 0, func480_481WithAtt, 0, attributeCmd.Length);
                System.Buffer.BlockCopy(func480_481, 0, func480_481WithAtt, attributeCmd.Length, func480_481.Length);

                writtenBytes = await _MPosConnection.writeAsync(DeviceID, func480_481WithAtt);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : Exception = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation printCompositeSymbology : END");
            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printDataMatrix(string data, int alignment, int moduleSize)
        {
            MPosDebug.WriteLine("PrinterImplementation printDataMatrix : START");
            MPosDebug.WriteLine("PrinterImplementation printDataMatrix : data = " + data + ", moduleSize = " + moduleSize.ToString());

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printDataMatrix : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printDataMatrix : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            byte[] func380 = { 0x1D, 0x28, 0x6B, 0x00, 0x00, 0x33, 0x50, 0x30 };       // <Function 380, 580>	// Store the data in the symbol storage area
            byte[] func381 = { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x33, 0x51, 0x30 };       // <Function 381, 581>	// Print the data in the symbol storage area
            byte[] attributeCmd = { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x33, 0x43, 0x03,         // <Function 367, 567>	// Select the size of module.
                                    0x1b, 0x61, 0x00};

            MPosDebug.WriteLine("PrinterImplementation printDataMatrix : _ModelName = " + _ModelName);
            if (_ModelName.Contains("SRP") || _ModelName.Contains("STP") || _ModelName.Contains("BGT"))
            {
                // POS 프린터 경우, DATA MATRIX 커맨드가 변경되었음. (구형 모델 제외)
                attributeCmd[5] = func380[5] = func381[5] = 0x3d;
            }
            // Module Size
            attributeCmd[7] = (byte)moduleSize;
            if (attributeCmd[7] < 2) attributeCmd[7] = 0x02;
            else if (attributeCmd[7] > 3) attributeCmd[7] = 0x03;

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

            //<Function 380/580>	// Store the data in the symbol storage area
            //<Function 381/581>	// Print the data in the symbol storage area
            byte[] codeData = Encoding.UTF8.GetBytes(data);
            func380[3] = (byte)((codeData.Length + 3) % 256);
            func380[4] = (byte)((codeData.Length + 3) / 256);

            int writtenBytes = 0;

            try
            {
                byte[] func380_381 = new byte[func380.Length + codeData.Length + func381.Length];
                System.Buffer.BlockCopy(func380, 0, func380_381, 0, func380.Length);
                System.Buffer.BlockCopy(codeData, 0, func380_381, func380.Length, codeData.Length);
                System.Buffer.BlockCopy(func381, 0, func380_381, func380.Length + codeData.Length, func381.Length);

                byte[] func380_381WithAtt = new byte[func380_381.Length + attributeCmd.Length];
                System.Buffer.BlockCopy(attributeCmd, 0, func380_381WithAtt, 0, attributeCmd.Length);
                System.Buffer.BlockCopy(func380_381, 0, func380_381WithAtt, attributeCmd.Length, func380_381.Length);

                writtenBytes = await _MPosConnection.writeAsync(DeviceID, func380_381WithAtt);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printDataMatrix : Exception = " + ex.ToString());
            }

            MPosDebug.WriteLine("PrinterImplementation printDataMatrix : written bytes = " + writtenBytes.ToString());
            MPosDebug.WriteLine("PrinterImplementation printDataMatrix : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printGS1Databar(string data, int symbology, int alignment, int moduleSize)
        {
            // 이 메소드는 POS 프린터 전용
            MPosDebug.WriteLine("PrinterImplementation printGS1Databar : START");
            MPosDebug.WriteLine("PrinterImplementation printGS1Databar : symbology = " + symbology + ", alignment = " + alignment);
            MPosDebug.WriteLine("PrinterImplementation printGS1Databar : moduleSize = " + moduleSize);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printGS1Databar : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printGS1Databar : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            // GS1_Databar
            byte[] func380 = { 0x1d, 0x28, 0x6B, 0x00, 0x00, 0x33, 0x50, 0x30, 0x48 };  // <Function 380>	// GS1 DATABAR: Store the data in the symbol storage area
            byte[] func381 = { 0x1d, 0x28, 0x6B, 0x03, 0x00, 0x33, 0x51, 0x30 };        // <Function 381>	// GS1 DATABAR: Print the data in the symbol storage area.
            byte[] attributeCmd = { 0x1d, 0x28, 0x6B, 0x03, 0x00, 0x33, 0x43, 0x02,			 // <Function 367>	// GS1 DATABAR: Select the size of module.
							        0x1b, 0x61, 0x00 };                                      // Alignment
            // moduleSize
            attributeCmd[7] = (byte)moduleSize;
            if (attributeCmd[7] < 1) attributeCmd[7] = 1;
            else if (attributeCmd[7] > 8) attributeCmd[7] = 8;

            // symbology : GS1 DataBar Stacked / GS1 DataBar Stacked Omnidirectional
            switch ((MPosGS1DatabarSymbol)symbology)
            {
                case MPosGS1DatabarSymbol.MPOS_BARCODE_GS1_RSS14STACKEDOMNIDIRECTIONAL:
                    func380[8] = 0x48;
                    break;
                case MPosGS1DatabarSymbol.MPOS_BARCODE_GS1_RSS14STACKED:
                    func380[8] = 0x49;
                    break;
                default:
                    MPosDebug.WriteLine("PrinterImplementation printGS1Databar : MPOS_FAIL_INVALID_PARAMETER");
                    MPosDebug.WriteLine("PrinterImplementation printGS1Databar : END");
                    return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

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

            //<Function 180>	// QR CODE: Store the data in the symbol storage area
            //<Function 181>	// QR CODE: Print the data in the symbol storage area
            byte[] codeData = Encoding.UTF8.GetBytes(data);
            func380[3] = (byte)((codeData.Length + 4) % 256);
            func380[4] = (byte)((codeData.Length + 4) / 256);

            int writtenBytes = 0;

            try
            {
                byte[] func380_381 = new byte[func380.Length + codeData.Length + func381.Length];
                System.Buffer.BlockCopy(func380, 0, func380_381, 0, func380.Length);
                System.Buffer.BlockCopy(codeData, 0, func380_381, func380.Length, codeData.Length);
                System.Buffer.BlockCopy(func381, 0, func380_381, func380.Length + codeData.Length, func381.Length);
                byte[] func380_381WithAtt = new byte[func380_381.Length + attributeCmd.Length];
                System.Buffer.BlockCopy(attributeCmd, 0, func380_381WithAtt, 0, attributeCmd.Length);
                System.Buffer.BlockCopy(func380_381, 0, func380_381WithAtt, attributeCmd.Length, func380_381.Length);
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, func380_381WithAtt);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printGS1Databar : Exception = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation printGS1Databar : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation printGS1Databar : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printQRCode(string data, int model, int alignment, int moduleSize, int eccLevel)
        {
            MPosDebug.WriteLine("PrinterImplementation printQRCode : START");
            MPosDebug.WriteLine("PrinterImplementation printQRCode : model = " + model + ", alignment = " + alignment);
            MPosDebug.WriteLine("PrinterImplementation printQRCode : moduleSize = " + moduleSize + ", eccLevel = " + eccLevel);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printQRCode : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printQRCode : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            byte[] func180 = { 0x1D, 0x28, 0x6B, 0x00, 0x00, 0x31, 0x50, 0x30 };       //<Function 180>	// QR CODE: Store the data in the symbol storage area
            byte[] func181 = { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30 };       //<Function 181>	// QR CODE: Print the data in the symbol storage area
            byte[] attributeCmd = { 0x1D, 0x28, 0x6B, 0x04, 0x00, 0x31, 0x41, 0x31, 0x00,   //<Function 165>	// QR CODE Model
                                    0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x43, 0x03,			//<Function 167>	// QR CODE Size
							        0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, 0x30,         //<Function 169>	// QR CODE Error Correction Level
                                    0x1b, 0x61, 0x00 };		                                // Alignment

            switch ((MPos2dCodeType)model)
            {
                case MPos2dCodeType.MPOS_BARCODE_QRCODE_MODEL1: attributeCmd[7] = 0x31; break;
                case MPos2dCodeType.MPOS_BARCODE_QRCODE_MODEL2: attributeCmd[7] = 0x32; break;
                default:
                    MPosDebug.WriteLine("PrinterImplementation printQRCode : MPOS_FAIL_INVALID_PARAMETER");
                    MPosDebug.WriteLine("PrinterImplementation printQRCode : END");
                    return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            // QR Code Size
            attributeCmd[16] = (byte)moduleSize;
            if (attributeCmd[16] < 1) attributeCmd[16] = 0x01;
            else if (attributeCmd[16] > 8) attributeCmd[16] = 0x08;

            // Error correction level of QR Code.  (0x30 ~ 0x33)
            switch ((MPosQRCodeECCLevel)eccLevel)
            {
                case MPosQRCodeECCLevel.MPOS_QRCODE_ECC_LEVEL_L: attributeCmd[24] = 0x30; break;
                case MPosQRCodeECCLevel.MPOS_QRCODE_ECC_LEVEL_M: attributeCmd[24] = 0x31; break;
                case MPosQRCodeECCLevel.MPOS_QRCODE_ECC_LEVEL_Q: attributeCmd[24] = 0x32; break;
                case MPosQRCodeECCLevel.MPOS_QRCODE_ECC_LEVEL_H: attributeCmd[24] = 0x33; break;
            }

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

            //<Function 180>	// QR CODE: Store the data in the symbol storage area
            //<Function 181>	// QR CODE: Print the data in the symbol storage area
            byte[] codeData = Encoding.UTF8.GetBytes(data);
            func180[3] = (byte)((codeData.Length + 3) % 256);
            func180[4] = (byte)((codeData.Length + 3) / 256);

            int writtenBytes = 0;

            try
            {
                byte[] func180_181 = new byte[func180.Length + codeData.Length + func181.Length];
                System.Buffer.BlockCopy(func180, 0, func180_181, 0, func180.Length);
                System.Buffer.BlockCopy(codeData, 0, func180_181, func180.Length, codeData.Length);
                System.Buffer.BlockCopy(func181, 0, func180_181, func180.Length + codeData.Length, func181.Length);

                byte[] func180_181WithAtt = new byte[func180_181.Length + attributeCmd.Length];
                System.Buffer.BlockCopy(attributeCmd, 0, func180_181WithAtt, 0, attributeCmd.Length);
                System.Buffer.BlockCopy(func180_181, 0, func180_181WithAtt, attributeCmd.Length, func180_181.Length);

                writtenBytes = await _MPosConnection.writeAsync(DeviceID, func180_181WithAtt);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printQRCode : Exception = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation printQRCode : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation printQRCode : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printPDF417(string data, int symbology, int alignment, int columnNumber, int rowNumber, int moduleWidth, int moduleHeight, int eccLevel)
        {
            MPosDebug.WriteLine("PrinterImplementation printPDF417 : START");
            MPosDebug.WriteLine("PrinterImplementation printPDF417 : symbology = " + symbology.ToString() + ", alignment = " + eccLevel.ToString());
            MPosDebug.WriteLine("PrinterImplementation printPDF417 : columnNumber = " + columnNumber.ToString() + ", rowNumber = " + rowNumber.ToString());
            MPosDebug.WriteLine("PrinterImplementation printPDF417 : moduleWidth = " + moduleWidth.ToString() + ", moduleHeight = " + moduleHeight.ToString());
            MPosDebug.WriteLine("PrinterImplementation printPDF417 : eccLevel = " + eccLevel.ToString());

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printPDF417 : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printPDF417 : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            byte[] func80 = { 0x1D, 0x28, 0x6B, 0x00, 0x00, 0x30, 0x50, 0x30 };             //<Function 080>	// PDF417: Store the received data in the symbol storage area
            byte[] func81 = { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x30, 0x51, 0x30 };             //<Function 081>	// PDF417: Print the symbol data in the symbol storage area
            byte[] attributeCmd = { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x30, 0x41, 0x00,			//<Function 065>	// PDF417: number of columns
								    0x1D, 0x28, 0x6B, 0x03, 0x00, 0x30, 0x42, 0x00,			//<Function 066>	// PDF417: number of rows
								    0x1D, 0x28, 0x6B, 0x03, 0x00, 0x30, 0x43, 0x03,			//<Function 067>	// PDF417: width of module
								    0x1D, 0x28, 0x6B, 0x03, 0x00, 0x30, 0x44, 0x03,			//<Function 068>	// PDF417: module height
								    0x1D, 0x28, 0x6B, 0x04, 0x00, 0x30, 0x45, 0x30, 0x30,	//<Function 069>	// PDF417: error correction level
								    0x1D, 0x28, 0x6B, 0x03, 0x00, 0x30, 0x46, 0x00, 		//<Function 070>	// PDF417: standard / simplified
                                    0x1b, 0x61, 0x00 };                                     // Alignment

            switch ((MPos2dCodeType)symbology)
            {
                case MPos2dCodeType.MPOS_BARCODE_PDF417: attributeCmd[48] = 0x00; break;
                case MPos2dCodeType.MPOS_BARCODE_PDF417_SIMPLIFIED: attributeCmd[48] = 0x01; break;
                default:
                    MPosDebug.WriteLine("PrinterImplementation printPDF417 : MPOS_FAIL_INVALID_PARAMETER");
                    MPosDebug.WriteLine("PrinterImplementation printPDF417 : END");
                    return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            attributeCmd[7] = (byte)columnNumber;
            if (attributeCmd[7] < 0) attributeCmd[7] = 0;
            else if (attributeCmd[7] > 30) attributeCmd[7] = 30;

            attributeCmd[15] = (byte)rowNumber;
            if (attributeCmd[15] < 3) attributeCmd[15] = 0;
            else if (attributeCmd[15] > 90) attributeCmd[15] = 90;

            attributeCmd[23] = (byte)moduleWidth;
            if (attributeCmd[23] < 1) attributeCmd[23] = 1;
            else if (attributeCmd[23] > 4) attributeCmd[23] = 4;

            attributeCmd[31] = (byte)moduleHeight;
            if (attributeCmd[31] < 2) attributeCmd[31] = 2;
            else if (attributeCmd[31] > 8) attributeCmd[31] = 8;

            switch ((MPosPDF417ECCLevel)eccLevel)
            {
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_0: attributeCmd[40] = 0x30; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_1: attributeCmd[40] = 0x31; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_2: attributeCmd[40] = 0x32; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_3: attributeCmd[40] = 0x33; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_4: attributeCmd[40] = 0x34; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_5: attributeCmd[40] = 0x35; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_6: attributeCmd[40] = 0x36; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_7: attributeCmd[40] = 0x37; break;
                case MPosPDF417ECCLevel.MPOS_PDF417_ECC_LEVEL_8: attributeCmd[40] = 0x38; break;
            }

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

            //<Function 80>	// PDF417: Store the data in the symbol storage area
            //<Function 81>	// PDE417: Print the data in the symbol storage area
            byte[] codeData = Encoding.UTF8.GetBytes(data);
            func80[3] = (byte)((codeData.Length + 3) % 256);
            func80[4] = (byte)((codeData.Length + 3) / 256);

            int writtenBytes = 0;

            try
            {
                byte[] func80_81 = new byte[func80.Length + codeData.Length + func81.Length];
                System.Buffer.BlockCopy(func80, 0, func80_81, 0, func80.Length);
                System.Buffer.BlockCopy(codeData, 0, func80_81, func80.Length, codeData.Length);
                System.Buffer.BlockCopy(func81, 0, func80_81, func80.Length + codeData.Length, func81.Length);

                byte[] func80_81WithAtt = new byte[func80_81.Length + attributeCmd.Length];
                System.Buffer.BlockCopy(attributeCmd, 0, func80_81WithAtt, 0, attributeCmd.Length);
                System.Buffer.BlockCopy(func80_81, 0, func80_81WithAtt, attributeCmd.Length, func80_81.Length);

                writtenBytes = await _MPosConnection.writeAsync(DeviceID, func80_81WithAtt);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printPDF417 : Exception = " + ex.ToString());
            }

            MPosDebug.WriteLine("PrinterImplementation printPDF417 : written bytes = " + writtenBytes.ToString());
            MPosDebug.WriteLine("PrinterImplementation printPDF417 : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        // IMPosDevicePrinter Methods
        public async Task<int> print1DBarcode(string data, int symbology, int width, int height, int alignment, int textPosition)
        {
            MPosDebug.WriteLine("PrinterImplementation print1DBarcode : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation print1DBarcode : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation print1DBarcode : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            MPosDebug.WriteLine("PrinterImplementation print1DBarcode : data = " + data + " symbology = " + symbology);
            MPosDebug.WriteLine("PrinterImplementation print1DBarcode : width = " + width + " height = " + height);
            MPosDebug.WriteLine("PrinterImplementation print1DBarcode : alignment = " + alignment + " textPosition = " + textPosition);

            switch ((MPos1dCodeType)symbology)
            {
                case MPos1dCodeType.MPOS_BARCODE_UPCA:
                case MPos1dCodeType.MPOS_BARCODE_UPCE:
                case MPos1dCodeType.MPOS_BARCODE_JAN13:
                case MPos1dCodeType.MPOS_BARCODE_JAN8:
                case MPos1dCodeType.MPOS_BARCODE_CODE39:
                case MPos1dCodeType.MPOS_BARCODE_ITF:
                case MPos1dCodeType.MPOS_BARCODE_CODABAR:
                case MPos1dCodeType.MPOS_BARCODE_CODE93:
                case MPos1dCodeType.MPOS_BARCODE_CODE128:
                    //case MPos1dCodeType.MPOS_BARCODE_GS1_128:                 
                    //case MPos1dCodeType.MPOS_BARCODE_GS1DATABAR_OMNIDIRECTION:
                    //case MPos1dCodeType.MPOS_BARCODE_GS1DATABAR_TRUNCATED:
                    //case MPos1dCodeType.MPOS_BARCODE_GS1DATABAR_LIMITED:
                    break;
                default:
                    MPosDebug.WriteLine("PrinterImplementation print1DBarcode : END");
                    return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            int writtenBytes = 0;
            byte[] barcodePrintCmd = MPosEscPosCmdBuilder.getPrint1DBarcodeCmd(data, symbology, width, height, alignment, textPosition);

            try
            {
                if (barcodePrintCmd != null)
                    writtenBytes = await _MPosConnection.writeAsync(DeviceID, barcodePrintCmd);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation print1DBarcode : Exception = " + ex.ToString());
            }

            MPosDebug.WriteLine("PrinterImplementation print1DBarcode : written bytes = " + writtenBytes.ToString());
            MPosDebug.WriteLine("PrinterImplementation print1DBarcode : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }

        public async Task<int> printText(string data, MPosFontAttribute attribute)
        {
            MPosDebug.WriteLine("PrinterImplementation printText : START", _DebugId);
            MPosDebug.WriteLine("PrinterImplementation printText : data = " + data, _DebugId);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printText : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation printText : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            int writtenBytes = 0;

            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)
                        {
                            MPosDebug.WriteLine("PrinterImplementation printText : part = " + part + " , length = " + part.Length, _DebugId);
                            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;
                            }
                        }
                        writtenBytes = await _MPosConnection.writeAsync(DeviceID, totalData);
                    }
                    else
                    {
                        textData = getEncodedTextData(data, null);
                        writtenBytes = await _MPosConnection.writeAsync(DeviceID, textData);
                    }
                }
                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);
                    writtenBytes = await _MPosConnection.writeAsync(DeviceID, dataWithAttributeCmd);
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printText : Exception = " + ex.ToString(), _DebugId);
            }

            MPosDebug.WriteLine("PrinterImplementation printText : written bytes = " + writtenBytes.ToString(), _DebugId);
            MPosDebug.WriteLine("PrinterImplementation printText : END", _DebugId);

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printText(byte[] data, MPosFontAttribute attribute)
        {
            MPosDebug.WriteLine("PrinterImplementation printText : START", _DebugId);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printText : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation printText : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            int writtenBytes = 0;

            try
            {
                var byteArray = MPosEscPosCmdBuilder.getPrintTextCmd(data, attribute);
                if (byteArray != null)
                    writtenBytes = await _MPosConnection.writeAsync(DeviceID, data);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printText : Exception = " + ex.ToString(), _DebugId);
            }

            MPosDebug.WriteLine("PrinterImplementation printText : written bytes = " + writtenBytes.ToString(), _DebugId);
            MPosDebug.WriteLine("PrinterImplementation printText : END", _DebugId);

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printPDFfile(string filePath,
                                    int startPage,
                                    int endPage,
                                    int width,
                                    int alignment,
                                    int brightness,
                                    bool isDithering,
                                    bool isCompress)
        {
            int result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("PrinterImplementation printPDFfile : START");
            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printPDFfile : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printPDFfile : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            MPosDebug.WriteLine("PrinterImplementation printPDFfile : filePath = " + filePath);
            MPosDebug.WriteLine("PrinterImplementation printPDFfile : width = " + width + ", alignment = " + alignment + ", brightness = " + brightness);
            MPosDebug.WriteLine("PrinterImplementation printPDFfile : isDithering = " + isDithering + ", isCompress = " + isCompress);

            try
            {
                StorageFile pdfFile = await StorageFile.GetFileFromPathAsync(filePath);
                PdfDocument pdfDoc = await PdfDocument.LoadFromFileAsync(pdfFile);

                if (startPage <= 0) startPage = 0;                            // set it with the first index
                if (startPage >= pdfDoc.PageCount) startPage = ((int)pdfDoc.PageCount) - 1;    // set it with the last index
                if (endPage < 0) endPage = ((int)pdfDoc.PageCount) - 1;    // for printing all the pages
                if (endPage >= pdfDoc.PageCount) endPage = ((int)pdfDoc.PageCount) - 1;    // set it with the last index
                if (startPage > endPage) startPage = endPage;                      // startPage is less than endPage, then set it with the same value.

                for (uint i = (uint)startPage; i <= endPage; i++)
                {
                    var page = pdfDoc.GetPage(i);
                    //BitmapImage image = new BitmapImage();
                    WriteableBitmap image = new WriteableBitmap((int)page.Size.Width, (int)page.Size.Height);
                    using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
                    {
                        await page.RenderToStreamAsync(stream);
                        await image.SetSourceAsync(stream);
                        result = await printBitmap(image, width, alignment, brightness, isDithering, isCompress);
                    }
                    if (result != (int)MPosResult.MPOS_SUCCESS)
                    {
                        MPosDebug.WriteLine("PrinterImplementation printPDFfile : printBitmap failed, break the loop");
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printPDFfile : Exception Code = 0x" + ex.HResult.ToString("X2"));
                MPosDebug.WriteLine("PrinterImplementation printPDFfile : Exception = " + ex.ToString());
                MPosDebug.WriteLine("PrinterImplementation printPDFfile : END");
                if ((uint)ex.HResult == 0x80070005)
                {
                    // UnauthorizedAccessException
                    result = (int)MPosResult.MPOS_FAIL_NOT_PERMISSION_ALLOWD;
                    MPosDebug.WriteLine("PrinterImplementation printPDFfile : UnauthorizedAccessException");
                }
                if ((uint)ex.HResult == 0x80070002)
                {
                    // FileNotFoundException
                    MPosDebug.WriteLine("PrinterImplementation printPDFfile : FileNotFoundException");
                    result = (int)MPosResult.MPOS_FAIL_NO_FILE;
                }
            }

            return result;
        }
        public async Task<int> printBitmapFile(string filePath, int width, int alignment, int brightness, bool isDithering, bool isCompress)
        {
            int result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("PrinterImplementation printBitmap : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : filePath = " + filePath);
            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : width = " + width + ", alignment = " + alignment + ", brightness = " + brightness);
            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : isDithering = " + isDithering + ", isCompress = " + isCompress);
            //filePath = filePath.ToUpper();

            Guid decoderId;

            var upperfilePath = filePath.ToUpper();
            if (upperfilePath.Contains(".JPG")) decoderId = BitmapDecoder.JpegDecoderId;
            else if (upperfilePath.Contains(".JPEG")) decoderId = BitmapDecoder.JpegDecoderId;
            else if (upperfilePath.Contains(".GIF")) decoderId = BitmapDecoder.GifDecoderId;
            else if (upperfilePath.Contains(".PNG")) decoderId = BitmapDecoder.PngDecoderId;
            else if (upperfilePath.Contains(".BMP")) decoderId = BitmapDecoder.BmpDecoderId;
            else
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : MPOS_FAIL_NOT_SUPPORTED_IMAGE_TYPE");
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                throw new Exception("NOT SUPPORTED IMAGE TYPE");
                //return (int)MPosResult.MPOS_FAIL_NOT_SUPPORTED_IMAGE_TYPE;
            }

            if (width == 0)
            {
                // Image width cannot be Zero (0)
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            int writtenBytes = 0;
            byte[] imgData = null;

            try
            {
                StorageFile file = null;

                if (filePath.Contains("ms-appx:///") || filePath.Contains("ms-appdata:///") || filePath.Contains("ms-appx-web:///"))
                    file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(filePath));
                else
                    file = await StorageFile.GetFileFromPathAsync(filePath);

                using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
                {
                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(decoderId, stream);
                    imgData = await getPrintImageData(decoder, width, alignment, brightness, isDithering, isCompress);
                    if (imgData != null)
                        writtenBytes = await _MPosConnection.writeAsync(DeviceID, imgData);
                }

                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : written bytes = " + writtenBytes.ToString());
                result = (int)MPosResult.MPOS_SUCCESS;
                if (imgData == null || (writtenBytes != imgData.Length))
                    result = (int)MPosResult.MPOS_FAIL;
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : Exception Code = 0x" + ex.HResult.ToString("X2"));
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : Exception = " + ex.ToString());
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                if ((uint)ex.HResult == 0x80070005)
                {
                    // UnauthorizedAccessException
                    result = (int)MPosResult.MPOS_FAIL_NOT_PERMISSION_ALLOWD;
                    MPosDebug.WriteLine("PrinterImplementation printBitmapFile : UnauthorizedAccessException");
                }
                if ((uint)ex.HResult == 0x80070002)
                {
                    // FileNotFoundException
                    MPosDebug.WriteLine("PrinterImplementation printBitmapFile : FileNotFoundException");
                    result = (int)MPosResult.MPOS_FAIL_NO_FILE;
                }
            }

            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
            return result;
        }

        public async Task<int> printBitmapFile(string filePath, int width, int alignment, int threshold, int ditheringType, int compressType)
        {
            int result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("PrinterImplementation printBitmap : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : filePath = " + filePath);
            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : width = " + width + ", alignment = " + alignment + ", threshold = " + threshold);
            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : ditheringType = " + ditheringType + ", compressType = " + compressType);
            //filePath = filePath.ToUpper();

            Guid decoderId;

            var upperfilePath = filePath.ToUpper();
            if (upperfilePath.Contains(".JPG")) decoderId = BitmapDecoder.JpegDecoderId;
            else if (upperfilePath.Contains(".JPEG")) decoderId = BitmapDecoder.JpegDecoderId;
            else if (upperfilePath.Contains(".GIF")) decoderId = BitmapDecoder.GifDecoderId;
            else if (upperfilePath.Contains(".PNG")) decoderId = BitmapDecoder.PngDecoderId;
            else if (upperfilePath.Contains(".BMP")) decoderId = BitmapDecoder.BmpDecoderId;
            else
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : MPOS_FAIL_NOT_SUPPORTED_IMAGE_TYPE");
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                throw new Exception("NOT SUPPORTED IMAGE TYPE");
                //return (int)MPosResult.MPOS_FAIL_NOT_SUPPORTED_IMAGE_TYPE;
            }

            if (width == 0)
            {
                // Image width cannot be Zero (0)
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            int writtenBytes = 0;
            byte[] imgData = null;

            try
            {
                StorageFile file = null;

                if (filePath.Contains("ms-appx:///") || filePath.Contains("ms-appdata:///") || filePath.Contains("ms-appx-web:///"))
                    file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(filePath));
                else
                    file = await StorageFile.GetFileFromPathAsync(filePath);

                using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
                {
                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(decoderId, stream);
                    bool isDithering = true;
                    if (ditheringType == 0) isDithering = false;
                    imgData = await getPrintImageData(decoder, width, alignment, threshold, isDithering, compressType);

                    if (imgData != null)
                        writtenBytes = await _MPosConnection.writeAsync(DeviceID, imgData);
                }

                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : written bytes = " + writtenBytes.ToString());
                result = (int)MPosResult.MPOS_SUCCESS;
                if (imgData == null || (writtenBytes != imgData.Length))
                    result = (int)MPosResult.MPOS_FAIL;
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : Exception Code = 0x" + ex.HResult.ToString("X2"));
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : Exception = " + ex.ToString());
                MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
                if ((uint)ex.HResult == 0x80070005)
                {
                    // UnauthorizedAccessException
                    result = (int)MPosResult.MPOS_FAIL_NOT_PERMISSION_ALLOWD;
                    MPosDebug.WriteLine("PrinterImplementation printBitmapFile : UnauthorizedAccessException");
                }
                if ((uint)ex.HResult == 0x80070002)
                {
                    // FileNotFoundException
                    MPosDebug.WriteLine("PrinterImplementation printBitmapFile : FileNotFoundException");
                    result = (int)MPosResult.MPOS_FAIL_NO_FILE;
                }
            }

            MPosDebug.WriteLine("PrinterImplementation printBitmapFile : END");
            return result;
        }

        public async Task<int> PrintBitmapWithBase64(string base64String, int width, int alignment, int brightness, bool isDithering, bool isCompress)
        {
            var result = (int)MPosResult.MPOS_FAIL;
            byte[] imgPrintCmd = null;

            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : START");
            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }
            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : width = " + width + ", alignment = " + alignment + ", brightness = " + brightness);
            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : isDithering = " + isDithering + ", isCompress = " + isCompress);

            try
            {
                var randomAccessStream = new InMemoryRandomAccessStream();

                using (var dataWriter = new DataWriter(randomAccessStream))
                {
                    // read stream
                    dataWriter.WriteBytes(Convert.FromBase64String(base64String));
                    await dataWriter.StoreAsync();

                    // decode image
                    var decoder = await BitmapDecoder.CreateAsync(randomAccessStream);
                    randomAccessStream.Seek(0);

                    // Get the SoftwareBitmap representation of the file
                    SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
                    MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : PixelHeight = " + softwareBitmap.PixelHeight + ", PixelWidth = " + softwareBitmap.PixelWidth);

                    byte[] imgData = null;
                    UInt32 resizeWidth = 0;
                    UInt32 resizeHeight = 0;

                    if (width == (int)MPosImageWidth.MPOS_IMAGE_WIDTH_ASIS || width < 0)
                    {
                        resizeWidth = decoder.PixelWidth;
                        resizeHeight = decoder.PixelHeight;
                    }
                    else
                    {
                        resizeWidth = (UInt32)width;
                        resizeHeight = (UInt32)((decoder.PixelHeight * resizeWidth) / decoder.PixelWidth);
                    }

                    BitmapTransform resizeForm = new BitmapTransform()
                    {
                        ScaledWidth = resizeWidth,
                        ScaledHeight = resizeHeight
                    };

                    MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : PixelWidth = " + decoder.PixelWidth + ", PixelHeight = " + decoder.PixelHeight);
                    MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : resizeWidth = " + resizeWidth + ", resizeHeight = " + resizeHeight);

                    PixelDataProvider pixelDataProvider = await decoder.GetPixelDataAsync(
                                          BitmapPixelFormat.Bgra8,    // WriteableBitmap uses BGRA format
                                          BitmapAlphaMode.Straight,
                                          resizeForm,
                                          ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation
                                          ColorManagementMode.DoNotColorManage);

                    if (pixelDataProvider != null)
                    {
                        // cannot print image with RLE in page mode.
                        isCompress = _PageModeInOut ? false : isCompress;

                        if (isCompress)
                            imgPrintCmd = MPosImagePrintCommand.getRleCmd(pixelDataProvider.DetachPixelData(),
                                                                            resizeForm.ScaledWidth,
                                                                            resizeForm.ScaledHeight,
                                                                            (uint)brightness,
                                                                            alignment,
                                                                            isDithering);
                        else
                            imgPrintCmd = MPosImagePrintCommand.getGSv0Cmd(pixelDataProvider.DetachPixelData(),
                                                                            resizeForm.ScaledWidth,
                                                                            resizeForm.ScaledHeight,
                                                                            (uint)brightness,
                                                                            alignment,
                                                                            isDithering);

                        var writtenBytes = await _MPosConnection.writeAsync(DeviceID, imgPrintCmd);
                        MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : written bytes = " + writtenBytes + ", imgPrintCmd = " + imgPrintCmd.Length);
                        if (writtenBytes == imgPrintCmd.Length)
                            result = (int)MPosResult.MPOS_SUCCESS;
                    }
                }

            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : ex = " + ex.Message);
                result = (int)MPosResult.MPOS_FAIL_IMAGE_DECODE;
            }
            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : END");
            return result;
        }

        public async Task<int> PrintBitmapWithBase64(string base64String, int width, int alignment, int threshold, int ditheringType, int compressType)
        {
            var result = (int)MPosResult.MPOS_FAIL;
            byte[] imgPrintCmd = null;

            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : START");
            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }
            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : width = " + width + ", alignment = " + alignment + ", threshold = " + threshold);
            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : ditheringType = " + ditheringType + ", compressType = " + compressType);

            try
            {
                var randomAccessStream = new InMemoryRandomAccessStream();

                using (var dataWriter = new DataWriter(randomAccessStream))
                {
                    // read stream
                    dataWriter.WriteBytes(Convert.FromBase64String(base64String));
                    await dataWriter.StoreAsync();

                    // decode image
                    var decoder = await BitmapDecoder.CreateAsync(randomAccessStream);
                    randomAccessStream.Seek(0);

                    // Get the SoftwareBitmap representation of the file
                    SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
                    MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : PixelHeight = " + softwareBitmap.PixelHeight + ", PixelWidth = " + softwareBitmap.PixelWidth);

                    UInt32 resizeWidth = 0;
                    UInt32 resizeHeight = 0;

                    if (width == (int)MPosImageWidth.MPOS_IMAGE_WIDTH_ASIS || width < 0)
                    {
                        resizeWidth = decoder.PixelWidth;
                        resizeHeight = decoder.PixelHeight;
                    }
                    else
                    {
                        resizeWidth = (UInt32)width;
                        resizeHeight = (UInt32)((decoder.PixelHeight * resizeWidth) / decoder.PixelWidth);
                    }

                    BitmapTransform resizeForm = new BitmapTransform()
                    {
                        ScaledWidth = resizeWidth,
                        ScaledHeight = resizeHeight
                    };

                    MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : PixelWidth = " + decoder.PixelWidth + ", PixelHeight = " + decoder.PixelHeight);
                    MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : resizeWidth = " + resizeWidth + ", resizeHeight = " + resizeHeight);

                    PixelDataProvider pixelDataProvider = await decoder.GetPixelDataAsync(
                                          BitmapPixelFormat.Bgra8,    // WriteableBitmap uses BGRA format
                                          BitmapAlphaMode.Straight,
                                          resizeForm,
                                          ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation
                                          ColorManagementMode.DoNotColorManage);

                    if (pixelDataProvider != null)
                    {
                        // cannot print image with RLE in page mode.
                        compressType = _PageModeInOut ? 0 : compressType;
                        bool isDithering = true;
                        if (ditheringType == 0) isDithering = false;

                        if ((MPosCompressType)compressType == MPosCompressType.MPOS_IMAGE_COMPRESS_RLE)
                            imgPrintCmd = MPosImagePrintCommand.getRleCmd(pixelDataProvider.DetachPixelData(),
                                                                            resizeForm.ScaledWidth,
                                                                            resizeForm.ScaledHeight,
                                                                            (uint)50,
                                                                            alignment,
                                                                            isDithering,
                                                                            threshold);
                        else if ((MPosCompressType)compressType == MPosCompressType.MPOS_IMAGE_COMPRESS_LZMA)
                            imgPrintCmd = MPosImagePrintCommand.getLZMACmd(pixelDataProvider.DetachPixelData(),
                                                                            resizeForm.ScaledWidth,
                                                                            resizeForm.ScaledHeight,
                                                                            (uint)50,
                                                                            alignment,
                                                                            isDithering,
                                                                            threshold);
                        else
                            imgPrintCmd = MPosImagePrintCommand.getGSv0Cmd(pixelDataProvider.DetachPixelData(),
                                                                            resizeForm.ScaledWidth,
                                                                            resizeForm.ScaledHeight,
                                                                            (uint)50,
                                                                            alignment,
                                                                            isDithering,
                                                                            threshold);

                        var writtenBytes = await _MPosConnection.writeAsync(DeviceID, imgPrintCmd);
                        MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : written bytes = " + writtenBytes + ", imgPrintCmd = " + imgPrintCmd.Length);
                        if (writtenBytes == imgPrintCmd.Length)
                            result = (int)MPosResult.MPOS_SUCCESS;
                    }
                }

            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : ex = " + ex.Message);
                result = (int)MPosResult.MPOS_FAIL_IMAGE_DECODE;
            }
            MPosDebug.WriteLine("PrinterImplementation printBitmapWithBase64 : END");
            return result;
        }
        public async Task<int> printBitmap(object platformImage, int width, int alignment, int brightness, bool isDithering, bool isCompress)
        {
            MPosDebug.WriteLine("PrinterImplementation printBitmap : START");
            MPosDebug.WriteLine("PrinterImplementation printBitmap : width = " + width + ", alignment = " + alignment + ", brightness = " + brightness);
            MPosDebug.WriteLine("PrinterImplementation printBitmap : isDithering = " + isDithering + ", isCompress = " + isCompress);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmap : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printBitmap : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (width == 0)
            {
                // Image width cannot be Zero (0)
                MPosDebug.WriteLine("PrinterImplementation printBitmap : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("PrinterImplementation printBitmap : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            if (platformImage as BitmapImage == null && platformImage as WriteableBitmap == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmap : Type not supported");
                MPosDebug.WriteLine("PrinterImplementation printBitmap : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            int writtenBytes = 0;
            byte[] imgData = null;

            try
            {
                if (platformImage as WriteableBitmap != null)
                {
                    imgData = await getPrintImageData(platformImage as WriteableBitmap, width, alignment, brightness, isDithering, isCompress);
                }
                else if (platformImage as BitmapImage != null)
                {
                    // 참조 : https://www.codeproject.com/Tips/804423/Conversion-between-File-Byte-Stream-BitmapImage-an
                    RandomAccessStreamReference random = RandomAccessStreamReference.CreateFromUri((platformImage as BitmapImage).UriSource);
                    using (IRandomAccessStream stream = await random.OpenReadAsync())
                    {
                        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
                        imgData = await getPrintImageData(decoder, width, alignment, brightness, isDithering, isCompress);
                    }
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmap : Exception = " + ex.ToString());
            }

            if (imgData != null)
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, imgData);

            MPosDebug.WriteLine("PrinterImplementation printBitmap : written bytes = " + writtenBytes.ToString());
            MPosDebug.WriteLine("PrinterImplementation printBitmap : END");

            if (imgData == null || (writtenBytes != imgData.Length))
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }

        public async Task<int> printBitmap(object platformImage, int width, int alignment, int threshold, int ditheringType, int compressType)
        {
            MPosDebug.WriteLine("PrinterImplementation printBitmap : START");
            MPosDebug.WriteLine("PrinterImplementation printBitmap : width = " + width + ", alignment = " + alignment + ", threshold = " + threshold);
            MPosDebug.WriteLine("PrinterImplementation printBitmap : ditheringType = " + ditheringType + ", compressType = " + compressType);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmap : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation printBitmap : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (width == 0)
            {
                // Image width cannot be Zero (0)
                MPosDebug.WriteLine("PrinterImplementation printBitmap : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("PrinterImplementation printBitmap : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            if (platformImage as BitmapImage == null && platformImage as WriteableBitmap == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmap : Type not supported");
                MPosDebug.WriteLine("PrinterImplementation printBitmap : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            int writtenBytes = 0;
            byte[] imgData = null;

            try
            {
                if (platformImage as WriteableBitmap != null)
                {
                    bool isDithering = true;
                    if (ditheringType == 0) isDithering = false;
                    imgData = await getPrintImageData(platformImage as WriteableBitmap, width, alignment, threshold, isDithering, compressType);
                }
                else if (platformImage as BitmapImage != null)
                {
                    // 참조 : https://www.codeproject.com/Tips/804423/Conversion-between-File-Byte-Stream-BitmapImage-an
                    RandomAccessStreamReference random = RandomAccessStreamReference.CreateFromUri((platformImage as BitmapImage).UriSource);
                    using (IRandomAccessStream stream = await random.OpenReadAsync())
                    {
                        bool isDithering = true;
                        if (ditheringType == 0) isDithering = false;
                        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
                        imgData = await getPrintImageData(decoder, width, alignment, threshold, isDithering, compressType);
                    }
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBitmap : Exception = " + ex.ToString());
            }

            if (imgData != null)
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, imgData);

            MPosDebug.WriteLine("PrinterImplementation printBitmap : written bytes = " + writtenBytes.ToString());
            MPosDebug.WriteLine("PrinterImplementation printBitmap : END");

            if (imgData == null || (writtenBytes != imgData.Length))
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }

        public async Task<int> printLine(int x1, int y1, int x2, int y2, int thickness)
        {
            MPosDebug.WriteLine("PrinterImplementation printLine : START", _DebugId);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printLine : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation printLine : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (!_MPosConnection._PageModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation printLine : MPOS_FAIL");
                MPosDebug.WriteLine("PrinterImplementation printLine : END");
                return (int)MPosResult.MPOS_FAIL;
            }

            int writtenBytes = 0;

            byte[] printLineCmd = { 0x1C, 0x44, 0x4C, 0x49, 0x4E, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

            try
            {
                if (x1 < 0) x1 = 0;
                if (y1 < 0) y1 = 0;
                if (x2 < 0) x2 = 0;
                if (y2 < 0) y2 = 0;
                if (thickness < 0) thickness = 0;
                if (thickness >= 16) thickness = 16;

                printLineCmd[6] = (byte)(x1 % 256);
                printLineCmd[7] = (byte)(x1 / 256);
                printLineCmd[8] = (byte)(y1 % 256);
                printLineCmd[9] = (byte)(y1 / 256);
                printLineCmd[10] = (byte)(x2 % 256);
                printLineCmd[11] = (byte)(x2 / 256);
                printLineCmd[12] = (byte)(y2 % 256);
                printLineCmd[13] = (byte)(y2 / 256);
                printLineCmd[14] = (byte)thickness;

                writtenBytes = await _MPosConnection.writeAsync(DeviceID, printLineCmd);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printLine : Exception = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation printLine : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation printLine : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> printBox(int x1, int y1, int x2, int y2, int thickness)
        {
            MPosDebug.WriteLine("PrinterImplementation printBox : START", _DebugId);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation printBox : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation printBox : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (!_MPosConnection._PageModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation printBox : MPOS_FAIL");
                MPosDebug.WriteLine("PrinterImplementation printBox : END");
                return (int)MPosResult.MPOS_FAIL;
            }

            int writtenBytes = 0;

            byte[] printBoxCmd = { 0x1C, 0x44, 0x42, 0x4F, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

            try
            {
                if (x1 < 0) x1 = 0;
                if (y1 < 0) y1 = 0;
                if (x2 < 0) x2 = 0;
                if (y2 < 0) y2 = 0;
                if (thickness < 0) thickness = 0;
                if (thickness >= 16) thickness = 16;

                printBoxCmd[5] = (byte)(x1 % 256);
                printBoxCmd[6] = (byte)(x1 / 256);
                printBoxCmd[7] = (byte)(y1 % 256);
                printBoxCmd[8] = (byte)(y1 / 256);
                printBoxCmd[9] = (byte)(x2 % 256);
                printBoxCmd[10] = (byte)(x2 / 256);
                printBoxCmd[11] = (byte)(y2 % 256);
                printBoxCmd[12] = (byte)(y2 / 256);
                printBoxCmd[13] = (byte)thickness;

                writtenBytes = await _MPosConnection.writeAsync(DeviceID, printBoxCmd);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("PrinterImplementation printBox : Exception = " + ex.Message);
            }

            MPosDebug.WriteLine("PrinterImplementation printBox : written bytes = " + writtenBytes);
            MPosDebug.WriteLine("PrinterImplementation printBox : END");

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
    }


    internal partial class MPosDevicePrinterImplementation : MPosControllerPrinter
    {
        public async Task<int> setCharacterset(int characterset)
        {
            MPosDebug.WriteLine("PrinterImplementation setCharacterset : START", _DebugId);

            bool isMember = Enum.GetValues(typeof(MPosCodePage)).Cast<int>().Any(x => x == (int)characterset);

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

            if (isMember != true)
            {
                MPosDebug.WriteLine("PrinterImplementation setCharacterset : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setCharacterset : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation setCharacterset : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            byte[] command = getCharactersetCmd(characterset);

            var writtenBytes = 0;

            if (command != null)
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, command);

            MPosDebug.WriteLine("PrinterImplementation setCharacterset : written bytes = " + writtenBytes.ToString(), _DebugId);
            MPosDebug.WriteLine("PrinterImplementation setCharacterset : END", _DebugId);

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public int setTextEncoding(int textEncoding)
        {
            MPosDebug.WriteLine("PrinterImplementation setTextEncoding : START", _DebugId);

            bool isMember = Enum.GetValues(typeof(MPosCodePage)).Cast<int>().Any(x => x == (int)textEncoding);
            isMember |= Enum.GetValues(typeof(MPosEastAsiaCodePage)).Cast<int>().Any(x => x == (int)textEncoding);
            isMember |= Enum.GetValues(typeof(MPosUnicodeCodePage)).Cast<int>().Any(x => x == (int)textEncoding);

            if (isMember != true)
            {
                MPosDebug.WriteLine("PrinterImplementation setTextEncoding : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NOT_SUPPORT_CODEPAGE;
            }

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setTextEncoding : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation setTextEncoding : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            _TextEncoding = textEncoding;

            MPosDebug.WriteLine("PrinterImplementation setTextEncoding : TextEncoding = " + _TextEncoding, _DebugId);
            MPosDebug.WriteLine("PrinterImplementation setTextEncoding : END", _DebugId);
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> setInternationalCharacterset(int internationalCharacterset)
        {
            MPosDebug.WriteLine("PrinterImplementation setInternationalCharacterset : START", _DebugId);

            bool isMember = Enum.GetValues(typeof(MPosICS)).Cast<int>().Any(x => x == (int)internationalCharacterset);

            if (isMember != true)
            {
                MPosDebug.WriteLine("PrinterImplementation setInternationalCharacterset : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setInternationalCharacterset : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation setInternationalCharacterset : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            byte[] command = new byte[] { 0x1b, 0x52, 0x00 };
            command[2] = (byte)internationalCharacterset;

            var writtenBytes = await _MPosConnection.writeAsync(DeviceID, command);

            MPosDebug.WriteLine("PrinterImplementation setInternationalCharacterset : written bytes = " + writtenBytes.ToString(), _DebugId);
            MPosDebug.WriteLine("PrinterImplementation setInternationalCharacterset : END", _DebugId);

            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }

        public async Task<int> setPagemode(int pageMode)
        {
            MPosDebug.WriteLine("PrinterImplementation setPagemode : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemode : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation setPagemode : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            MPosDebug.WriteLine("PrinterImplementation setPagemode : setPagemode = " + pageMode.ToString());

            bool isMember = Enum.GetValues(typeof(MPosPageMode)).Cast<int>().Any(x => x == (int)pageMode);

            if (isMember == false)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemode : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("PrinterImplementation setPagemode : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            bool mode = Convert.ToBoolean(pageMode);

            // Already transaction mode in
            if (mode && _MPosConnection._TransactionModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemode : MPOS_TRANSACTION_ALREADY_IN");
                MPosDebug.WriteLine("PrinterImplementation setPagemode : END");
                //return (int)MPosResult.MPOS_TRANSACTION_ALREADY_IN;
                throw new Exception("TRANSACTION_MODE_ALREADY_IN");
            }

            // Already page mode entered.
            if (mode && _MPosConnection._PageModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemode : MPOS_PAGEMODE_ALREADY_IN");
                MPosDebug.WriteLine("PrinterImplementation setPagemode : END");
                //return (int)MPosResult.MPOS_PAGEMODE_ALREADY_IN;
                throw new Exception("PAGE_MODE_ALREADY_IN");
            }

            _PageModeInOut = mode;
            _MPosConnection._PageModeInOut = mode;

            if (_MPosConnection._PageModeInOut)
            {
                _MPosConnection.setBufferStored(new byte[] { 0x1b, 0x4c });
                MPosDebug.WriteLine("PrinterImplementation setPagemode : END");
                return (int)MPosResult.MPOS_SUCCESS;
            }

            //MPosConnection.setBufferStored(new byte[] { 0x1b, 0x53, 0x0c });
            _MPosConnection.setBufferStored(new byte[] { 0x0c });

            var result = await _MPosConnection.clearBufferStored(DeviceID);
            MPosDebug.WriteLine("PrinterImplementation setPagemode : END");

            return result;
        }
        public int setPagemodePrintArea(int x, int y, int width, int height)
        {
            MPosDebug.WriteLine("PrinterImplementation setPagemodePrintArea : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemodePrintArea : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation setPagemodePrintArea : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (_MPosConnection._PageModeInOut == false)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemodePrintArea : MPOS_FAIL");
                MPosDebug.WriteLine("PrinterImplementation setPagemodePrintArea : END");
                throw new Exception("NOT IN PAGE MODE");
            }

            byte[] cmdPrintArea = new byte[10];

            cmdPrintArea[0] = 0x1b;
            cmdPrintArea[1] = 0x57;
            cmdPrintArea[2] = (byte)(x % 256);
            cmdPrintArea[3] = (byte)(x / 256);
            cmdPrintArea[4] = (byte)(y % 256);
            cmdPrintArea[5] = (byte)(y / 256);
            cmdPrintArea[6] = (byte)(width % 256);
            cmdPrintArea[7] = (byte)(width / 256);
            cmdPrintArea[8] = (byte)(height % 256);
            cmdPrintArea[9] = (byte)(height / 256);

            _MPosConnection.setBufferStored(cmdPrintArea);

            MPosDebug.WriteLine("PrinterImplementation setPagemodePrintArea : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public int setPagemodeDirection(int direction)
        {
            MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (_MPosConnection._PageModeInOut == false)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : MPOS_FAIL");
                MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : END");
                throw new Exception("NOT IN PAGE MODE");
            }

            byte[] cmdDirection = new byte[3] { 0x1b, 0x54, 0x00 };

            // ESC T n, select print direction in page mode
            switch ((MPosPageModeDirection)direction)
            {
                case MPosPageModeDirection.MPOS_PRINTER_PD_0:
                case MPosPageModeDirection.MPOS_PRINTER_PD_LEFT90:
                case MPosPageModeDirection.MPOS_PRINTER_PD_180:
                case MPosPageModeDirection.MPOS_PRINTER_PD_RIGHT90:
                    cmdDirection[2] = (byte)direction;
                    _MPosConnection.setBufferStored(cmdDirection);
                    break;
                default:
                    MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : END");
                    return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            MPosDebug.WriteLine("PrinterImplementation setPagemodeDirection : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public int setPagemodePosition(int x, int y)
        {
            MPosDebug.WriteLine("PrinterImplementation setPagemodePosition : START");

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemodePosition : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("PrinterImplementation setPagemodePosition : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            if (_MPosConnection._PageModeInOut == false)
            {
                MPosDebug.WriteLine("PrinterImplementation setPagemodePosition : MPOS_FAIL");
                MPosDebug.WriteLine("PrinterImplementation setPagemodePosition : END");
                throw new Exception("NOT IN PAGE MODE");
            }

            if (x >= 0)
            {
                // ESC $ nL nH, set absolute horizontal position
                byte[] xPosCmd = new byte[] { 0x1b, 0x24, 0x00, 0x00 };
                xPosCmd[2] = (byte)(x % 256);
                xPosCmd[3] = (byte)(x / 256);
                _MPosConnection.setBufferStored(xPosCmd);
            }

            if (y >= 0)
            {
                // GS $ nL nH, set absolute vertical position
                byte[] yPosCmd = new byte[] { 0x1d, 0x24, 0x00, 0x00 };
                yPosCmd[2] = (byte)(y % 256);
                yPosCmd[3] = (byte)(y / 256);
                _MPosConnection.setBufferStored(yPosCmd);
            }

            MPosDebug.WriteLine("PrinterImplementation setPagemodePosition : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> setTransaction(int transactionMode)
        {
            MPosDebug.WriteLine("PrinterImplementation setTransaction : START", _DebugId);

            if (IsOpen == false || _MPosConnection == null)
            {
                MPosDebug.WriteLine("PrinterImplementation setTransaction : MPOS_FAIL_NO_OPEN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation setTransaction : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            MPosDebug.WriteLine("PrinterImplementation setTransaction : transactionMode = " + transactionMode.ToString(), _DebugId);

            bool isMember = Enum.GetValues(typeof(MPosTransactionMode)).Cast<int>().Any(x => x == (int)transactionMode);

            if (isMember == false)
            {
                MPosDebug.WriteLine("PrinterImplementation setTransaction : MPOS_FAIL_INVALID_PARAMETER", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation setTransaction : END", _DebugId);
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            // Already page mode entered.
            bool mode = Convert.ToBoolean(transactionMode);

            if (mode && _MPosConnection._PageModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation setTransaction : MPOS_PAGEMODE_ALREADY_IN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation setTransaction : END", _DebugId);
                //return (int)MPosResult.MPOS_PAGEMODE_ALREADY_IN;
                throw new Exception("PAGEMODE_ALREADY_IN");
            }

            // Already transaction mode in
            if (mode && _MPosConnection._TransactionModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation transactionMode : MPOS_TRANSACTION_ALREADY_IN", _DebugId);
                MPosDebug.WriteLine("PrinterImplementation transactionMode : END", _DebugId);
                //return (int)MPosResult.MPOS_TRANSACTION_ALREADY_IN;
                throw new Exception("TRANSACTION MODE ALREADY IN");
            }

            _TransactionModeInOut = mode;
            _MPosConnection._TransactionModeInOut = mode;

            if (_MPosConnection._TransactionModeInOut)
            {
                MPosDebug.WriteLine("PrinterImplementation setTransaction : END", _DebugId);
                return (int)MPosResult.MPOS_SUCCESS;
            }

            int result = await _MPosConnection.clearBufferStored(DeviceID);
            MPosDebug.WriteLine("PrinterImplementation transactionMode : END", _DebugId);

            return result;
        }

    }

}
