using System;
using WinRT.BxlComponent.Plugin.BxlMpXamarinSDK.Abstractions;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;
using System.IO;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage;
using System.Net.Http;


namespace WinRT.BxlComponent.Plugin.BxlMpXamarinSDK
{
    internal class MPosImagePrintCommand
    {
        private const string tempImgFileName = "tempImgFile";

        public static async Task<byte[]> getResizedPixelData(StorageFile file, Guid decoderId, int targetWidth, uint[] imageSize)
        {
            MPosDebug.WriteLine("getResizedPixelData : START");

            byte[] pixelData = null;

            try
            {
                using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
                {
                    UInt32 resizeWidth = 0;
                    UInt32 resizeHeight = 0;

                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(decoderId, stream);

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

                    imageSize[0] = resizeWidth;
                    imageSize[1] = resizeHeight;

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

                    MPosDebug.WriteLine("getResizedPixelData : decoder.PixelWidth = " + decoder.PixelWidth.ToString());
                    MPosDebug.WriteLine("getResizedPixelData : decoder.PixelHeight = " + decoder.PixelHeight.ToString());
                    MPosDebug.WriteLine("getResizedPixelData : resizeWidth = " + resizeWidth.ToString());
                    MPosDebug.WriteLine("getResizedPixelData : resizeHeight = " + resizeHeight.ToString());

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

                    pixelData = pixelDataProvider.DetachPixelData();
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("getResizedPixelData : Exception Code = 0x" + ex.HResult.ToString("X2"));
                MPosDebug.WriteLine("getResizedPixelData : Exception = " + ex.ToString());
            }

            MPosDebug.WriteLine("getResizedPixelData : END");
            return pixelData;
        }
        public static async Task<string> createImageFileFromURL(string uriString)
        {
            var fileName = "";

            MPosDebug.WriteLine("createImageFileFromURL : UriString = " + uriString);

            try
            {
                using (HttpClient client = new HttpClient())
                {
                    var streamImage = await client.GetStreamAsync(new Uri(uriString));
                    var charArray = uriString.ToCharArray();
                    Array.Reverse(charArray);
                    uriString = new string(charArray);

                    var splitUri = uriString.Split('/');
                    charArray = splitUri[0].ToCharArray();
                    Array.Reverse(charArray);
                    fileName = new string(charArray);

                    MPosDebug.WriteLine("createImageFileFromURL : fileName = " + fileName);
                    MPosDebug.WriteLine("createImageFileFromURL : TemporaryFolder.Path = " + ApplicationData.Current.TemporaryFolder.Path);

                    StorageFile storageFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);

                    using (Stream outputStream = await storageFile.OpenStreamForWriteAsync())
                    {
                        await streamImage.CopyToAsync(outputStream);
                    }
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("createImageFileFromURL : ex = " + ex.ToString());
                return "";
            }

            return ApplicationData.Current.TemporaryFolder.Path + "\\" + fileName;
        }
        public static async Task<bool> deleteTempImageFile()
        {
            try
            {
                MPosDebug.WriteLine("deleteTempImageFile : TemporaryFolder.Path = " + ApplicationData.Current.TemporaryFolder.Path);
                StorageFile file = await ApplicationData.Current.TemporaryFolder.GetFileAsync(tempImgFileName);
                if (file != null)
                {
                    await file.DeleteAsync();
                    return true;
                }
            }
            catch (Exception)
            {

            }
            return false;
        }
        public static async Task<StorageFile> writeableBitmapToStorageFile(WriteableBitmap WB, Guid encoderId)
        {
            MPosDebug.WriteLine("writeableBitmapToStorageFile : START");
            MPosDebug.WriteLine("writeableBitmapToStorageFile : encoderId = " + encoderId.ToString());

            StorageFile file = null;
            Guid BitmapEncoderGuid = encoderId;
            string FileName = tempImgFileName;

            try
            {
                MPosDebug.WriteLine("writeableBitmapToStorageFile : TemporaryFolder.Path = " + ApplicationData.Current.TemporaryFolder.Path);

                file = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(FileName, CreationCollisionOption.ReplaceExisting);

                using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                {
                    MPosDebug.WriteLine("writeableBitmapToStorageFile : WB.PixelWidth = " + WB.PixelWidth.ToString());
                    MPosDebug.WriteLine("writeableBitmapToStorageFile : WB.PixelHeight = " + WB.PixelHeight.ToString());

                    BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream);
                    Stream pixelStream = WB.PixelBuffer.AsStream();
                    byte[] pixels = new byte[pixelStream.Length];

                    await pixelStream.ReadAsync(pixels, 0, pixels.Length);

                    encoder.SetPixelData(BitmapPixelFormat.Bgra8,
                                            BitmapAlphaMode.Ignore,
                                            (uint)WB.PixelWidth,
                                            (uint)WB.PixelHeight,
                                            96.0,  // dpiX
                                            96.0,  // dpiY
                                            pixels);
                    await encoder.FlushAsync();
                }
            }
            catch (Exception ex)
            {
                MPosDebug.WriteLine("writeableBitmapToStorageFile : Exception Code = 0x" + ex.HResult.ToString("X2"));
                MPosDebug.WriteLine("writeableBitmapToStorageFile : Exception = " + ex.ToString());
                file = null;
            }

            MPosDebug.WriteLine("writeableBitmapToStorageFile : END");
            return file;
        }

