package com.ycgis.zpSDK;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.graphics.Bitmap;
import android.graphics.Color;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.rs.macall.androidx.basemodel.utils.BitMapUtils;
import com.rs.macall.androidx.basemodel.utils.StringUtil;
import com.ycgis.macall.personalcenter.m.datamodel.PrintModel;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;


public class BluetoothSocket {
    public static String ErrorMessage = "No Error";
    private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private static BluetoothAdapter myBluetoothAdapter;
    private static BluetoothDevice myDevice;
    private static android.bluetooth.BluetoothSocket mySocket = null;
    private static OutputStream myOutStream = null;
    private static InputStream myInStream = null;
    private static BluetoothSocket mBluetoothSocket = null;

    private final static String GBK = "GBK";

    public static BluetoothSocket getInstance() {
        if (mBluetoothSocket == null)
            mBluetoothSocket = new BluetoothSocket();
        return mBluetoothSocket;
    }


    // 检查是否已连接
    public boolean isConnected() {
        return mySocket != null && mySocket.isConnected();
    }

    /**
     * TODO 仅且仅当 {@link #isConnected()} 结果为true时该方法返回连接设备
     *
     * @return
     */
    public BluetoothDevice getRemoteDevice() {
        if (isConnected())
            return mySocket.getRemoteDevice();
        return null;
    }


    public boolean ConnectPrinter(String BDAddr) {
        if (BDAddr.equals("") || BDAddr == null) {
            ErrorMessage = "没有选择打印机";
            return false;
        }
        myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (myBluetoothAdapter == null) {
            ErrorMessage = "蓝牙系统错误";
            return false;
        }
        myDevice = myBluetoothAdapter.getRemoteDevice(BDAddr);
        if (myDevice == null) {
            ErrorMessage = "读取蓝牙设备错误";
            return false;
        }
        if (!SPPOpen(myBluetoothAdapter, myDevice)) {
            return false;
        }
        return true;
    }

    public boolean SPPOpen(BluetoothAdapter bluetoothAdapter, BluetoothDevice btDevice) {
        boolean error = false;
        myBluetoothAdapter = bluetoothAdapter;
        myDevice = btDevice;

        if (!myBluetoothAdapter.isEnabled()) {
            ErrorMessage = "蓝牙适配器没有打开";
            return false;
        }
        myBluetoothAdapter.cancelDiscovery();

        try {
            //mySocket = myDevice.createRfcommSocketToServiceRecord(SPP_UUID);
            Method m = myDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
            mySocket = (android.bluetooth.BluetoothSocket) m.invoke(myDevice, 1);
        } catch (SecurityException e) {
            mySocket = null;
            ErrorMessage = "蓝牙端口错误";
            return false;
        } catch (NoSuchMethodException e) {
            mySocket = null;
            ErrorMessage = "蓝牙端口错误";
            return false;
        } catch (IllegalArgumentException e) {
            mySocket = null;
            ErrorMessage = "蓝牙端口错误";
            return false;
        } catch (IllegalAccessException e) {
            mySocket = null;
            ErrorMessage = "蓝牙端口错误";
            return false;
        } catch (InvocationTargetException e) {
            mySocket = null;
            ErrorMessage = "蓝牙端口错误";
            return false;
        }

        try {
            mySocket.connect();
        } catch (IOException e2) {
            ErrorMessage = e2.getLocalizedMessage();//"无法连接蓝牙打印机";
            mySocket = null;
            return false;
        }

        try {
            myOutStream = mySocket.getOutputStream();
        } catch (IOException e3) {
            myOutStream = null;
            error = true;
            return false;
        }

        try {
            myInStream = mySocket.getInputStream();
        } catch (IOException e3) {
            myInStream = null;
            error = true;
            return false;
        }

        if (error) {
            //SPPClose();
            return false;
        }

        return true;
    }

    public boolean disconnect() {
        if (!SPPClose())
            return false;
        return true;
    }

