package com.megster.cordova;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import java.util.Arrays;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.json.JSONArray;
import org.json.JSONObject;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.annotation.SuppressLint;
import android.text.TextUtils;
//---
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Set;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONException;
import android.content.Intent;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Bitmap.Config;
import android.util.Xml.Encoding;
import android.util.Base64;
import java.util.ArrayList;
import java.util.List;
/**
 * This class does all the work for setting up and managing Bluetooth
 * connections with other devices. It has a thread that listens for
 * incoming connections, a thread for connecting with a device, and a
 * thread for performing data transmissions when connected.
 *
 * This code was based on the Android SDK BluetoothChat Sample
 * $ANDROID_SDK/samples/android-17/BluetoothChat
 */
public class BluetoothSerialService {
	

    private static final String LOG_TAG = "BluetoothPrinter";
    BluetoothAdapter mBluetoothAdapter;
    BluetoothSocket mmSocket;
    BluetoothDevice mmDevice;
    OutputStream mmOutputStream;
    InputStream mmInputStream;
    Thread workerThread;
    byte[] readBuffer;
    int readBufferPosition;
    int counter;
    volatile boolean stopWorker;
    
    

    // Debugging
    private static final String TAG = "BluetoothSerialService";
    private static final boolean D = true;

    // Name for the SDP record when creating server socket
    private static final String NAME_SECURE = "PhoneGapBluetoothSerialServiceSecure";
    private static final String NAME_INSECURE = "PhoneGapBluetoothSerialServiceInSecure";

    // Unique UUID for this application
    private static final UUID MY_UUID_SECURE = UUID.fromString("7A9C3B55-78D0-44A7-A94E-A93E3FE118CE");
    private static final UUID MY_UUID_INSECURE = UUID.fromString("23F18142-B389-4772-93BD-52BDBB2C03E9");

    // Well known SPP UUID
    private static final UUID UUID_SPP = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    // Member fields
    private final BluetoothAdapter mAdapter;
    private final Handler mHandler;
    private AcceptThread mSecureAcceptThread;
    private AcceptThread mInsecureAcceptThread;
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;
    private int mState;
    Bitmap bitmap;

    // Constants that indicate the current connection state
    public static final int STATE_NONE = 0;       // we're doing nothing
    public static final int STATE_LISTEN = 1;     // now listening for incoming connections
    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
    public static final int STATE_CONNECTED = 3;  // now connected to a remote device

    /**
     * Constructor. Prepares a new BluetoothSerial session.
     * @param handler  A Handler to send messages back to the UI Activity
     */
    public BluetoothSerialService(Handler handler) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mState = STATE_NONE;
        mHandler = handler;
    }

    /**
     * Set the current state of the chat connection
     * @param state  An integer defining the current connection state
     */
    private synchronized void setState(int state) {
        if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
        mState = state;

        // Give the new state to the Handler so the UI Activity can update
        mHandler.obtainMessage(BluetoothSerial.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
    }

    /**
     * Return the current connection state. */
    public synchronized int getState() {
        return mState;
    }

    /**
     * Start the chat service. Specifically start AcceptThread to begin a
     * session in listening (server) mode. Called by the Activity onResume() */
    public synchronized void start() {
        if (D) Log.d(TAG, "start");

        // Cancel any thread attempting to make a connection
        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}

        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}

        setState(STATE_NONE);