        public static uint compressData(byte byCode, byte[] pSourceData, uint dwCurrentPos, uint dwRemainDataLength)
        {
            uint dwCount = 0;
            uint dwIndex = 0;

            //MPosDebug.WriteLine("compressData : START");

            for (dwIndex = dwCurrentPos; dwIndex < dwRemainDataLength; dwIndex++)
            {
                if (pSourceData[dwIndex] == byCode)
                {
                    dwCount += 1;

                    if (dwCount == 255)
                    {
                        return dwCount;
                    }
                }
                else
                {
                    break;
                }
            }

            //MPosDebug.WriteLine("compressData : END");
            return dwCount;
        }
        private static double Round(double value)
        {
            return (value + (value > 0 ? .5 : -.5));
        }
        private static void applyDitherAlgorithm(byte[] pixelImg, uint width, uint height, int threshold = 128)
        {
            MPosDebug.WriteLine("applyDitherAlgorithm : START");
            MPosDebug.WriteLine("applyDitherAlgorithm : width = " + width.ToString() + ", height = " + height.ToString());

            double nbrightness = 15.0;
            long bx = width;
            long by = height;

            // brightness
            for (int k = 0; k < by; k++)
            {
                for (int l = 0; l < bx; l++)
                {
                    int b = pixelImg[4 * (l + bx * k) + 0];
                    int g = pixelImg[4 * (l + bx * k) + 1];
                    int r = pixelImg[4 * (l + bx * k) + 2];

                    b = (int)(pixelImg[4 * (l + bx * k) + 0] + (255.00 * nbrightness / 100.00));
                    g = (int)(pixelImg[4 * (l + bx * k) + 1] + (255.00 * nbrightness / 100.00));
                    r = (int)(pixelImg[4 * (l + bx * k) + 2] + (255.00 * nbrightness / 100.00));

                    pixelImg[4 * (l + bx * k) + 0] = (byte)((b > 255) ? 255 : b);
                    pixelImg[4 * (l + bx * k) + 1] = (byte)((g > 255) ? 255 : g);
                    pixelImg[4 * (l + bx * k) + 2] = (byte)((r > 255) ? 255 : r);
                }
            }

            // Gray Scale
            float gray = 0;
            for (int i = 0; i < by; i++)
            {
                for (int j = 0; j < bx; j++)
                {
                    byte b = pixelImg[4 * (j + bx * i) + 0];
                    byte g = pixelImg[4 * (j + bx * i) + 1];
                    byte r = pixelImg[4 * (j + bx * i) + 2];
                    gray = (b + g + r) / 3;

                    pixelImg[4 * (j + bx * i) + 0] = (byte)MPosImagePrintCommand.Round(gray);
                    pixelImg[4 * (j + bx * i) + 1] = (byte)MPosImagePrintCommand.Round(gray);
                    pixelImg[4 * (j + bx * i) + 2] = (byte)MPosImagePrintCommand.Round(gray);
                }
            }

            // Error Diffusion dithering
            long nearest = 0;
            long quant = 0;
            float blue = 0;
            float temp = 0;
            //long z = 0;

            for (int y = 0; y < by; y++)
            {
                for (int x = 0; x < bx; x++)
                {
                    int z = 0;

                    long b = pixelImg[4 * (x + bx * y) + z];

                    //if (b < 128) 
                    //2020.11.25 threshold 적용
                    if (b < threshold)
                        nearest = 0;
                    else
                        nearest = 255;

                    quant = b - nearest;

                    pixelImg[4 * (x + bx * y) + z] = (byte)nearest;

                    if (x + 1 < bx) // y, x+1
                    {
                        blue = (float)0.4375 * quant;
                        temp = pixelImg[4 * ((x + 1) + bx * y) + z] + blue;
                        if (temp < 0)
                            pixelImg[4 * ((x + 1) + bx * y) + z] = 0;
                        else if (temp >= 0 && temp < 255)
                            pixelImg[4 * ((x + 1) + bx * y) + z] = (byte)MPosImagePrintCommand.Round(temp);
                        else
                            pixelImg[4 * ((x + 1) + bx * y) + z] = 255;
                    }

                    if (y + 1 < by && x - 1 > 0)
                    {
                        blue = (float)0.1875 * quant;
                        temp = pixelImg[4 * ((x - 1) + bx * (y + 1)) + z] + blue;
                        if (temp < 0)
                            pixelImg[4 * ((x - 1) + bx * (y + 1)) + z] = 0;
                        else if (temp >= 0 && temp < 255)
                            pixelImg[4 * ((x - 1) + bx * (y + 1)) + z] = (byte)MPosImagePrintCommand.Round(temp);
                        else
                            pixelImg[4 * ((x - 1) + bx * (y + 1)) + z] = 255;
                    }

                    if (y + 1 < by)
                    {
                        blue = (float)0.3125 * quant;
                        temp = pixelImg[4 * (x + bx * (y + 1)) + z] + blue;
                        if (temp < 0)
                            pixelImg[4 * (x + bx * (y + 1)) + z] = 0;
                        else if (temp >= 0 && temp < 255)
                            pixelImg[4 * (x + bx * (y + 1)) + z] = (byte)MPosImagePrintCommand.Round(temp);
                        else
                            pixelImg[4 * (x + bx * (y + 1)) + z] = 255;
                    }

                    if (y + 1 < by && x + 1 < bx)
                    {
                        blue = (float)0.0625 * quant;
                        temp = pixelImg[4 * ((x + 1) + bx * (y + 1)) + z] + blue;
                        if (temp < 0)
                            pixelImg[4 * ((x + 1) + bx * (y + 1)) + z] = 0;
                        else if (temp >= 0 && temp < 255)
                            pixelImg[4 * ((x + 1) + bx * (y + 1)) + z] = (byte)MPosImagePrintCommand.Round(temp);
                        else
                            pixelImg[4 * ((x + 1) + bx * (y + 1)) + z] = 255;
                    }

                    pixelImg[4 * (x + bx * y) + z] = (byte)nearest;
                    pixelImg[4 * (x + bx * y) + 1] = (byte)nearest;
                    pixelImg[4 * (x + bx * y) + 2] = (byte)nearest;
                }
            }

            MPosDebug.WriteLine("applyDitherAlgorithm : END");
        }
        private static byte[] getDitherImage(byte[] pixelImg, uint width, uint height, uint brightnessLevel, bool isDithering = true, int threshold = 128)
        {
            uint index = 0;
            uint bx_byte = 0;
            uint bx = width;
            uint by = height;
            uint imgBufIndex = 0;
            byte data = 0;
            byte shift = 0;
            byte[] imgBuffer = null;

            MPosDebug.WriteLine("getDitherImage : START");
            MPosDebug.WriteLine("getDitherImage : width = " + width.ToString() + ", height = " + height.ToString());
            MPosDebug.WriteLine("getDitherImage : brightnessLevel = " + brightnessLevel.ToString() + ", isDithering = " + isDithering.ToString());

            if (isDithering)
            {
                applyDitherAlgorithm(pixelImg, width, height, threshold);
            }

            brightnessLevel = (brightnessLevel * 255) / 100;
            MPosDebug.WriteLine("getDitherImage : brightnessLevel = " + brightnessLevel.ToString());

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);
            imgBuffer = new byte[by * bx_byte];