    private boolean SPPClose() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        if (myOutStream != null) {
            try {
                myOutStream.flush();
            } catch (IOException e1) {
            }
            try {
                myOutStream.close();
            } catch (IOException e) {
            }
            myOutStream = null;
        }
        if (myInStream != null) {
            try {
                myInStream.close();
            } catch (IOException e) {
            }
            myInStream = null;
        }
        if (mySocket != null) {
            try {
                mySocket.close();
            } catch (IOException e) {
            }
            mySocket = null;
        }
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
        }
        return true;
    }


    // 发送数据
    public boolean sendData(List<PrintModel> dataList) {
        if (!isConnected())return false;
        if (myOutStream != null) {
            if (dataList == null) return false;
            try {
                boolean isNewline = dataList.size()>1;
                for (int i = 0; i < dataList.size(); i++) {
                    PrintModel printModel = dataList.get(i);
                    if (printModel == null) continue;
                    if (printModel.getPrintType() == 1) {
                        String content = printModel.getContent();
                        if (StringUtil.isNullOrEmpty(content)){
                            continue;
                        }
                        if (isNewline){
                            if (!content.endsWith("\n")){
                                content =  content+"\n";
                            }
                        }
                        printText(content,printModel);
                    } else if (printModel.getPrintType() == 2) {
                        printQrCode(printModel);
                    } else if (printModel.getPrintType() == 3) {
                        printBitMap(printModel);
//                        Bitmap bitmap = BitMapUtils.base64ToBitmap(printModel.getContent());
//                        if (bitmap == null) continue;
//                        myOutStream.write(decodeBitmap(bitmap)); // 部分打印机需GBK编码
                    }
                }
                myOutStream.flush();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return false;
    }


    public boolean printText(String content, PrintModel model) throws UnsupportedEncodingException {
        zpPrinter zpSDK = new zpPrinter();
        zpSDK.pageSetup(model.getPageWidth(), model.getPageHeight());
        //zpSDK.drawBarCode(10,10,"123456789012",1,0,4,80);
        zpSDK.alignLeft();
        zpSDK.drawText(10, 10,content, "思源黑体", 24, 0, 0, false, false);
        String print = zpSDK.print(0, 0);
       return Write(print.getBytes(GBK));
    }

    public boolean printBitMap(PrintModel model){
        Bitmap bitmap = BitMapUtils.base64ToBitmap(model.getContent());
        if (bitmap == null) return false;
        zpPrinter zpSDK = new zpPrinter();
        zpSDK.pageSetup(model.getPageWidth(), model.getPageHeight());
        zpSDK.drawGraphic(10,10,model.getContentHeight(),model.getContentWidth(),bitmap);
        byte[] bytes = zpSDK.printByte(0, 1);
        byte[] gzipBytes = GZIPFrame.codec(bytes);
        return Write(gzipBytes); // 部分打印机需GBK编码
    }

    public boolean printQrCode(PrintModel model) {
        zpPrinter zpSDK = new zpPrinter();
        try {
            zpSDK.pageSetup(model.getPageWidth(), model.getPageHeight());
            zpSDK.drawQrCode(10, 10, model.getContent(), 0, model.getZoomLevel(), 4);
            String print = zpSDK.print(0, 0);
//            return Write(print.getBytes("GBK")); // 部分打印机需GBK编码/
            return Write(print.getBytes("UTF-8")); // 部分打印机需GBK编码
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean Write(byte[] Data) {
        try {
            myOutStream.write(Data);
        } catch (IOException e) {
            ErrorMessage = "发送蓝牙数据失败";
            return false;
        }
        return true;
    }

    public boolean Write(byte[] Data, int DataLen) {
        try {
            myOutStream.write(Data, 0, DataLen);
        } catch (IOException e) {
            ErrorMessage = "发送蓝牙数据失败";
            return false;
        }
        return true;
    }

    public void SPPFlush() {
        int i = 0, DataLen = 0;
        try {
            DataLen = myInStream.available();
        } catch (IOException e1) {
        }
        for (i = 0; i < DataLen; i++) {
            try {
                myInStream.read();
            } catch (IOException e) {

            }
        }
    }

    public byte[] Read(int Timeout) {
        byte[] data = null;
        float rest = (float) Timeout;
        int len = 0;
        try {
            len = myInStream.available();
            if (len > 0) {
                myInStream.skip(myInStream.available());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            while (rest > 0.0F) {
                if (myInStream != null) {
                    int available = myInStream.available();
                    if (available > 0) {
                        data = new byte[available];
                        myInStream.read(data);
                        return data;
                    }
                    Thread.sleep(100L);
                    rest -= 0.1F;
                }
            }

            return null;
        } catch (Exception var6) {
            return null;
        }

    }

    /**
     * 生成二位码图片
     *
     * @param content
     * @param size
     * @return
     * @throws WriterException
     */
    public Bitmap generateQRCode(String content, int size) throws WriterException {
        QRCodeWriter writer = new QRCodeWriter();
        BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, size, size);
        Bitmap bmp = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565);
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                bmp.setPixel(x, y, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE);
            }
        }
        return bmp;
    }

    /**
     * 位图转换
     *
     * @param bmp
     * @return
     */
    public byte[] decodeBitmap(Bitmap bmp) {
        int width = bmp.getWidth();
        int height = bmp.getHeight();
        int[] pixels = new int[width * height];
        bmp.getPixels(pixels, 0, width, 0, 0, width, height);

        // 生成 ESC/POS 位图指令
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(0x1D);
        baos.write(0x76);
        baos.write(0x30);
        baos.write(0x00);
        baos.write(width / 8); // 宽度（以8为单位）
        baos.write(0x00);
        baos.write(height % 256); // 高度低字节
        baos.write(height / 256); // 高度高字节

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x += 8) {
                byte b = 0;
                for (int n = 0; n < 8; n++) {
                    int pixel = pixels[y * width + x + n];
                    int gray = (Color.red(pixel) + Color.green(pixel) + Color.blue(pixel)) / 3;
                    if (gray < 128) {
                        b |= (1 << (7 - n));
                    }
                }
                baos.write(b);
            }
        }
        return baos.toByteArray();
    }

}