//      Listen isn't working with Arduino. Ignore since assuming the phone will initiate the connection.
//        setState(STATE_LISTEN);
//
//        // Start the thread to listen on a BluetoothServerSocket
//        if (mSecureAcceptThread == null) {
//            mSecureAcceptThread = new AcceptThread(true);
//            mSecureAcceptThread.start();
//        }
//        if (mInsecureAcceptThread == null) {
//            mInsecureAcceptThread = new AcceptThread(false);
//            mInsecureAcceptThread.start();
//        }
    }

    /**
     * Start the ConnectThread to initiate a connection to a remote device.
     * @param device  The BluetoothDevice to connect
     * @param secure Socket Security type - Secure (true) , Insecure (false)
     */
    public synchronized void connect(BluetoothDevice device, boolean secure) {
        if (D) Log.d(TAG, "connect to: " + device);

        // Cancel any thread attempting to make a connection
        if (mState == STATE_CONNECTING) {
            if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
        }

        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}

        // Start the thread to connect with the given device
        mConnectThread = new ConnectThread(device, secure);
        mConnectThread.start();
        setState(STATE_CONNECTING);
    }

    /**
     * Start the ConnectedThread to begin managing a Bluetooth connection
     * @param socket  The BluetoothSocket on which the connection was made
     * @param device  The BluetoothDevice that has been connected
     */
    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device, final String socketType) {
        if (D) Log.d(TAG, "connected, Socket Type:" + socketType);

        // Cancel the thread that completed the connection
        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}

        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}

        // Cancel the accept thread because we only want to connect to one device
        if (mSecureAcceptThread != null) {
            mSecureAcceptThread.cancel();
            mSecureAcceptThread = null;
        }
        if (mInsecureAcceptThread != null) {
            mInsecureAcceptThread.cancel();
            mInsecureAcceptThread = null;
        }

        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(socket, socketType);
        mConnectedThread.start();

        // Send the name of the connected device back to the UI Activity
        Message msg = mHandler.obtainMessage(BluetoothSerial.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothSerial.DEVICE_NAME, device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        setState(STATE_CONNECTED);
    }

    /**
     * Stop all threads
     */
    public synchronized void stop() {
        if (D) Log.d(TAG, "stop");

        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

        if (mSecureAcceptThread != null) {
            mSecureAcceptThread.cancel();
            mSecureAcceptThread = null;
        }

        if (mInsecureAcceptThread != null) {
            mInsecureAcceptThread.cancel();
            mInsecureAcceptThread = null;
        }
        setState(STATE_NONE);
    }

    /**
     * Write to the ConnectedThread in an unsynchronized manner
     * @param out The bytes to write
     * @see ConnectedThread#write(byte[])
     */
    public void write(byte[] out) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        // Perform the write unsynchronized
        r.write(out);
    }

    /**
     * Write to the ConnectedThread in an unsynchronized manner
     * @param args The String to write
     * @see ConnectedThread#write(String)
     */
    public void write(String args) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        // Perform the write unsynchronized
        r.write(args);
    }
    
    
    //---------------------------------------------------------------------
    public void writeImage(String imageData) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        // Perform the write unsynchronized
       // r.write(args);
        
        try {            
        				final String encodedString = imageData;
                        final String pureBase64Encoded = encodedString.substring(encodedString.indexOf(",") + 1);
                        final byte[] decodedBytes = Base64.decode(pureBase64Encoded, Base64.DEFAULT);

                        Bitmap decodedBitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);

                        bitmap = decodedBitmap;
                        int mWidth = bitmap.getWidth();
                        int mHeight = bitmap.getHeight();

                        //bitmap = resizeImage(bitmap, 48 * 8, mHeight);
                        bitmap = resizeImage(bitmap, 30 * 5, mHeight);
                        byte[] bt = decodeBitmap(bitmap);

                        r.write(bt);
                        // tell the user data were sent
                        //Log.d(LOG_TAG, "Data Sent");
                       // callbackContext.success("Data Sent");
                       // return true;

                    } catch (Exception e) {
                        String errMsg = e.getMessage();
                        Log.e(LOG_TAG, errMsg);
                        e.printStackTrace();
                       // callbackContext.error(errMsg);
                    }
                  //  return false; 
                    }
    
    //New implementation
    private static Bitmap resizeImage(Bitmap bitmap, int w, int h) {
        Bitmap BitmapOrg = bitmap;
        int width = BitmapOrg.getWidth();
        int height = BitmapOrg.getHeight();

        if (width > w) {
            float scaleWidth = ((float) w) / width;
            float scaleHeight = ((float) h) / height + 24;
            Matrix matrix = new Matrix();
            matrix.postScale(scaleWidth, scaleWidth);
            Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 0, 0, width,
                    height, matrix, true);
            return resizedBitmap;
        } else {
            Bitmap resizedBitmap = Bitmap.createBitmap(w, height + 24, Config.RGB_565);
            Canvas canvas = new Canvas(resizedBitmap);
            Paint paint = new Paint();
            canvas.drawColor(Color.WHITE);
            canvas.drawBitmap(bitmap, (w - width) / 2, 0, paint);
            return resizedBitmap;
        }
    }

    public static byte[] decodeBitmap(Bitmap bmp) {
        int bmpWidth = bmp.getWidth();
        int bmpHeight = bmp.getHeight();
        List<String> list = new ArrayList<String>(); //binaryString list
        StringBuffer sb;
        int bitLen = bmpWidth / 8;
        int zeroCount = bmpWidth % 8;
        String zeroStr = "";
        if (zeroCount > 0) {
            bitLen = bmpWidth / 8 + 1;
            for (int i = 0; i < (8 - zeroCount); i++) {
                zeroStr = zeroStr + "0";
            }
        }

        for (int i = 0; i < bmpHeight; i++) {
            sb = new StringBuffer();
            for (int j = 0; j < bmpWidth; j++) {
                int color = bmp.getPixel(j, i);

                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                // if color close to white锛宐it='0', else bit='1'
                if (r > 160 && g > 160 && b > 160) {
                    sb.append("0");
                } else {
                    sb.append("1");
                }
            }
            if (zeroCount > 0) {
                sb.append(zeroStr);
            }
            list.add(sb.toString());
        }

        List<String> bmpHexList = binaryListToHexStringList(list);
        String commandHexString = "1D763000";

        //construct xL and xH
        //there are 8 pixels per byte. In case of modulo: add 1 to compensate.
        bmpWidth = bmpWidth % 8 == 0 ? bmpWidth / 8 : (bmpWidth / 8 + 1);
        int xL = bmpWidth % 256;
        int xH = (bmpWidth - xL) / 256;

        String xLHex = Integer.toHexString(xL);
        String xHHex = Integer.toHexString(xH);
        if(xLHex.length() == 1){
            xLHex = "0" + xLHex;
        }
        if(xHHex.length() == 1){
            xHHex = "0" + xHHex;
        }
        String widthHexString = xLHex + xHHex;


        //construct yL and yH
        int yL = bmpHeight % 256;
        int yH = (bmpHeight - yL) / 256;

        String yLHex = Integer.toHexString(yL);
        String yHHex = Integer.toHexString(yH);
        if(yLHex.length() == 1){
            yLHex = "0" + yLHex;
        }
        if(yHHex.length() == 1){
            yHHex = "0" + yHHex;
        }
        String heightHexString = yLHex + yHHex;

        List<String> commandList = new ArrayList<String>();
        commandList.add(commandHexString + widthHexString + heightHexString);
        commandList.addAll(bmpHexList);

        return hexList2Byte(commandList);
    }
    public static List<String> binaryListToHexStringList(List<String> list) {
        List<String> hexList = new ArrayList<String>();
        for (String binaryStr : list) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < binaryStr.length(); i += 8) {
                String str = binaryStr.substring(i, i + 8);

                String hexString = myBinaryStrToHexString(str);
                sb.append(hexString);
            }
            hexList.add(sb.toString());
        }
        return hexList;

    }
    public static String myBinaryStrToHexString(String binaryStr) {
        String hex = "";
        String f4 = binaryStr.substring(0, 4);
        String b4 = binaryStr.substring(4, 8);
        for (int i = 0; i < binaryArray.length; i++) {
            if (f4.equals(binaryArray[i])) {
                hex += hexStr.substring(i, i + 1);
            }
        }
        for (int i = 0; i < binaryArray.length; i++) {
            if (b4.equals(binaryArray[i])) {
                hex += hexStr.substring(i, i + 1);
            }
        }

        return hex;
    }
    
    private static String hexStr = "0123456789ABCDEF";

    private static String[] binaryArray = {"0000", "0001", "0010", "0011",
        "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011",
        "1100", "1101", "1110", "1111"};

    public static byte[] hexList2Byte(List<String> list) {
        List<byte[]> commandList = new ArrayList<byte[]>();

        for (String hexStr : list) {
            commandList.add(hexStringToBytes(hexStr));
        }
        byte[] bytes = sysCopy(commandList);
        return bytes;
    }
    //New implementation, change old
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
    public static byte[] sysCopy(List<byte[]> srcArrays) {
        int len = 0;
        for (byte[] srcArray : srcArrays) {
            len += srcArray.length;
        }
        byte[] destArray = new byte[len];
        int destLen = 0;
        for (byte[] srcArray : srcArrays) {
            System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
            destLen += srcArray.length;
        }
        return destArray;
    }
    
    //--------------------

    /**
     * Write to the ConnectedThread in an unsynchronized manner
     * @param args The String to write
     * @see ConnectedThread#writeByTemplate(String)
     */
    public void writeByTemplate(String args) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        // Perform the write unsynchronized
        r.writeByTemplate(args);
    }

    /**
     * Indicate that the connection attempt failed and notify the UI Activity.
     */
    private void connectionFailed() {
        // Send a failure message back to the Activity
        Message msg = mHandler.obtainMessage(BluetoothSerial.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothSerial.TOAST, "Unable to connect to device");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        // Start the service over to restart listening mode
        BluetoothSerialService.this.start();
    }

    /**
     * Indicate that the connection was lost and notify the UI Activity.
     */
    private void connectionLost() {
        // Send a failure message back to the Activity
        Message msg = mHandler.obtainMessage(BluetoothSerial.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothSerial.TOAST, "Device connection was lost");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        // Start the service over to restart listening mode
        BluetoothSerialService.this.start();
    }

    /**
     * This thread runs while listening for incoming connections. It behaves
     * like a server-side client. It runs until a connection is accepted
     * (or until cancelled).
     */
    private class AcceptThread extends Thread {
        // The local server socket
        private final BluetoothServerSocket mmServerSocket;
        private String mSocketType;

        public AcceptThread(boolean secure) {
            BluetoothServerSocket tmp = null;
            mSocketType = secure ? "Secure":"Insecure";

            // Create a new listening server socket
            try {
                if (secure) {
                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
                } else {
                    tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, MY_UUID_INSECURE);
                }
            } catch (IOException e) {
                Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
            }
            mmServerSocket = tmp;
        }

        public void run() {
            if (D) Log.d(TAG, "Socket Type: " + mSocketType + "BEGIN mAcceptThread" + this);
            setName("AcceptThread" + mSocketType);

            BluetoothSocket socket;

            // Listen to the server socket if we're not connected
            while (mState != STATE_CONNECTED) {
                try {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
                    break;
                }

                // If a connection was accepted
                if (socket != null) {
                    synchronized (BluetoothSerialService.this) {
                        switch (mState) {
                            case STATE_LISTEN:
                            case STATE_CONNECTING:
                                // Situation normal. Start the connected thread.
                                connected(socket, socket.getRemoteDevice(),
                                        mSocketType);
                                break;
                            case STATE_NONE:
                            case STATE_CONNECTED:
                                // Either not ready or already connected. Terminate new socket.
                                try {
                                    socket.close();
                                } catch (IOException e) {
                                    Log.e(TAG, "Could not close unwanted socket", e);
                                }
                                break;
                        }
                    }
                }
            }
            if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);

        }

        public void cancel() {
            if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
            try {
                mmServerSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
            }
        }
    }


    /**
     * This thread runs while attempting to make an outgoing connection
     * with a device. It runs straight through; the connection either
     * succeeds or fails.
     */
    private class ConnectThread extends Thread {
        private /*final*/ BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
        private String mSocketType;

        public ConnectThread(BluetoothDevice device, boolean secure) {
            mmDevice = device;
            BluetoothSocket tmp = null;
            mSocketType = secure ? "Secure" : "Insecure";

            // Get a BluetoothSocket for a connection with the given BluetoothDevice
            try {
                if (secure) {
                    // tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
                    tmp = device.createRfcommSocketToServiceRecord(UUID_SPP);
                } else {
                    //tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);
                    tmp = device.createInsecureRfcommSocketToServiceRecord(UUID_SPP);
                }
            } catch (IOException e) {
                Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
            }
            mmSocket = tmp;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
            setName("ConnectThread" + mSocketType);

            // Always cancel discovery because it will slow down a connection
            mAdapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a successful connection or an exception
                Log.i(TAG,"Connecting to socket...");
                mmSocket.connect();
                Log.i(TAG,"Connected");
            } catch (IOException e) {
                Log.e(TAG, e.toString());

                // Some 4.1 devices have problems, try an alternative way to connect
                // See https://github.com/don/BluetoothSerial/issues/89
                try {
                    Log.i(TAG,"Trying fallback...");
                    mmSocket = (BluetoothSocket) mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(mmDevice,1);
                    mmSocket.connect();
                    Log.i(TAG,"Connected");
                } catch (Exception e2) {
                    Log.e(TAG, "Couldn't establish a Bluetooth connection.");
                    try {
                        mmSocket.close();
                    } catch (IOException e3) {
                        Log.e(TAG, "unable to close() " + mSocketType + " socket during connection failure", e3);
                    }
                    connectionFailed();
                    return;
                }
            }

            // Reset the ConnectThread because we're done
            synchronized (BluetoothSerialService.this) {
                mConnectThread = null;
            }

            // Start the connected thread
            connected(mmSocket, mmDevice, mSocketType);
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
            }
        }
    }

    /**
     * This thread runs during a connection with a remote device.
     * It handles all incoming and outgoing transmissions.
     */
    class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket, String socketType) {
            Log.d(TAG, "create ConnectedThread: " + socketType);
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    String data = new String(buffer, 0, bytes);

                    // Send the new data String to the UI Activity
                    mHandler.obtainMessage(BluetoothSerial.MESSAGE_READ, data).sendToTarget();

                    // Send the raw bytestream to the UI Activity.
                    // We make a copy because the full array can have extra data at the end
                    // when / if we read less than its size.
                    if (bytes > 0) {
                        byte[] rawdata = Arrays.copyOf(buffer, bytes);
                        mHandler.obtainMessage(BluetoothSerial.MESSAGE_READ_RAW, rawdata).sendToTarget();
                    }

                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    // Start the service over to restart listening mode
                    BluetoothSerialService.this.start();
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         * @param buffer  The bytes to write
         */
        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
                // Share the sent message back to the UI Activity
                mHandler.obtainMessage(BluetoothSerial.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
            } catch (Exception e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        /**
         * Write to the connected OutStream.
         * @param String  The args to write
         */
        public void write(String args) {
            try {
                byte[] buffer = args.getBytes("gbk");
                mmOutStream.write(buffer);
                // Share the sent message back to the UI Activity
                mHandler.obtainMessage(BluetoothSerial.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
            } catch (Exception e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        /**
         * Write to the connected OutStream.
         * @param String  The args to write
         */
        public void writeByTemplate(String args) {
            try {
                Log.d(TAG, "==================寮�濮嬫墦鍗�");
                Log.d(TAG, "==================鎵撳嵃鐨勬暟鎹�"+args);
               // printTemplate(args);
                printCodes(args);
                Log.d(TAG, "==================鎵撳嵃瀹屼簡");
                // Share the sent message back to the UI Activity
                mHandler.obtainMessage(BluetoothSerial.MESSAGE_WRITE, -1, -1, args).sendToTarget();
            } catch (Exception e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
        
        public void printCodes(String data){
            try{
                if(data != null && data.trim() != ""){

                    JSONObject o = new JSONObject(data); //鑾峰彇鏁版嵁瀵硅薄

                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                    PrintUtils.setOutputStream(mmOutStream);
                    PrintUtils.selectCommand(PrintUtils.RESET); //鍒濆鍖�
                    PrintUtils.selectCommand(PrintUtils.LINE_SPACING_DEFAULT); // 琛岄棿璺�
                    PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT); //宸﹀榻�
                    //PrintUtils.selectCommand(PrintUtils.ALIGN_CENTER); //涓棿瀵归綈
                    // PrintUtils.selectCommand(PrintUtils.DOUBLE_HEIGHT_WIDTH); //2鍊嶅楂�
                    PrintUtils.printText(o.getString("titel")+"\n\n");  //!鏍囬瀛楃
                    PrintUtils.selectCommand(PrintUtils.NORMAL); //瀛椾綋杩樺師
                    
                    String remark = o.getString("code"); 
                    if(remark != null && remark.trim() != "" && remark != "null"){
                        PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT); //宸﹀榻�
                        PrintUtils.selectCommand(PrintUtils.BOLD); //鍔犵矖
                        PrintUtils.printText(o.getString("code")); //!鎸囦护瀛楃
                        PrintUtils.printText("-------------------\n\n");
                        PrintUtils.selectCommand(PrintUtils.BOLD_CANCEL);
                    }

                    PrintUtils.selectCommand(PrintUtils.NORMAL);
                    PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);                  
                    PrintUtils.printText(PrintUtils.printTwoData("鎵撳嵃鏃堕棿", sdf.format(date)+"\n\n"));
                    PrintUtils.selectCommand(PrintUtils.ALIGN_CENTER);
                    PrintUtils.printText("\n\n\n\n\n");
                }
            }catch(Exception e){
                e.printStackTrace();
                Log.e(TAG, "Exception during write", e);
            }
        }
        

        public void printTemplate(String data){
            try{
                if(data != null && data.trim() != ""){
                    JSONObject o = new JSONObject(data);
                    Log.d(TAG, "==================JSON杞崲娌℃湁闂");
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                    PrintUtils.setOutputStream(mmOutStream);
                    PrintUtils.selectCommand(PrintUtils.RESET);
                    PrintUtils.selectCommand(PrintUtils.LINE_SPACING_DEFAULT);
                    PrintUtils.selectCommand(PrintUtils.ALIGN_CENTER);
                    PrintUtils.selectCommand(PrintUtils.DOUBLE_HEIGHT_WIDTH);
                    PrintUtils.printText(o.getString("shopName")+"\n\n");
                    PrintUtils.selectCommand(PrintUtils.NORMAL);

                    Log.d(TAG, "==================搴楅摵鍚嶇О鎵撳嵃娌℃湁闂");
                    
                    String remark = o.getString("remark");
                    if(remark != null && remark.trim() != "" && remark != "null"){
                        PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);
                        PrintUtils.selectCommand(PrintUtils.BOLD);
                        PrintUtils.printText("銆愬娉ㄣ��"+o.getString("remark")+"\n");
                        PrintUtils.printText("--------------------------------\n\n");
                        PrintUtils.selectCommand(PrintUtils.BOLD_CANCEL);
                    }

                    Log.d(TAG, "==================澶囨敞鎵撳嵃娌℃湁闂");
                    
                    PrintUtils.printText("--------------------------------\n");
                    PrintUtils.selectCommand(PrintUtils.BOLD);
                    PrintUtils.printText(PrintUtils.printThreeData("鍟嗗搧", "鏁伴噺", "閲戦\n"));
                    PrintUtils.printText("--------------------------------\n");
                    PrintUtils.selectCommand(PrintUtils.BOLD_CANCEL);
                    
                    JSONArray goods = o.getJSONArray("orderGoods");
                    for(int i=0;i<goods.length();i++){
                        JSONObject g = goods.getJSONObject(i);
                        PrintUtils.printText(PrintUtils.printThreeData(g.getString("goodsName"), g.getString("goodsNum"), g.getDouble("totalPrice")+"\n"));
                    }

                    Log.d(TAG, "==================鍟嗗搧鎵撳嵃娌℃湁闂");
                    
                    PrintUtils.printText("--------------------------------\n");
                    PrintUtils.printText(PrintUtils.printTwoData("鎬讳欢鏁�", o.getInt("goodsNum")+"\n"));
                    PrintUtils.printText(PrintUtils.printTwoData("鍟嗗搧鍚堣", o.getDouble("totalGoodsMoney")+"\n"));
                    PrintUtils.printText(PrintUtils.printTwoData("杩愯垂", o.getDouble("freightMoney")+"\n"));
                    PrintUtils.printText(PrintUtils.printTwoData("浼樻儬", o.getDouble("preferentialPrice")+"\n"));
                    PrintUtils.printText(PrintUtils.printTwoData("浣欓鎶垫墸", o.getDouble("balanceMoney")+"\n"));
                    PrintUtils.printText("--------------------------------\n");
                    PrintUtils.printText(PrintUtils.printTwoData("搴旀敹", o.getDouble("receivableSum")+"\n"));

                    Log.d(TAG, "==================閲戦鎵撳嵃娌℃湁闂");
                    
                    PrintUtils.selectCommand(PrintUtils.DOUBLE_HEIGHT_WIDTH);
                    PrintUtils.printText(o.getString("sendAddress")+"\n");
                    PrintUtils.printText("銆�"+o.getString("receiver")+"銆慭n");
                    PrintUtils.printText(o.getString("receiverMobile")+"\n");
                    PrintUtils.selectCommand(PrintUtils.NORMAL);
                    PrintUtils.printText("--------------------------------\n");

                    Log.d(TAG, "==================鏀惰揣浜烘墦鍗版病鏈夐棶棰�");
                    
                    PrintUtils.selectCommand(PrintUtils.NORMAL);
                    PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);
                    PrintUtils.printText(PrintUtils.printTwoData("璁㈠崟缂栧彿", o.getString("code")+"\n"));
                    if(o.getString("createTime")!=null){
                        PrintUtils.printText(PrintUtils.printTwoData("涓嬪崟鏃堕棿", o.getString("createTime").replace("T"," ")+"\n"));
                    }
                    PrintUtils.printText(PrintUtils.printTwoData("鎵撳嵃鏃堕棿", sdf.format(date)+"\n\n"));
                    PrintUtils.selectCommand(PrintUtils.ALIGN_CENTER);
                    PrintUtils.printText("娆㈣繋涓嬫鍏変复");

                    Log.d(TAG, "==================鏈�鍚庢墦鍗版病鏈夐棶棰�");

                    PrintUtils.printText("\n\n\n\n\n");
                }
            }catch(Exception e){
                e.printStackTrace();
                Log.e(TAG, "Exception during write", e);
            }
        }
    }

    static class PrintUtils {

        /**
         * 鎵撳嵃绾镐竴琛屾渶澶х殑瀛楄妭
         */
        private static final int LINE_BYTE_SIZE = 32;

        private static final int LEFT_LENGTH = 20;

        private static final int RIGHT_LENGTH = 12;

        /**
         * 宸︿晶姹夊瓧鏈�澶氭樉绀哄嚑涓枃瀛�
         */
        private static final int LEFT_TEXT_MAX_LENGTH = 8;

        /**
         * 灏忕エ鎵撳嵃鑿滃搧鐨勫悕绉帮紝涓婇檺璋冨埌8涓瓧
         */
        public static final int MEAL_NAME_MAX_LENGTH = 8;

        private static OutputStream outputStream = null;

        public static OutputStream getOutputStream() {
            return outputStream;
        }

        public static void setOutputStream(OutputStream outputStream) {
            PrintUtils.outputStream = outputStream;
        }


        /**
         * 鎵撳嵃鏂囧瓧
         *
         * @param text 瑕佹墦鍗扮殑鏂囧瓧
         */
        public static void printText(String text) {
            try {
                byte[] data = text.getBytes("gbk");
                outputStream.write(data, 0, data.length);
                outputStream.flush();
            } catch (IOException e) {
                //Toast.makeText(this.context, "鍙戦�佸け璐ワ紒", Toast.LENGTH_SHORT).show();
                e.printStackTrace();
                Log.e(TAG, "Exception during write", e); 
            }
        }

        /**
         * 璁剧疆鎵撳嵃鏍煎紡
         *
         * @param command 鏍煎紡鎸囦护
         */
        public static void selectCommand(byte[] command) {
            try {
                outputStream.write(command);
                outputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "Exception during write", e);
            }
        }

        /**
         * 澶嶄綅鎵撳嵃鏈�
         */
        public static final byte[] RESET = {0x1b, 0x40};

        /**
         * 宸﹀榻�
         */
        public static final byte[] ALIGN_LEFT = {0x1b, 0x61, 0x00};

        /**
         * 涓棿瀵归綈
         */
        public static final byte[] ALIGN_CENTER = {0x1b, 0x61, 0x01};

        /**
         * 鍙冲榻�
         */
        public static final byte[] ALIGN_RIGHT = {0x1b, 0x61, 0x02};

        /**
         * 閫夋嫨鍔犵矖妯″紡
         */
        public static final byte[] BOLD = {0x1b, 0x45, 0x01};

        /**
         * 鍙栨秷鍔犵矖妯″紡
         */
        public static final byte[] BOLD_CANCEL = {0x1b, 0x45, 0x00};

        /**
         * 瀹介珮鍔犲��
         */
        public static final byte[] DOUBLE_HEIGHT_WIDTH = {0x1d, 0x21, 0x11};

        /**
         * 瀹藉姞鍊�
         */
        public static final byte[] DOUBLE_WIDTH = {0x1d, 0x21, 0x10};

        /**
         * 楂樺姞鍊�
         */
        public static final byte[] DOUBLE_HEIGHT = {0x1d, 0x21, 0x01};

        /**
         * 瀛椾綋涓嶆斁澶�
         */
        public static final byte[] NORMAL = {0x1d, 0x21, 0x00};

        /**
         * 璁剧疆榛樿琛岄棿璺�
         */
        public static final byte[] LINE_SPACING_DEFAULT = {0x1b, 0x32};

        /**
         * 璁剧疆琛岄棿璺�
         */
    //  public static final byte[] LINE_SPACING = {0x1b, 0x32};//{0x1b, 0x33, 0x14};  // 20鐨勮闂磋窛锛�0锛�255锛�


    //  final byte[][] byteCommands = {
    //          { 0x1b, 0x61, 0x00 }, // 宸﹀榻�
    //          { 0x1b, 0x61, 0x01 }, // 涓棿瀵归綈
    //          { 0x1b, 0x61, 0x02 }, // 鍙冲榻�
    //          { 0x1b, 0x40 },// 澶嶄綅鎵撳嵃鏈�
    //          { 0x1b, 0x4d, 0x00 },// 鏍囧噯ASCII瀛椾綋
    //          { 0x1b, 0x4d, 0x01 },// 鍘嬬缉ASCII瀛椾綋
    //          { 0x1d, 0x21, 0x00 },// 瀛椾綋涓嶆斁澶�
    //          { 0x1d, 0x21, 0x11 },// 瀹介珮鍔犲��
    //          { 0x1b, 0x45, 0x00 },// 鍙栨秷鍔犵矖妯″紡
    //          { 0x1b, 0x45, 0x01 },// 閫夋嫨鍔犵矖妯″紡
    //          { 0x1b, 0x7b, 0x00 },// 鍙栨秷鍊掔疆鎵撳嵃
    //          { 0x1b, 0x7b, 0x01 },// 閫夋嫨鍊掔疆鎵撳嵃
    //          { 0x1d, 0x42, 0x00 },// 鍙栨秷榛戠櫧鍙嶆樉
    //          { 0x1d, 0x42, 0x01 },// 閫夋嫨榛戠櫧鍙嶆樉
    //          { 0x1b, 0x56, 0x00 },// 鍙栨秷椤烘椂閽堟棆杞�90掳
    //          { 0x1b, 0x56, 0x01 },// 閫夋嫨椤烘椂閽堟棆杞�90掳
    //  };

        /**
         * 鎵撳嵃涓ゅ垪
         *
         * @param leftText  宸︿晶鏂囧瓧
         * @param rightText 鍙充晶鏂囧瓧
         * @return
         */
        @SuppressLint("NewApi")
        public static String printTwoData(String leftText, String rightText) {
            StringBuilder sb = new StringBuilder();
            int leftTextLength = getBytesLength(leftText);
            int rightTextLength = getBytesLength(rightText);
            sb.append(leftText);

            // 璁＄畻涓や晶鏂囧瓧涓棿鐨勭┖鏍�
            int marginBetweenMiddleAndRight = LINE_BYTE_SIZE - leftTextLength - rightTextLength;

            for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
                sb.append(" ");
            }
            sb.append(rightText);
            return sb.toString();
        }

        /**
         * 鎵撳嵃涓夊垪
         *
         * @param leftText   宸︿晶鏂囧瓧
         * @param middleText 涓棿鏂囧瓧
         * @param rightText  鍙充晶鏂囧瓧
         * @return
         */
        @SuppressLint("NewApi")
        public static String printThreeData(String leftText, String middleText, String rightText) {
            StringBuilder sb = new StringBuilder();
            // 宸﹁竟鏈�澶氭樉绀� LEFT_TEXT_MAX_LENGTH 涓眽瀛� + 涓や釜鐐�
            if (leftText.length() > LEFT_TEXT_MAX_LENGTH) {
                leftText = leftText.substring(0, LEFT_TEXT_MAX_LENGTH) + "..";
            }
            int leftTextLength = getBytesLength(leftText);
            int middleTextLength = getBytesLength(middleText);
            int rightTextLength = getBytesLength(rightText);

            sb.append(leftText);
            // 璁＄畻宸︿晶鏂囧瓧鍜屼腑闂存枃瀛楃殑绌烘牸闀垮害
            int marginBetweenLeftAndMiddle = LEFT_LENGTH - leftTextLength - middleTextLength / 2;

            for (int i = 0; i < marginBetweenLeftAndMiddle; i++) {
                sb.append(" ");
            }
            sb.append(middleText);

            // 璁＄畻鍙充晶鏂囧瓧鍜屼腑闂存枃瀛楃殑绌烘牸闀垮害
            int marginBetweenMiddleAndRight = RIGHT_LENGTH - middleTextLength / 2 - rightTextLength;

            for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
                sb.append(" ");
            }

            // 鎵撳嵃鐨勬椂鍊欏彂鐜帮紝鏈�鍙宠竟鐨勬枃瀛楁�绘槸鍋忓彸涓�涓瓧绗︼紝鎵�浠ラ渶瑕佸垹闄や竴涓┖鏍�
            sb.delete(sb.length() - 1, sb.length()).append(rightText);
            return sb.toString();
        }

        /**
         * 鑾峰彇鏁版嵁闀垮害
         *
         * @param msg
         * @return
         */
        @SuppressLint("NewApi")
        private static int getBytesLength(String msg) {
            return msg.getBytes(Charset.forName("GB2312")).length;
        }

        /**
         * 鏍煎紡鍖栬彍鍝佸悕绉帮紝鏈�澶氭樉绀篗EAL_NAME_MAX_LENGTH涓暟
         *
         * @param name
         * @return
         */
        public static String formatMealName(String name) {
            if (TextUtils.isEmpty(name)) {
                return name;
            }
            if (name.length() > MEAL_NAME_MAX_LENGTH) {
                return name.substring(0, 8) + "..";
            }
            return name;
        }

    }
}