            for (uint y = 0; y < by; y++)
            {
                uint count = 0;

                for (uint x = 0; x < bx; x++)
                {
                    index = 4 * (x + bx * y);

                    if (pixelImg[index + 0] > brightnessLevel &&
                        pixelImg[index + 1] > brightnessLevel &&
                        pixelImg[index + 2] > brightnessLevel)
                    {
                        data = 0;
                    }
                    else
                    {
                        data = 1;
                    }

                    shift = (byte)(7 - (x % 8));
                    imgBuffer[imgBufIndex] += (byte)(data << shift);

                    if (count == 7)
                    {
                        imgBufIndex++;
                        count = 0;
                    }
                    else
                    {
                        count++;
                    }
                }

                if (count != 0)
                    imgBufIndex++;
            }

            MPosDebug.WriteLine("getDitherImage : imgBuffer length = " + imgBuffer.Length.ToString());
            MPosDebug.WriteLine("getDitherImage : END");
            return imgBuffer;
        }
        public static byte[] getLabelLDCmd(byte[] pixelImg, uint xPos, uint yPos, uint width, uint height, uint brightnessLevel, bool isDithering = true, int threshold = 128)
        {
            uint bx = width;
            uint by = height;
            uint bx_byte = 0;
            uint bufIndex = 0;
            byte[] imgBuffer = null;
            byte[] outBuffer = null;

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);

            MPosDebug.WriteLine("getLabelLDCmd : START");
            MPosDebug.WriteLine("getLabelLDCmd : xPos = " + xPos.ToString() + ", yPos = " + yPos.ToString());
            MPosDebug.WriteLine("getLabelLDCmd : width = " + width.ToString() + ", height = " + height.ToString());

            imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);

            MPosDebug.WriteLine("getLabelLDCmd : imgBuffer.Length = " + imgBuffer.Length.ToString());

            if (imgBuffer == null)
            {
                MPosDebug.WriteLine("getLabelLDCmd : END");
                return null;
            }

            if (xPos < 0) xPos = 0;
            if (yPos < 0) yPos = 0;

            outBuffer = new byte[imgBuffer.Length + 10];

            outBuffer[bufIndex++] = (byte)'L';
            outBuffer[bufIndex++] = (byte)'D';
            outBuffer[bufIndex++] = (byte)(xPos % 256);
            outBuffer[bufIndex++] = (byte)(xPos / 256);
            outBuffer[bufIndex++] = (byte)(yPos % 256);
            outBuffer[bufIndex++] = (byte)(yPos / 256);
            outBuffer[bufIndex++] = (byte)(bx_byte % 256);
            outBuffer[bufIndex++] = (byte)(bx_byte / 256);
            outBuffer[bufIndex++] = (byte)(by % 256);
            outBuffer[bufIndex++] = (byte)(by / 256);

            for (uint index = 0; index < imgBuffer.Length; index++)
            {
                outBuffer[bufIndex++] = imgBuffer[index];
            }

            byte[] newBuffer = new byte[bufIndex];
            System.Buffer.BlockCopy(outBuffer, 0, newBuffer, 0, (int)bufIndex);
            outBuffer = newBuffer;

            MPosDebug.WriteLine("getLabelLDCmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getLabelLDCmd : END");
            return outBuffer;
        }

        public static byte[] getLabelRleCmd(byte[] pixelImg, uint xPos, uint yPos, uint width, uint height, uint brightnessLevel, bool isDithering = true, int threshold = 128)
        {
            uint bx = width;
            uint by = height;
            uint bx_byte = 0;
            uint bufIndex = 0;
            byte[] imgBuffer = null;
            byte[] outBuffer = null;

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);

            MPosDebug.WriteLine("getLabelRleCmd : START");
            MPosDebug.WriteLine("getLabelRleCmd : xPos = " + xPos.ToString() + ", yPos = " + yPos.ToString());
            MPosDebug.WriteLine("getLabelRleCmd : width = " + width.ToString() + ", height = " + height.ToString());

            imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);

            MPosDebug.WriteLine("getLabelRleCmd : imgBuffer.Length = " + imgBuffer.Length.ToString());

            if (imgBuffer == null)
            {
                MPosDebug.WriteLine("getLabelRleCmd : END");
                return null;
            }

            if (xPos < 0) xPos = 0;
            if (yPos < 0) yPos = 0;

            outBuffer = new byte[imgBuffer.Length + 12];

            outBuffer[bufIndex++] = (byte)'L';
            outBuffer[bufIndex++] = (byte)'C';
            outBuffer[bufIndex++] = (byte)'R';
            outBuffer[bufIndex++] = 0x00;
            outBuffer[bufIndex++] = (byte)(xPos % 256);
            outBuffer[bufIndex++] = (byte)(xPos / 256);
            outBuffer[bufIndex++] = (byte)(yPos % 256);
            outBuffer[bufIndex++] = (byte)(yPos / 256);
            outBuffer[bufIndex++] = (byte)(bx_byte % 256);
            outBuffer[bufIndex++] = (byte)(bx_byte / 256);
            outBuffer[bufIndex++] = (byte)(by % 256);
            outBuffer[bufIndex++] = (byte)(by / 256);

            for (uint index = 0; index < imgBuffer.Length; index++)
            {
                if (imgBuffer[index] == 0x00 || imgBuffer[index] == 0xFF)
                {
                    var count = compressData(imgBuffer[index], imgBuffer, index, (uint)imgBuffer.Length);

                    if (count == 1)
                    {
                        byte[] bufIncrease = new byte[outBuffer.Length + 1];
                        System.Buffer.BlockCopy(outBuffer, 0, bufIncrease, 0, outBuffer.Length);
                        outBuffer = bufIncrease;
                    }

                    outBuffer[bufIndex++] = imgBuffer[index];
                    outBuffer[bufIndex++] = (byte)count;
                    index += (count - 1);
                }
                else
                {
                    outBuffer[bufIndex++] = imgBuffer[index];
                }
            }

            byte[] newBuffer = new byte[bufIndex];
            System.Buffer.BlockCopy(outBuffer, 0, newBuffer, 0, (int)bufIndex);
            outBuffer = newBuffer;

            MPosDebug.WriteLine("getLabelRleCmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getLabelRleCmd : END");
            return outBuffer;
        }
        public static byte[] getLabelLZMACmd(byte[] pixelImg, uint xPos, uint yPos, uint width, uint height, uint brightnessLevel, bool isDithering = true, int threshold = 128)
        {
            uint bx = width;
            uint by = height;
            uint bx_byte = 0;
            uint bufIndex = 0;
            byte[] imgBuffer = null;
            byte[] outBuffer = null;

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);

            MPosDebug.WriteLine("getLabelLZMACmd : START");
            MPosDebug.WriteLine("getLabelLZMACmd : xPos = " + xPos.ToString() + ", yPos = " + yPos.ToString());
            MPosDebug.WriteLine("getLabelLZMACmd : width = " + width.ToString() + ", height = " + height.ToString());

            imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);

            MPosDebug.WriteLine("getLabelLZMACmd : imgBuffer.Length = " + imgBuffer.Length.ToString());

            if (imgBuffer == null)
            {
                MPosDebug.WriteLine("getLabelLZMACmd : END");
                return null;
            }

            if (xPos < 0) xPos = 0;
            if (yPos < 0) yPos = 0;

            outBuffer = new byte[imgBuffer.Length + 15];

            outBuffer[bufIndex++] = (byte)'L';
            outBuffer[bufIndex++] = (byte)'C';
            outBuffer[bufIndex++] = (byte)'L';
            outBuffer[bufIndex++] = 0x00;
            outBuffer[bufIndex++] = (byte)(xPos % 256);
            outBuffer[bufIndex++] = (byte)(xPos / 256);
            outBuffer[bufIndex++] = (byte)(yPos % 256);
            outBuffer[bufIndex++] = (byte)(yPos / 256);
            outBuffer[bufIndex++] = (byte)(bx_byte % 256);
            outBuffer[bufIndex++] = (byte)(bx_byte / 256);
            outBuffer[bufIndex++] = (byte)(by % 256);
            outBuffer[bufIndex++] = (byte)(by / 256);

            //LZMA
            byte[] Compressed = SevenZip.Compression.LZMA.SevenZipHelper.Compress(imgBuffer);
            if (imgBuffer.Length < Compressed.Length)
            {
                byte[] reBuffer = new byte[Compressed.Length + 15];
                System.Buffer.BlockCopy(outBuffer, 0, reBuffer, 0, (int)bufIndex);
                outBuffer = reBuffer;
            }

            outBuffer[bufIndex++] = (byte)(Compressed.Length % 256);
            outBuffer[bufIndex++] = (byte)(Compressed.Length / 256);
            outBuffer[bufIndex++] = (byte)(Compressed.Length / 65536);

            // Copy
            System.Buffer.BlockCopy(Compressed, 0, outBuffer, (int)bufIndex, Compressed.Length);
            bufIndex += (uint)Compressed.Length;

            byte[] newBuffer = new byte[bufIndex];
            System.Buffer.BlockCopy(outBuffer, 0, newBuffer, 0, (int)bufIndex);
            outBuffer = newBuffer;

            MPosDebug.WriteLine("getLabelLZMACmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getLabelLZMACmd : END");
            return outBuffer;
        }
        public static byte[] getRleCmd(byte[] pixelImg, uint width, uint height, uint brightnessLevel, int alignment, bool isDithering = true, int threshold = 128)
        {
            uint MAX_HEIGHT = 100; //246;
            uint BUFF_CMD_LENGTH = 12;
            uint PRT_CMD_LENGTH = 5;
            uint bufferSize = 0;
            uint bx_byte = 0;
            uint bx = width;
            uint by = height;
            uint bufferCount = 0;
            uint divideLine = MAX_HEIGHT;
            uint by_divide = by / divideLine + (uint)((by % divideLine == 0) ? 0 : 1);

            MPosDebug.WriteLine("getRleCmd : START");
            MPosDebug.WriteLine("getRleCmd : width = " + width.ToString() + ", height = " + height.ToString());
            MPosDebug.WriteLine("getRleCmd : brightnessLevel = " + brightnessLevel.ToString() + ", alignment = " + alignment.ToString());

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);
            bufferSize = ((BUFF_CMD_LENGTH + PRT_CMD_LENGTH) * by_divide) + bx_byte * by;

            uint lRestLine = by;
            uint lStartY = 0;
            byte[] buffer = new byte[bufferSize];
            var imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);
            var imgBufferCount = imgBuffer.Length;

            while (lRestLine > 0)
            {
                uint lHeight = lRestLine > MAX_HEIGHT ? MAX_HEIGHT : lRestLine;

                buffer[bufferCount++] = 0x08;                               // BS
                buffer[bufferCount++] = 0x43;                               // C
                buffer[bufferCount++] = 0x52;                               // p1 : Type of Compression : R
                buffer[bufferCount++] = 0x00;                               // p2 : Color Mono:0x00, MultiTone:~0x04
                buffer[bufferCount++] = 0x00;                               // p3
                buffer[bufferCount++] = 0x00;                               // p4
                buffer[bufferCount++] = 0x00;                               // p5
                buffer[bufferCount++] = 0x00;                               // p6
                buffer[bufferCount++] = (byte)((bx_byte * 8) % 256);        // xL
                buffer[bufferCount++] = (byte)((bx_byte * 8) / 256);        // xH
                buffer[bufferCount++] = (byte)(lHeight % 256);              // yL
                buffer[bufferCount++] = (byte)(lHeight / 256);				// yH

                uint lLength = bx_byte * (lStartY + lHeight);
                uint count = 0;

                for (uint nn = (lStartY * bx_byte); nn < lLength; nn++)
                {
                    byte btVal = imgBuffer[nn];

                    if (btVal == 0x00 || btVal == 0xFF)
                    {
                        count = compressData(btVal, imgBuffer, nn, lLength);
                        if (count == 1)
                        {
                            byte[] newBuffer = new byte[buffer.Length + 1];
                            System.Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
                            buffer = newBuffer;
                        }
                        buffer[bufferCount++] = btVal;
                        buffer[bufferCount++] = (byte)count;
                        nn += (count - 1);
                    }
                    else
                    {
                        buffer[bufferCount++] = btVal;
                    }
                }

                lRestLine -= lHeight;
                lStartY += lHeight;

                buffer[bufferCount++] = 0x08;       // BS
                buffer[bufferCount++] = 0x43;       // C
                buffer[bufferCount++] = 0x52;       // p1 : Type of Compression
                buffer[bufferCount++] = 0xFF;	    // p2 : Color
                buffer[bufferCount++] = 0x00;		// p3 :
            }

            byte[] alignmentCmd = new byte[] { 0x1b, 0x61, 0x00 };
            byte[] outBuffer = new byte[bufferCount + alignmentCmd.Length];

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

            System.Buffer.BlockCopy(alignmentCmd, 0, outBuffer, 0, alignmentCmd.Length);
            System.Buffer.BlockCopy(buffer, 0, outBuffer, alignmentCmd.Length, (int)bufferCount);

            MPosDebug.WriteLine("getRleCmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getRleCmd : END");
            return outBuffer;
        }
        public static byte[] getEscStarCmd(byte[] pixelImg, uint width, uint height, uint brightnessLevel, int alignment, bool isDithering = true)
        {
            uint by_byte = 0;
            uint bx = width;
            uint by = height;
            uint imgBufIndex = 0;
            uint bufferSize = 0;
            byte[] imgBufWithCmd = null;

            MPosDebug.WriteLine("getEscStarCmd : START");
            MPosDebug.WriteLine("getEscStarCmd : width = " + width.ToString() + ", height = " + height.ToString());
            MPosDebug.WriteLine("getEscStarCmd : brightnessLevel = " + brightnessLevel.ToString() + ", isDithering = " + isDithering.ToString());

            if (isDithering)
            {
                applyDitherAlgorithm(pixelImg, width, height);
            }

            brightnessLevel = (brightnessLevel * 255) / 100;
            MPosDebug.WriteLine("getEscStarCmd : brightnessLevel = " + brightnessLevel.ToString());

            by_byte = (by / 8) + (uint)((by % 8 == 0) ? 0 : 1);
            bufferSize = (11 * by_byte) + (bx * by_byte);
            imgBufWithCmd = new byte[bufferSize];

            for (uint y = 0; y < by; y += 8)
            {
                imgBufWithCmd[imgBufIndex++] = 0x1B;       // ESC
                imgBufWithCmd[imgBufIndex++] = 0x72;       // r
                imgBufWithCmd[imgBufIndex++] = 0x00;       // Black is selected
                imgBufWithCmd[imgBufIndex++] = 0x1B;       // ESC
                imgBufWithCmd[imgBufIndex++] = 0x2A;       // *
                imgBufWithCmd[imgBufIndex++] = 0x01;       // 8-dot single-density
                imgBufWithCmd[imgBufIndex++] = (byte)(bx % 256);
                imgBufWithCmd[imgBufIndex++] = (byte)(bx / 256);

                for (uint x = 0; x < bx; x++)
                {
                    byte byBitImgData = 0x00;
                    byte byShift = 128;
                    byte color = 0;

                    for (int i = 0; i < 8; i++)
                    {
                        if (4 * (x + bx * (y + i)) < (4 * bx * by))
                        {
                            // RGB + Alpha
                            byte b = pixelImg[4 * (x + bx * (y + i)) + 0];
                            byte g = pixelImg[4 * (x + bx * (y + i)) + 1];
                            byte r = pixelImg[4 * (x + bx * (y + i)) + 2];

                            if (r > brightnessLevel && g > brightnessLevel && b > brightnessLevel)
                                color = 0;
                            else
                                color = 1;
                        }
                        byBitImgData |= (byte)(color * byShift);
                        byShift = (byte)(byShift >> 1);
                    }
                    imgBufWithCmd[imgBufIndex++] = byBitImgData;
                }
                // 8 dots feeds
                imgBufWithCmd[imgBufIndex++] = 0x1B;
                imgBufWithCmd[imgBufIndex++] = 0x4A;
                imgBufWithCmd[imgBufIndex++] = 0x08;
            }

            byte[] alignmentCmd = new byte[] { 0x1b, 0x61, 0x00 };
            byte[] outBuffer = new byte[imgBufIndex + alignmentCmd.Length];

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

            System.Buffer.BlockCopy(alignmentCmd, 0, outBuffer, 0, alignmentCmd.Length);
            System.Buffer.BlockCopy(imgBufWithCmd, 0, outBuffer, alignmentCmd.Length, (int)imgBufIndex);

            MPosDebug.WriteLine("getEscStarCmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getEscStarCmd : END");
            return outBuffer;
        }
        public static byte[] getGSv0Cmd(byte[] pixelImg, uint width, uint height, uint brightnessLevel, int alignment, bool isDithering = true, int threshold = 128)
        {
            uint MAX_HEIGHT = 100; //246;
            uint BUFF_CMD_LENGTH = 12;
            uint bufferSize = 0;
            uint bx_byte = 0;
            uint bx = width;
            uint by = height;
            uint bufferCount = 0;
            uint divideLine = MAX_HEIGHT;
            uint by_divide = by / divideLine + (uint)((by % divideLine == 0) ? 0 : 1);

            MPosDebug.WriteLine("getGSv0Cmd : START");
            MPosDebug.WriteLine("getGSv0Cmd : width = " + width.ToString() + ", height = " + height.ToString());
            MPosDebug.WriteLine("getGSv0Cmd : brightnessLevel = " + brightnessLevel.ToString() + ", alignment = " + alignment.ToString());

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);
            bufferSize = (BUFF_CMD_LENGTH * by_divide) + bx_byte * by;

            uint lRestLine = by;
            uint lStartY = 0;
            byte[] buffer = new byte[bufferSize];
            var imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);

            while (lRestLine > 0)
            {
                uint lHeight = lRestLine > MAX_HEIGHT ? MAX_HEIGHT : lRestLine;

                buffer[bufferCount++] = 0x1d;       // GS
                buffer[bufferCount++] = 0x76;       // v
                buffer[bufferCount++] = 0x30;       // 0
                buffer[bufferCount++] = 0x00;
                buffer[bufferCount++] = (byte)(bx_byte % 256);
                buffer[bufferCount++] = (byte)(bx_byte / 256);
                buffer[bufferCount++] = (byte)(lHeight % 256);
                buffer[bufferCount++] = (byte)(lHeight / 256);

                uint lLength = bx_byte * (lStartY + lHeight);

                for (uint nn = (lStartY * bx_byte); nn < lLength; nn++)
                {
                    buffer[bufferCount++] = imgBuffer[nn];
                }

                lRestLine -= lHeight;
                lStartY += lHeight;
            }

            byte[] alignmentCmd = new byte[] { 0x1b, 0x61, 0x00 };
            byte[] outBuffer = new byte[bufferCount + alignmentCmd.Length];

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

            System.Buffer.BlockCopy(alignmentCmd, 0, outBuffer, 0, alignmentCmd.Length);
            System.Buffer.BlockCopy(buffer, 0, outBuffer, alignmentCmd.Length, (int)bufferCount);

            MPosDebug.WriteLine("getGSv0Cmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getGSv0Cmd : END");
            return outBuffer;
        }
        public static byte[] getLZMACmd(byte[] pixelImg, uint width, uint height, uint brightnessLevel, int alignment, bool isDithering = true, int threshold = 128)
        {
            uint MAX_HEIGHT = 100; //246;
            uint BUFF_CMD_LENGTH = 24;  // GSV0 + LZMA + alignment
            //uint BUFF_CMD_LZMA = 12;
            uint bufferSize = 0;
            uint bx_byte = 0;
            uint bx = width;
            uint by = height;
            uint bufferCount = 0;
            uint divideLine = MAX_HEIGHT;
            uint by_divide = by / divideLine + (uint)((by % divideLine == 0) ? 0 : 1);

            MPosDebug.WriteLine("getLZMACmd : START");
            MPosDebug.WriteLine("getLZMACmd : width = " + width.ToString() + ", height = " + height.ToString());
            MPosDebug.WriteLine("getLZMACmd : brightnessLevel = " + brightnessLevel.ToString() + ", alignment = " + alignment.ToString());

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);
            bufferSize = (BUFF_CMD_LENGTH * by_divide) + bx_byte * by;

            uint lRestLine = by;
            uint lStartY = 0;
            byte[] buffer = new byte[bufferSize];
            var imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);

            while (lRestLine > 0)
            {
                uint lHeight = lRestLine > MAX_HEIGHT ? MAX_HEIGHT : lRestLine;

                int size = (int)(bx_byte * lHeight);
                byte[] newBuffer = new byte[size + 8];

                newBuffer[0] = 0x1d;       // GS
                newBuffer[1] = 0x76;       // v
                newBuffer[2] = 0x30;       // 0
                newBuffer[3] = 0x00;
                newBuffer[4] = (byte)(bx_byte % 256);
                newBuffer[8] = (byte)(bx_byte / 256);
                newBuffer[6] = (byte)(lHeight % 256);
                newBuffer[7] = (byte)(lHeight / 256);

                System.Buffer.BlockCopy(imgBuffer, (int)(lStartY * bx_byte), newBuffer, 8, size);
                byte[] Compressed = SevenZip.Compression.LZMA.SevenZipHelper.Compress(newBuffer);
                
                if ((bufferCount + Compressed.Length) > bufferSize)
                {
                    byte[] reBuffer = new byte[bufferCount + Compressed.Length];
                    System.Buffer.BlockCopy(buffer, 0, reBuffer, 0, buffer.Length);
                    buffer = reBuffer;
                }
                //Start LZMA
                buffer[bufferCount++] = 0x08;       // BS
                buffer[bufferCount++] = 0x43;       // C
                buffer[bufferCount++] = 0x4c;       // L
                buffer[bufferCount++] = 0x00;       // start
                buffer[bufferCount++] = 0x00;       // Type: 0
                buffer[bufferCount++] = (byte)(Compressed.Length % 256);
                buffer[bufferCount++] = (byte)(Compressed.Length / 256);
                buffer[bufferCount++] = (byte)(Compressed.Length / 65536);

                // Copy
                System.Buffer.BlockCopy(Compressed, 0, buffer, (int)bufferCount, Compressed.Length);
                bufferCount += (uint)Compressed.Length;

                //End LZMA
                buffer[bufferCount++] = 0x08;       // BS
                buffer[bufferCount++] = 0x43;       // C
                buffer[bufferCount++] = 0x4c;       // L
                buffer[bufferCount++] = 0xff;       // end

                lRestLine -= lHeight;
                lStartY += lHeight;
            }

            byte[] alignmentCmd = new byte[] { 0x1b, 0x61, 0x00 };
            byte[] outBuffer = new byte[bufferCount + alignmentCmd.Length];

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

            System.Buffer.BlockCopy(alignmentCmd, 0, outBuffer, 0, alignmentCmd.Length);
            System.Buffer.BlockCopy(buffer, 0, outBuffer, alignmentCmd.Length, (int)bufferCount);

            MPosDebug.WriteLine("getGSv0Cmd : outBuffer length = " + outBuffer.Length.ToString());
            MPosDebug.WriteLine("getGSv0Cmd : END");
            return outBuffer;
        }

        public static byte[] getEscICmd(byte[] pixelImg, uint width, uint height, int num, uint brightnessLevel, int alignment, bool isDithering = true, int threshold = 128)
        {
            uint MAX_HEIGHT = 100; //246;
            uint BUFF_CMD_LENGTH = 12;
            uint bufferSize = 0;
            uint bx_byte = 0;
            uint bx = width;
            uint by = height;
            uint bufferCount = 0;
            uint divideLine = MAX_HEIGHT;
            uint by_divide = by / divideLine + (uint)((by % divideLine == 0) ? 0 : 1);

            MPosDebug.WriteLine("getEscICmd : START");
            MPosDebug.WriteLine("getEscICmd : width = " + width.ToString() + ", height = " + height.ToString());
            MPosDebug.WriteLine("getEscICmd : brightnessLevel = " + brightnessLevel.ToString() + ", alignment = " + alignment.ToString());

            bx_byte = bx / 8 + (uint)((bx % 8 == 0) ? 0 : 1);
            bufferSize = (BUFF_CMD_LENGTH * by_divide) + bx_byte * by;

            uint lRestLine = by;
            uint lStartY = 0;
            byte[] buffer = new byte[bufferSize];
            var imgBuffer = getDitherImage(pixelImg, width, height, brightnessLevel, isDithering, threshold);

            while (lRestLine > 0)
            {
                uint lHeight = lRestLine > MAX_HEIGHT ? MAX_HEIGHT : lRestLine;

                buffer[bufferCount++] = 0x1b;       // ESC
                buffer[bufferCount++] = 0x49;       // I
                buffer[bufferCount++] = 0x00;       // 0
                buffer[bufferCount++] = (byte)num;
                buffer[bufferCount++] = (byte)(bx_byte % 256);
                buffer[bufferCount++] = (byte)(lHeight % 256);

                uint lLength = bx_byte * (lStartY + lHeight);
                for (uint nn = (lStartY * bx_byte); nn < lLength; nn++)
                {
                    buffer[bufferCount++] = imgBuffer[nn];
                }

                lRestLine -= lHeight;
                lStartY += lHeight;
            }

            MPosDebug.WriteLine("getEscICmd : outBuffer length = " + buffer.Length.ToString());
            MPosDebug.WriteLine("getEscICmd : END");
            return buffer;
        }
    }
}