using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;
using WinRT.BxlComponent.Plugin.BxlMpXamarinSDK;
using WinRT.BxlComponent.Plugin.BxlMpXamarinSDK.Abstractions;

namespace Plugin.BxlMAUISDK.PlatformImplementations
{
    partial class MPosControllerBCDImplementation : MPosControllerBCD
    {
        public int DeviceID { get; private set; }
        public MPosDeviceType DeviceType { get; private set; }
        public MPosInterfaceType InterfaceType { get; private set; } = MPosInterfaceType.MPOS_INTERFACE_BLUETOOTH;
        public bool IsOpen { get; private set; } = false;
        public MPosCommandMode CommandMode { get; private set; } = MPosCommandMode.MPOS_COMMAND_MODE_BGATE;
        public IMPosConnection _MPosConnection { get; private set; } = null;
        public string _ConnectionString { get; private set; }
        private MPosDeviceDataProcessor _DeviceDataProcessor = new MPosDeviceDataProcessor();
        private string _DebugId;
    }

    partial class MPosControllerBCDImplementation : MPosControllerBCD
    {
        public MPosControllerBCDImplementation(int deviceType, int deviceId)
        {
            MPosDebug.WriteLine("BCDImplementation Constructor : START");
            DeviceID = deviceId;
            DeviceType = (MPosDeviceType)deviceType;
            _DebugId = DebugHelper.GetId(this);
            MPosDebug.WriteLine("BCDImplementation Constructor : DeviceID = " + DeviceID);
            MPosDebug.WriteLine("BCDImplementation Constructor : DeviceType = " + DeviceType);
            MPosDebug.WriteLine("BCDImplementation Constructor : END", _DebugId);
            MPosDebug.WriteLine("BCDImplementation Constructor : END");
        }
        public async Task<int> openService(uint timeout = 5 /*second*/)
        {
            if (IsOpen)
            {
                MPosDebug.WriteLine("BCDImplementation openService : MPOS_ALREADY_OPEN");
                MPosDebug.WriteLine("BCDImplementation openService : END");
                return (int)MPosResult.MPOS_ALREADY_OPEN;
            }

            return await openService((uint)DeviceID, timeout);
        }
        public async Task<int> openService(uint deviceId = 0, uint timeout = 5 /*second*/)
        {
            int result = (int)MPosResult.MPOS_SUCCESS;
            MPosDebug.WriteLine("BCDImplementation openService : START");
            MPosDebug.WriteLine("BCDImplementation openService : ConnectionString = " + _ConnectionString);
            MPosDebug.WriteLine("BCDImplementation openService : CommandMode = " + CommandMode.ToString());

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

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

            try
            {
                _DeviceDataProcessor.initDataStream();
                _MPosConnection.addDataReadEvent(onDataReadEvent);
                _MPosConnection.addDisconnectedEvent(onDisconnectedEvent);
                _MPosConnection.addConnectedEvent(onConnectedEvent);
                _MPosConnection.addDeviceListUpdateEvent(onDeviceListUpdateEvent);

                if (await _MPosConnection.openAsync(DeviceType, timeout))
                {
                    IsOpen = true;
                    DeviceID = (int)deviceId;

                    onConnectedEvent();
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("BCDImplementation openService : ex.Message = " + ex.Message);
                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("BCDImplementation openService : END");
            return result;
        }
        public async Task<int> closeService(uint closeTimeout = 0 /*second*/)
        {
            MPosDebug.WriteLine("BCDImplementation closeService : START");
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
                MPosConnectionBuilderImplementation.RemoveDevice(DeviceID);
            IsOpen = false;
            statusUpdateEvent = null;
            dataEvent = null;
            //_DeviceDataProcessor.clearDataStream();

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

            MPosDebug.WriteLine("BCDImplementation closeService : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public bool isOpen()
        {
            return IsOpen;
        }
        public int selectCommandMode(int commandMode)
        {
            MPosDebug.WriteLine("BCDImplementation selectCommandMode : START");
            MPosDebug.WriteLine("BCDImplementation selectCommandMode : commandMode = " + commandMode);

            CommandMode = (MPosCommandMode)commandMode;

            MPosDebug.WriteLine("BCDImplementation selectCommandMode : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public int selectInterface(int interfaceType, string address)
        {
            MPosDebug.WriteLine("BCDImplementation selectInterface : START");
            MPosDebug.WriteLine("BCDImplementation selectInterface : interfaceType = " + interfaceType + " address = " + address);

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

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

            MPosDebug.WriteLine("BCDImplementation selectInterface : MPOS_FAIL_INVALID_PARAMETER");
            MPosDebug.WriteLine("BCDImplementation selectInterface : END");
            return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
        }
        public async Task<int> setTransaction(int transactionMode)
        {
            return (int)MPosResult.MPOS_FAIL_NOT_SUPPORT;
        }
        public async Task<int> directIO(byte[] data)
        {
            MPosDebug.WriteLine("BCDImplementation directIO : START");

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

            int writtenBytes = 0;
            if (data != null)
            {
                MPosDebug.WriteLine("BCDImplementation directIO : data.Length = " + data.Length);
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, data, false, true);
                MPosDebug.WriteLine("BCDImplementation directIO : written bytes = " + writtenBytes);
            }

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

    partial class MPosControllerBCDImplementation : MPosControllerBCD
    {
        public async Task<int> displayString(string data, int codePage, int internationalCharacterSet, int line)
        {
            MPosDebug.WriteLine("BCDImplementation displayString : START", _DebugId);

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

            List<byte> command = new List<byte>();
            command.AddRange(getMoveCursorPosCmd(0x01, (byte)line));
            command.AddRange(getInternationalCharSetCmd(internationalCharacterSet));
            command.AddRange(getEncodedTextData(data, codePage));

            var result = (int)MPosResult.MPOS_SUCCESS;
            if (command.Count != 0)
            {
                var writtenBytes = await _MPosConnection.writeAsync(DeviceID, command.ToArray());
                MPosDebug.WriteLine("BCDImplementation displayString : written bytes = " + writtenBytes);
                if (writtenBytes <= 0)
                    result = (int)MPosResult.MPOS_FAIL;
            }
            MPosDebug.WriteLine("BCDImplementation displayString : END");
            return result;
        }

        private byte[] getEncodedTextData(string data, int codepage)
        {
            if (data.Length <= 0)
                return null;

            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            byte[] codepageCmd = null;
            codepageCmd = getCharactersetCmd(codepage);

            byte[] encodedData = null;
            switch ((MPosCodePage)codepage)
            {
                // Arabic, KHMER
                case MPosCodePage.MPOS_CODEPAGE_CP864:
                case MPosCodePage.MPOS_CODEPAGE_WPC1256:
                    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)codepage);
                    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)codepage);
                    break;
                case MPosCodePage.MPOS_CODEPAGE_CP928:
                    encodedData = Encoding.GetEncoding(1253).GetBytes(data);
                    break;
                default:
                    encodedData = Encoding.GetEncoding(codepage).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;
        }

        public async Task<int> storeImage(object platformImage, int width, int imageNumber)
        {
            MPosDebug.WriteLine("BCDImplementation storeImage : START");
            MPosDebug.WriteLine("BCDImplementation storeImage : width = " + width + ", imageNumber = " + imageNumber);

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

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

            if (platformImage as BitmapImage == null && platformImage as WriteableBitmap == null)
            {
                MPosDebug.WriteLine("BCDImplementation storeImage : Type not supported");
                MPosDebug.WriteLine("BCDImplementation storeImage : 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, imageNumber);
                }
                //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("BCDImplementation storeImage : Exception = " + ex.ToString());
            }

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

            MPosDebug.WriteLine("BCDImplementation storeImage : written bytes = " + writtenBytes.ToString());
            MPosDebug.WriteLine("BCDImplementation storeImage : END");

            if (imgData == null || (writtenBytes != imgData.Length))
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public async Task<int> storeImageFile(string filePath, int width, int imageNumber)
        {
            int result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("BCDImplementation storeImageFile : START");
            MPosDebug.WriteLine("BCDImplementation storeImageFile : filePath = " + filePath + ", width = " + width + ", imageNumber = " + imageNumber);

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

            MPosDebug.WriteLine("BCDImplementation storeImageFile : filePath = " + filePath);
            MPosDebug.WriteLine("BCDImplementation storeImageFile : width = " + width);

            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("BCDImplementation storeImageFile : MPOS_FAIL_NOT_SUPPORTED_IMAGE_TYPE");
                MPosDebug.WriteLine("BCDImplementation storeImageFile : 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("BCDImplementation storeImageFile : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("BCDImplementation storeImageFile : 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, imageNumber);

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

                MPosDebug.WriteLine("BCDImplementation storeImageFile : 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("BCDImplementation storeImageFile : Exception Code = 0x" + ex.HResult.ToString("X2"));
                MPosDebug.WriteLine("BCDImplementation storeImageFile : Exception = " + ex.ToString());
                MPosDebug.WriteLine("BCDImplementation storeImageFile : END");
                if ((uint)ex.HResult == 0x80070005)
                {
                    // UnauthorizedAccessException
                    result = (int)MPosResult.MPOS_FAIL_NOT_PERMISSION_ALLOWD;
                    MPosDebug.WriteLine("BCDImplementation storeImageFile : UnauthorizedAccessException");
                }
                if ((uint)ex.HResult == 0x80070002)
                {
                    // FileNotFoundException
                    MPosDebug.WriteLine("BCDImplementation storeImageFile : FileNotFoundException");
                    result = (int)MPosResult.MPOS_FAIL_NO_FILE;
                }
            }

            MPosDebug.WriteLine("BCDImplementation storeImageFile : END");
            return result;
        }
        public async Task<int> displayImage(int imageNumber, int xPos, int yPos)
        {
            MPosDebug.WriteLine("BCDImplementation displayImage : START", _DebugId);

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

            List<byte> command = new List<byte>();
            command.AddRange(getImageDisplayCmd(imageNumber, xPos, yPos));

            var result = (int)MPosResult.MPOS_SUCCESS;
            if (command.Count != 0)
            {
                var writtenBytes = await _MPosConnection.writeAsync(DeviceID, command.ToArray());
                MPosDebug.WriteLine("BCDImplementation displayImage : written bytes = " + writtenBytes);
                if (writtenBytes <= 0)
                    result = (int)MPosResult.MPOS_FAIL;
            }
            MPosDebug.WriteLine("BCDImplementation displayImage : END");
            return result;
        }
        public async Task<int> clearImage(int imageNumber)
        {
            MPosDebug.WriteLine("BCDImplementation clearImage : START", _DebugId);

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

            var result = (int)MPosResult.MPOS_SUCCESS;
            var writtenBytes = await _MPosConnection.writeAsync(DeviceID, getClearImageCmd(imageNumber));
            MPosDebug.WriteLine("BCDImplementation clearImage : written bytes = " + writtenBytes);
            if (writtenBytes <= 0)
                result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("BCDImplementation clearImage : END");
            return result;
        }
        public async Task<int> showCursor(int cursor)
        {
            MPosDebug.WriteLine("BCDImplementation showCursor : START", _DebugId);

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

            var result = (int)MPosResult.MPOS_SUCCESS;
            var writtenBytes = await _MPosConnection.writeAsync(DeviceID, getShowCursorCmd(cursor));
            MPosDebug.WriteLine("BCDImplementation showCursor : written bytes = " + writtenBytes);
            if (writtenBytes <= 0)
                result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("BCDImplementation showCursor : END");
            return result;
        }
        public async Task<int> clearScreen()
        {
            MPosDebug.WriteLine("BCDImplementation clearScreen : START", _DebugId);

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

            var result = (int)MPosResult.MPOS_SUCCESS;
            var writtenBytes = await _MPosConnection.writeAsync(DeviceID, getClearScreenCmd());
            MPosDebug.WriteLine("BCDImplementation clearScreen : written bytes = " + writtenBytes);
            if (writtenBytes <= 0)
                result = (int)MPosResult.MPOS_FAIL;
            MPosDebug.WriteLine("BCDImplementation clearScreen : END");
            return result;
        }
    }

    partial class MPosControllerBCDImplementation : MPosControllerBCD
    {
        public event Action<byte[]> dataEvent;
        public event Action<uint> statusUpdateEvent;
        private async void onConnectedEvent()
        {
            MPosDebug.WriteLine("BCDImplementation onConnectedEvent");
            byte[] commandToWrite = null;
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
                commandToWrite = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
            else if (DeviceID < 0xff)
                commandToWrite = new byte[] { 0x08, 0x7e, 0x52, (byte)DeviceID, 0x01 };
            await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false);

            if (statusUpdateEvent == null)
                MPosDebug.WriteLine("BCDImplementation onConnectedEvent : statusUpdateEvent is null");
            MPosDebug.WriteLine("BCDImplementation onConnectedEvent : fireStatusUpdateEvent");
            statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_NORMAL);
        }
        private void onDisconnectedEvent()
        {
            MPosDebug.WriteLine("BCDImplementation onDisconnectedEvent");
            if (statusUpdateEvent == null)
                MPosDebug.WriteLine("BCDImplementation onDisconnectedEvent : statusUpdateEvent is null");
            MPosDebug.WriteLine("BCDImplementation onDisconnectedEvent : fireStatusUpdateEvent");
            statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR);
        }
        private void onDataReadEvent(int deviceId, byte[] data)
        {
            MPosDebug.WriteLine("BCDImplementation onDataReadEvent : deviceId = " + deviceId + ", DeviceID = " + DeviceID);
            if (data == null)
                return;
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            {
                if (deviceId != DeviceID)
                    return;
            }
            //DeviceDataProcessor.writeData(data);
            if (dataEvent == null)
                MPosDebug.WriteLine("BCDImplementation onDataReadEvent : dataEvent is null");
            dataEvent?.Invoke(data);
        }
        private void onDeviceListUpdateEvent(int[] devList)
        {
            if (devList.Any(x => x == DeviceID) == false)
            {
                statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR);
                return;
            }
            statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_NORMAL);
        }

        private byte[] getMoveCursorPosCmd(byte posX, byte posY)
        {
            byte[] command = { 0x1F, 0x24, posX, posY };
            return command;
        }

        private byte[] getInternationalCharSetCmd(int interCharSet)
        {
            byte[] command = { 0x1B, 0x52, (byte)interCharSet };
            return command;
        }

        private byte[] getClearScreenCmd()
        {
            byte[] command = { 0x0C };
            return command;
        }

        private byte[] getClearImageCmd(int imageNumber)
        {
            byte[] command = { 0x1B, 0x49, 0x01, (byte)imageNumber };
            return command;
        }

        private byte[] getShowCursorCmd(int cursor)
        {
            byte[] command = { 0x1F, 0x43, (byte)cursor };
            return command;
        }

        private byte[] getCharactersetCmd(int characterset)
        {
            MPosDebug.WriteLine("BCDImplementation getCharactersetCmd : START", _DebugId);
            MPosDebug.WriteLine("BCDImplementation getCharactersetCmd : characterset = " + characterset, _DebugId);

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

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

            if (isMember != true)
            {
                MPosDebug.WriteLine("BCDImplementation 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_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_WPC1255: command[2] = 33; 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_WPC1256: command[2] = 40; break;
                case MPosCodePage.MPOS_CODEPAGE_WPC1258: command[2] = 41; 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;
                case MPosCodePage.MPOS_CODEPAGE_THAI42: command[2] = 23; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI14: command[2] = 31; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI11: command[2] = 34; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI18: command[2] = 35; break;
                case MPosCodePage.MPOS_CODEPAGE_THAI16: command[2] = 39; break;
                default:
                    command[2] = (byte)characterset;
                    break;
            }

            if (command != null)
                MPosDebug.WriteLine("BCDImplementation getCharactersetCmd : command[2] = " + command[2].ToString(), _DebugId);
            MPosDebug.WriteLine("BCDImplementation getCharactersetCmd : END", _DebugId);
            return command;
        }

        private async Task<byte[]> getPrintImageData(WriteableBitmap wb, int width, int num)
        {
            MPosDebug.WriteLine("BCDImplementation getPrintImageData : START");
            MPosDebug.WriteLine("BCDImplementation 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("BCDImplementation 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();
                }

                if (pixelImgData != null)
                    imgPrintCmd = MPosImagePrintCommand.getEscICmd(pixelImgData, imgSize[0], imgSize[1], num, 50, 0, false);

                pixelImgData = null;
            }

            MPosDebug.WriteLine("BCDImplementation getPrintImageData : END");
            return imgPrintCmd;
        }

        private async Task<byte[]> getPrintImageData(BitmapDecoder decoder, int width, int imageNumer)
        {
            byte[] printImageBytes = null;

            MPosDebug.WriteLine("BCDImplementation 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();

                printImageBytes = MPosImagePrintCommand.getEscICmd(pixelImg, resizeWidth, resizeHeight, imageNumer, 50, 0, false);
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("BCDImplementation getPrintImageData : ex = " + ex.Message);
            }

            MPosDebug.WriteLine("BCDImplementation getPrintImageData : END");
            return printImageBytes;
        }

        private byte[] getImageDisplayCmd(int nImageNumber, int posX, int posY)
        {
            byte[] command = { 0x1B, 0x49, 0x03, (byte)posX, (byte)posY, (byte)nImageNumber };
            return command;
        }
    }
}
