package com.pinmi.react.printer.adapter;
import static com.pinmi.react.printer.adapter.UtilsImage.getPixelsSlow;
import static com.pinmi.react.printer.adapter.UtilsImage.recollectSlice;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.util.Base64;
import android.util.Log;
import android.os.Handler;
import android.os.Looper;

import com.facebook.common.internal.ImmutableMap;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.pinmi.react.printer.R;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import android.graphics.BitmapFactory;

import androidx.annotation.RequiresApi;

/**
 * Created by xiesubin on 2017/9/22.
 */

public class NetPrinterAdapter implements PrinterAdapter {
    private static NetPrinterAdapter mInstance;
    private ReactApplicationContext mContext;
    private final String LOG_TAG = "RNNetPrinter";
    private NetPrinterDevice mNetDevice;

    // {TODO- support other ports later}
    // private int[] PRINTER_ON_PORTS = {515, 3396, 9100, 9303};

    private final int[] PRINTER_ON_PORTS = {9100};
    private static final String EVENT_SCANNER_RESOLVED = "scannerResolved";
    private static final String EVENT_SCANNER_RUNNING = "scannerRunning";

    private final static char ESC_CHAR = 0x1B;
    private static final byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33};
    private final static byte[] SET_LINE_SPACE_24 = new byte[]{ESC_CHAR, 0x33, 24};
    private final static byte[] SET_LINE_SPACE_32 = new byte[]{ESC_CHAR, 0x33, 32};
    private final static byte[] LINE_FEED = new byte[]{0x0A};
    private static final byte[] CENTER_ALIGN = {0x1B, 0X61, 0X31};

    private Socket mSocket;

    private boolean isRunning = false;

    private NetPrinterAdapter() {

    }

    public static NetPrinterAdapter getInstance() {
        if (mInstance == null) {
            mInstance = new NetPrinterAdapter();

        }
        return mInstance;
    }

    @Override
    public void init(ReactApplicationContext reactContext, Callback successCallback, Callback errorCallback) {
        this.mContext = reactContext;
        successCallback.invoke();
    }

    @Override
    public void getDeviceListCallback(Callback successCallback, Callback errorCallback) {
        // errorCallback.invoke("do not need to invoke get device list for net
        // printer");
        // Use emitter instancee get devicelist to non block main thread
        this.scan(successCallback, errorCallback);
    }

    @Override
    public List<PrinterDevice> getDeviceList(Callback errorCallback) {
        List<PrinterDevice> printerDevices = new ArrayList<>();
        return printerDevices;
    }

    // concurrency
    // private void scan(final Callback successCallback, final Callback errorCallback) {
    // if (isRunning)
    //     return;

    //     new Thread(new Runnable() {
    //         @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    //         @Override
    //         public void run() {
    //             try {
    //                 isRunning = true;
    //                 emitEvent(EVENT_SCANNER_RUNNING, isRunning);

    //                 WifiManager wifiManager = (WifiManager) mContext.getApplicationContext()
    //                         .getSystemService(Context.WIFI_SERVICE);
    //                 String ipAddress = ipToString(wifiManager.getConnectionInfo().getIpAddress());

    //                 String prefix = ipAddress.substring(0, ipAddress.lastIndexOf('.') + 1);
    //                 int suffix = Integer
    //                         .parseInt(ipAddress.substring(ipAddress.lastIndexOf('.') + 1, ipAddress.length()));

    //                 // Use ExecutorService for concurrent scanning
    //                 ExecutorService executor = Executors.newFixedThreadPool(10); // Adjust the number of threads as needed
    //                 List<WritableMap> results = Collections.synchronizedList(new ArrayList<>()); // Thread-safe list to collect results

    //                 for (int i = 0; i <= 255; i++) {
    //                     if (i == suffix)
    //                         continue;

    //                     final String host = prefix + i;
    //                     executor.submit(new Runnable() {
    //                         @Override
    //                         public void run() {
    //                             try {
    //                                 ArrayList<Integer> ports = getAvailablePorts(host);
    //                                 if (!ports.isEmpty()) {
    //                                     WritableMap payload = Arguments.createMap();
    //                                     payload.putString("host", host);
    //                                     payload.putInt("port", 9100);
    //                                     results.add(payload); // Add to the thread-safe list
    //                                 }
    //                             } catch (Exception e) {
    //                                 Log.e(LOG_TAG, "Error getting available ports for " + host, e);
    //                             }
    //                         }
    //                     });
    //                 }

    //                 // Shutdown the executor and wait for tasks to finish
    //                 executor.shutdown();
    //                 if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
    //                     executor.shutdownNow(); // Force shutdown if tasks do not finish
    //                 }

    //                 // Create a single WritableArray to return
    //                 WritableArray array = Arguments.createArray();
    //                 for (WritableMap result : results) {
    //                     array.pushMap(result); // Add each result to the array
    //                 }

    //                 emitEvent(EVENT_SCANNER_RESOLVED, array);
    //                 successCallback.invoke(array);
    //             } catch (NullPointerException ex) {
    //                 Log.i(LOG_TAG, "No connection");
    //                 errorCallback.invoke(ex.getMessage());
    //             } catch (InterruptedException e) {
    //                 Thread.currentThread().interrupt();
    //                 errorCallback.invoke(e.getMessage());
    //             } finally {
    //                 isRunning = false;
    //                 emitEvent(EVENT_SCANNER_RUNNING, isRunning);
    //             }
    //         }
    //     }).start();
    // }

    private void scan(final Callback successCallback, final Callback errorCallback) {
        if (isRunning)
            return;
        new Thread(new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.KITKAT)
            @Override
            public void run() {
                try {
                    isRunning = true;
                    emitEvent(EVENT_SCANNER_RUNNING, isRunning);

                    WifiManager wifiManager = (WifiManager) mContext.getApplicationContext()
                            .getSystemService(Context.WIFI_SERVICE);
                    String ipAddress = ipToString(wifiManager.getConnectionInfo().getIpAddress());
                    WritableArray array = Arguments.createArray();

                    String prefix = ipAddress.substring(0, ipAddress.lastIndexOf('.') + 1);
                    int suffix = Integer
                            .parseInt(ipAddress.substring(ipAddress.lastIndexOf('.') + 1, ipAddress.length()));

                    for (int i = 0; i <= 255; i++) {
                        if (i == suffix)
                            continue;
                        ArrayList<Integer> ports = getAvailablePorts(prefix + i);
                        if (!ports.isEmpty()) {
                            WritableMap payload = Arguments.createMap();

                            payload.putString("host", prefix + i);
                            payload.putInt("port", 9100);

                            array.pushMap(payload);
                        }
                    }

                    emitEvent(EVENT_SCANNER_RESOLVED, array);

                    successCallback.invoke(array);
                } catch (NullPointerException ex) {
                    Log.i(LOG_TAG, "No connection");

                    errorCallback.invoke(ex.getMessage());
                } finally {
                    isRunning = false;
                    emitEvent(EVENT_SCANNER_RUNNING, isRunning);
                }
            }
        }).start();
    }

    private void emitEvent(String eventName, Object data) {
        if (mContext != null) {
            mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, data);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    private ArrayList<Integer> getAvailablePorts(String address) {
        ArrayList<Integer> ports = new ArrayList<>();
        for (int port : PRINTER_ON_PORTS) {
            if (crunchifyAddressReachable(address, port))
                ports.add(port);
        }
        return ports;
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    private static boolean crunchifyAddressReachable(String address, int port) {
        try {

            try (Socket crunchifySocket = new Socket()) {
                // Connects this socket to the server with a specified timeout value.
                crunchifySocket.connect(new InetSocketAddress(address, port), 100);
            }
            // Return true if connection successful
            return true;
        } catch (IOException exception) {
            exception.printStackTrace();
            return false;
        }
    }

    private String ipToString(int ip) {
        return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + ((ip >> 24) & 0xFF);
    }

    @Override
    public void selectDevice(PrinterDeviceId printerDeviceId, Callback sucessCallback, Callback errorCallback) {
        NetPrinterDeviceId netPrinterDeviceId = (NetPrinterDeviceId) printerDeviceId;

        if (this.mSocket != null && !this.mSocket.isClosed()
                && mNetDevice.getPrinterDeviceId().equals(netPrinterDeviceId)) {
            Log.i(LOG_TAG, "already selected device, do not need repeat to connect");
            sucessCallback.invoke(this.mNetDevice.toRNWritableMap());
            return;
        }

        // new Thread(new Runnable() {
        //     @Override
        //     public void run() {
        //         try {
        //             final Socket socket = new Socket(netPrinterDeviceId.getHost(), netPrinterDeviceId.getPort());
        //             if (socket.isConnected()) {
        //                 closeConnectionIfExists();
        //                 mSocket = socket;
        //                 mNetDevice = new NetPrinterDevice(netPrinterDeviceId.getHost(), netPrinterDeviceId.getPort());
        //                 new Handler(Looper.getMainLooper()).post(new Runnable() {
        //                     @Override
        //                     public void run() {
        //                         successCallback.invoke(mNetDevice.toRNWritableMap());
        //                     }
        //                 });
        //             } else {
        //                 throw new IOException("Unable to connect");
        //             }
        //         } catch (final IOException e) {
        //             e.printStackTrace();
        //             new Handler(Looper.getMainLooper()).post(new Runnable() {
        //                 @Override
        //                 public void run() {
        //                     errorCallback.invoke("failed to connect printer: " + e.getMessage());
        //                 }
        //             });
        //         }
        //     }
        // }).start();

        try {
            Socket socket = new Socket(netPrinterDeviceId.getHost(), netPrinterDeviceId.getPort());
            if (socket.isConnected()) {
                closeConnectionIfExists();
                this.mSocket = socket;
                this.mNetDevice = new NetPrinterDevice(netPrinterDeviceId.getHost(), netPrinterDeviceId.getPort());
                sucessCallback.invoke(this.mNetDevice.toRNWritableMap());
            } else {
                errorCallback.invoke("unable to build connection with host: " + netPrinterDeviceId.getHost()
                        + ", port: " + netPrinterDeviceId.getPort());
                return;
            }
        } catch (IOException e) {
            e.printStackTrace();
            errorCallback.invoke("failed to connect printer: " + e.getMessage());
        }
    }

    @Override
    public void closeConnectionIfExists() {
        if (this.mSocket != null) {
            if (!this.mSocket.isClosed()) {
                try {
                    this.mSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            this.mSocket = null;

        }
    }

    @Override
    public void printRawData(String rawBase64Data, Callback errorCallback) {
        // if (this.mSocket == null) {
        //     errorCallback.invoke("bluetooth connection is not built, may be you forgot to connectPrinter");
        //     return;
        // }
        // final String rawData = rawBase64Data;
        // final Socket socket = this.mSocket;
        // Log.v(LOG_TAG, "start to print raw data " + rawBase64Data);
        // try {
        //     byte[] bytes = Base64.decode(rawData, Base64.DEFAULT);
        //     OutputStream printerOutputStream = socket.getOutputStream();
        //     printerOutputStream.write(bytes, 0, bytes.length);
        //     printerOutputStream.flush();
        // } catch (IOException e) {
        //     Log.e(LOG_TAG, "failed to print data" + rawData);
        //     e.printStackTrace();
        // }

        if (this.mSocket == null) {
            errorCallback.invoke("bluetooth connection is not built, may be you forgot to connectPrinter");
            return;
        }
        final String rawData = rawBase64Data;
        final Socket socket = this.mSocket;
        Log.v(LOG_TAG, "start to print raw data " + rawBase64Data);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    byte[] bytes = Base64.decode(rawData, Base64.DEFAULT);
                    OutputStream printerOutputStream = socket.getOutputStream();
                    printerOutputStream.write(bytes, 0, bytes.length);
                    printerOutputStream.flush();
                } catch (IOException e) {
                    Log.e(LOG_TAG, "failed to print data" + rawData);
                    e.printStackTrace();
                }
            }
        }).start();

    }

    @Override
    public void printRawDataAsync(String rawBase64Data, Callback errorCallback) {
        if (this.mSocket == null) {
            errorCallback.invoke("bluetooth connection is not built, may be you forgot to connectPrinter");
            return;
        }
        final String rawData = rawBase64Data;
        final Socket socket = this.mSocket;
        Log.v(LOG_TAG, "start to print raw data " + rawBase64Data);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    byte[] bytes = Base64.decode(rawData, Base64.DEFAULT);
                    OutputStream printerOutputStream = socket.getOutputStream();
                    printerOutputStream.write(bytes, 0, bytes.length);
                    printerOutputStream.flush();
                } catch (IOException e) {
                    Log.e(LOG_TAG, "failed to print data" + rawData);
                    e.printStackTrace();
                }
            }
        }).start();

    }

    public static Bitmap getBitmapFromURL(String src) {
        try {
            URL url = new URL(src);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream input = connection.getInputStream();
            Bitmap myBitmap = BitmapFactory.decodeStream(input);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            myBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);

            return myBitmap;
        } catch (IOException e) {
            // Log exception
            return null;
        }
    }


    @Override
    public void printImageData(final String imageUrl, int imageWidth, int imageHeight, Callback errorCallback) {
        final Bitmap bitmapImage = getBitmapFromURL(imageUrl);

        if (bitmapImage == null) {
            errorCallback.invoke("image not found");
            return;
        }

        if (this.mSocket == null) {
            errorCallback.invoke("Net connection is not built, may be you forgot to connectPrinter");
            return;
        }

        final Socket socket = this.mSocket;

        try {
            int[][] pixels = getPixelsSlow(bitmapImage, imageWidth, imageHeight);

            OutputStream printerOutputStream = socket.getOutputStream();

            printerOutputStream.write(SET_LINE_SPACE_24);
            printerOutputStream.write(CENTER_ALIGN);

            for (int y = 0; y < pixels.length; y += 24) {
                // Like I said before, when done sending data,
                // the printer will resume to normal text printing
                printerOutputStream.write(SELECT_BIT_IMAGE_MODE);
                // Set nL and nH based on the width of the image
                printerOutputStream.write(new byte[]{(byte) (0x00ff & pixels[y].length)
                        , (byte) ((0xff00 & pixels[y].length) >> 8)});
                for (int x = 0; x < pixels[y].length; x++) {
                    // for each stripe, recollect 3 bytes (3 bytes = 24 bits)
                    printerOutputStream.write(recollectSlice(y, x, pixels));
                }

                // Do a line feed, if not the printing will resume on the same line
                printerOutputStream.write(LINE_FEED);
            }
            printerOutputStream.write(SET_LINE_SPACE_32);
            printerOutputStream.write(LINE_FEED);

            printerOutputStream.flush();
        } catch (IOException e) {
            Log.e(LOG_TAG, "failed to print data");
            e.printStackTrace();
        }

        // new Thread(new Runnable() {
        //     @Override
        //     public void run() {
        //         try {
        //             int[][] pixels = getPixelsSlow(bitmapImage);

        //             OutputStream printerOutputStream = socket.getOutputStream();

        //             printerOutputStream.write(SET_LINE_SPACE_24);
        //             printerOutputStream.write(CENTER_ALIGN);

        //             for (int y = 0; y < pixels.length; y += 24) {
        //                 // Like I said before, when done sending data,
        //                 // the printer will resume to normal text printing
        //                 printerOutputStream.write(SELECT_BIT_IMAGE_MODE);
        //                 // Set nL and nH based on the width of the image
        //                 printerOutputStream.write(new byte[]{(byte)(0x00ff & pixels[y].length)
        //                         , (byte)((0xff00 & pixels[y].length) >> 8)});
        //                 for (int x = 0; x < pixels[y].length; x++) {
        //                     // for each stripe, recollect 3 bytes (3 bytes = 24 bits)
        //                     printerOutputStream.write(recollectSlice(y, x, pixels));
        //                 }

        //                 // Do a line feed, if not the printing will resume on the same line
        //                 printerOutputStream.write(LINE_FEED);
        //             }
        //             printerOutputStream.write(SET_LINE_SPACE_32);
        //             printerOutputStream.write(LINE_FEED);

        //             printerOutputStream.flush();
        //         } catch (IOException e) {
        //             Log.e(LOG_TAG, "failed to print data");
        //             e.printStackTrace();
        //         }
        //     }
        // }).start();
    }

    @Override
    public void printImageBase64(final Bitmap bitmapImage, int imageWidth, int imageHeight, Callback errorCallback) {
        if (bitmapImage == null) {
            errorCallback.invoke("image not found");
            return;
        }

        if (this.mSocket == null) {
            errorCallback.invoke("Net connection is not built, may be you forgot to connectPrinter");
            return;
        }

        final Socket socket = this.mSocket;

        try {
            int[][] pixels = getPixelsSlow(bitmapImage, imageWidth, imageHeight);

            OutputStream printerOutputStream = socket.getOutputStream();

            printerOutputStream.write(SET_LINE_SPACE_24);
            printerOutputStream.write(CENTER_ALIGN);

            for (int y = 0; y < pixels.length; y += 24) {
                // Like I said before, when done sending data,
                // the printer will resume to normal text printing
                printerOutputStream.write(SELECT_BIT_IMAGE_MODE);
                // Set nL and nH based on the width of the image
                printerOutputStream.write(new byte[]{(byte) (0x00ff & pixels[y].length)
                        , (byte) ((0xff00 & pixels[y].length) >> 8)});
                for (int x = 0; x < pixels[y].length; x++) {
                    // for each stripe, recollect 3 bytes (3 bytes = 24 bits)
                    printerOutputStream.write(recollectSlice(y, x, pixels));
                }

                // Do a line feed, if not the printing will resume on the same line
                printerOutputStream.write(LINE_FEED);
            }
            printerOutputStream.write(SET_LINE_SPACE_32);
            printerOutputStream.write(LINE_FEED);

            printerOutputStream.flush();
        } catch (IOException e) {
            Log.e(LOG_TAG, "failed to print data");
            e.printStackTrace();
        }

        // new Thread(new Runnable() {
        //     @Override
        //     public void run() {
        //         try {
        //             int[][] pixels = getPixelsSlow(bitmapImage);

        //             OutputStream printerOutputStream = socket.getOutputStream();

        //             printerOutputStream.write(SET_LINE_SPACE_24);
        //             printerOutputStream.write(CENTER_ALIGN);

        //             for (int y = 0; y < pixels.length; y += 24) {
        //                 // Like I said before, when done sending data,
        //                 // the printer will resume to normal text printing
        //                 printerOutputStream.write(SELECT_BIT_IMAGE_MODE);
        //                 // Set nL and nH based on the width of the image
        //                 printerOutputStream.write(new byte[]{(byte)(0x00ff & pixels[y].length)
        //                         , (byte)((0xff00 & pixels[y].length) >> 8)});
        //                 for (int x = 0; x < pixels[y].length; x++) {
        //                     // for each stripe, recollect 3 bytes (3 bytes = 24 bits)
        //                     printerOutputStream.write(recollectSlice(y, x, pixels));
        //                 }

        //                 // Do a line feed, if not the printing will resume on the same line
        //                 printerOutputStream.write(LINE_FEED);
        //             }
        //             printerOutputStream.write(SET_LINE_SPACE_32);
        //             printerOutputStream.write(LINE_FEED);

        //             printerOutputStream.flush();
        //         } catch (IOException e) {
        //             Log.e(LOG_TAG, "failed to print data");
        //             e.printStackTrace();
        //         }
        //     }
        // }).start();
    }

    // public static int[][] getPixelsSlow(Bitmap image2) {

    //     Bitmap image = resizeTheImageForPrinting(image2);

    //     int width = image.getWidth();
    //     int height = image.getHeight();
    //     int[][] result = new int[height][width];
    //     for (int row = 0; row < height; row++) {
    //         for (int col = 0; col < width; col++) {
    //             result[row][col] = getRGB(image, col, row);
    //         }
    //     }
    //     return result;
    // }

    // private byte[] recollectSlice(int y, int x, int[][] img) {
    //     byte[] slices = new byte[] { 0, 0, 0 };
    //     for (int yy = y, i = 0; yy < y + 24 && i < 3; yy += 8, i++) {
    //         byte slice = 0;
    //         for (int b = 0; b < 8; b++) {
    //             int yyy = yy + b;
    //             if (yyy >= img.length) {
    //                 continue;
    //             }
    //             int col = img[yyy][x];
    //             boolean v = shouldPrintColor(col);
    //             slice |= (byte) ((v ? 1 : 0) << (7 - b));
    //         }
    //         slices[i] = slice;
    //     }
    //     return slices;
    // }

    // private boolean shouldPrintColor(int col) {
    //     final int threshold = 127;
    //     int a, r, g, b, luminance;
    //     a = (col >> 24) & 0xff;
    //     if (a != 0xff) {// Ignore transparencies
    //         return false;
    //     }
    //     r = (col >> 16) & 0xff;
    //     g = (col >> 8) & 0xff;
    //     b = col & 0xff;

    //     luminance = (int) (0.299 * r + 0.587 * g + 0.114 * b);

    //     return luminance < threshold;
    // }

    // public static Bitmap resizeTheImageForPrinting(Bitmap image) {
    //     // making logo size 150 or less pixels
    //     int width = image.getWidth();
    //     int height = image.getHeight();
    //     if (width > 200 || height > 200) {
    //         if (width > height) {
    //             float decreaseSizeBy = (200.0f / width);
    //             return getBitmapResized(image, decreaseSizeBy);
    //         } else {
    //             float decreaseSizeBy = (200.0f / height);
    //             return getBitmapResized(image, decreaseSizeBy);
    //         }
    //     }
    //     return image;
    // }

    // public static int getRGB(Bitmap bmpOriginal, int col, int row) {
    //     // get one pixel color
    //     int pixel = bmpOriginal.getPixel(col, row);
    //     // retrieve color of all channels
    //     int R = Color.red(pixel);
    //     int G = Color.green(pixel);
    //     int B = Color.blue(pixel);
    //     return Color.rgb(R, G, B);
    // }

    // public static Bitmap getBitmapResized(Bitmap image, float decreaseSizeBy) {
    //     Bitmap resized = Bitmap.createScaledBitmap(image, (int) (image.getWidth() * decreaseSizeBy),
    //             (int) (image.getHeight() * decreaseSizeBy), true);
    //     return resized;
    // }

    // @Override
    // public void printQrCode(String qrCode, Callback errorCallback) {
    //     // final Bitmap bitmapImage = TextToQrImageEncode(qrCode);

    //     // if (bitmapImage == null) {
    //     //     errorCallback.invoke("image not found");
    //     //     return;
    //     // }

    //     // if (this.mSocket == null) {
    //     //     errorCallback.invoke("bluetooth connection is not built, may be you forgot to connectPrinter");
    //     //     return;
    //     // }

    //     // final Socket socket = this.mSocket;

    //     // int[][] pixels = getPixelsSlow(bitmapImage);

    //     // OutputStream printerOutputStream = socket.getOutputStream();

    //     // printerOutputStream.write(SET_LINE_SPACE_24);
    //     // printerOutputStream.write(CENTER_ALIGN);

    //     // for (int y = 0; y < pixels.length; y += 24) {
    //     //     // Like I said before, when done sending data,
    //     //     // the printer will resume to normal text printing
    //     //     printerOutputStream.write(SELECT_BIT_IMAGE_MODE);
    //     //     // Set nL and nH based on the width of the image
    //     //     printerOutputStream.write(
    //     //             new byte[] { (byte) (0x00ff & pixels[y].length), (byte) ((0xff00 & pixels[y].length) >> 8) });
    //     //     for (int x = 0; x < pixels[y].length; x++) {
    //     //         // for each stripe, recollect 3 bytes (3 bytes = 24 bits)
    //     //         printerOutputStream.write(recollectSlice(y, x, pixels));
    //     //     }

    //     //     // Do a line feed, if not the printing will resume on the same line
    //     //     printerOutputStream.write(LINE_FEED);
    //     // }
    //     // printerOutputStream.write(SET_LINE_SPACE_32);
    //     // printerOutputStream.write(LINE_FEED);

    //     // printerOutputStream.flush();

    //     // ///////////////////////////////////////////

    //     // new Thread(new Runnable() {
    //     //     @Override
    //     //     public void run() {
    //     //         try {
    //     //             int[][] pixels = getPixelsSlow(bitmapImage);

    //     //             OutputStream printerOutputStream = socket.getOutputStream();

    //     //             printerOutputStream.write(SET_LINE_SPACE_24);
    //     //             printerOutputStream.write(CENTER_ALIGN);

    //     //             for (int y = 0; y < pixels.length; y += 24) {
    //     //                 // Like I said before, when done sending data,
    //     //                 // the printer will resume to normal text printing
    //     //                 printerOutputStream.write(SELECT_BIT_IMAGE_MODE);
    //     //                 // Set nL and nH based on the width of the image
    //     //                 printerOutputStream.write(
    //     //                         new byte[] { (byte) (0x00ff & pixels[y].length), (byte) ((0xff00 & pixels[y].length) >> 8) });
    //     //                 for (int x = 0; x < pixels[y].length; x++) {
    //     //                     // for each stripe, recollect 3 bytes (3 bytes = 24 bits)
    //     //                     printerOutputStream.write(recollectSlice(y, x, pixels));
    //     //                 }

    //     //                 // Do a line feed, if not the printing will resume on the same line
    //     //                 printerOutputStream.write(LINE_FEED);
    //     //             }
    //     //             printerOutputStream.write(SET_LINE_SPACE_32);
    //     //             printerOutputStream.write(LINE_FEED);

    //     //             printerOutputStream.flush();
    //     //         } catch (IOException e) {
    //     //             Log.e(LOG_TAG, "failed to print data");
    //     //             e.printStackTrace();
    //     //         }
    //     //     }
    //     // }).start();
    // }

    private Bitmap TextToQrImageEncode(String Value) {

        com.google.zxing.Writer writer = new QRCodeWriter();

        BitMatrix bitMatrix = null;
        try {
            bitMatrix = writer.encode(Value, com.google.zxing.BarcodeFormat.QR_CODE, 250, 250,
                    ImmutableMap.of(EncodeHintType.MARGIN, 1));
            int width = 250;
            int height = 250;
            Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    bmp.setPixel(i, j, bitMatrix.get(i, j) ? Color.BLACK : Color.WHITE);
                }
            }
            return bmp;
        } catch (WriterException e) {
            // Log.e("QR ERROR", ""+e);

        }

        return null;
    }
}
