package com.joe.print.mvp.print.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.Nullable;

import com.elvishew.xlog.XLog;
import com.epson.epos2.Epos2Exception;
import com.epson.epos2.printer.Printer;
import com.epson.epos2.printer.PrinterStatusInfo;
import com.epson.epos2.printer.ReceiveListener;
import com.gingersoft.gsa.cloud.common.constans.HttpsConstans;
import com.gingersoft.gsa.cloud.common.constans.PrintConstans;
import com.gingersoft.gsa.cloud.common.core.restaurant.RestaurantInfoManager;
import com.gingersoft.gsa.cloud.common.core.user.UserContext;
import com.gingersoft.gsa.cloud.common.logan.LoganManager;
import com.gingersoft.gsa.cloud.common.utils.AidlUtil;
import com.gingersoft.gsa.cloud.common.utils.JsonUtils;
import com.gingersoft.gsa.cloud.common.utils.okhttpUtils.OkHttp3Utils;
import com.gingersoft.gsa.cloud.common.utils.other.TextUtil;
import com.gingersoft.gsa.cloud.common.utils.threadPool.ThreadPoolManager;
import com.gingersoft.gsa.cloud.common.utils.time.TimeUtils;
import com.gingersoft.gsa.cloud.common.utils.toast.ToastUtils;
import com.gingersoft.gsa.cloud.database.bean.PrintCurrencyBean;
import com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean;
import com.gingersoft.gsa.cloud.database.utils.PrinterDeviceDaoUtils;
import com.gingersoft.gsa.cloud.print.PrintExecutor;
import com.gingersoft.gsa.cloud.print.PrintSocketHolder;
import com.gingersoft.gsa.cloud.print.PrinterWriter58mm;
import com.gingersoft.gsa.cloud.print.bean.PrjBean;
import com.gingersoft.gsa.cloud.print.bean.UpdateBean;
import com.hyweb.n5.lib.constant.PrinterConstant;
import com.hyweb.n5.lib.util.PrinterUtil;
import com.hyweb.n5.server.aidl.IOnPrintCallback;
import com.joe.print.mvp.print.PrintPrjKitchen;
import com.joe.print.mvp.print.PrinterRoot;
import com.joe.print.mvp.print.common.PrinterFinderCallback;
import com.joe.print.mvp.print.common.SendResultCode;
import com.joe.print.mvp.print.maker.PrjPrintMaker;
import com.joe.print.mvp.print.usb.EscCommand;
import com.joe.print.mvp.print.usb.UsbPrint;
import com.joe.print.mvp.print.usb.UsbPrinter;
import com.joe.print.mvp.print.usb.UsbPrinterFinder;
import com.joe.print.mvp.print.utils.MyPrintUtils;
import com.sunmi.peripheral.printer.InnerResultCallbcak;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileOutputStream;
import okhttp3.MediaType;
import okhttp3.RequestBody;

import static com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean.PRINT_IP;
import static com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean.PRINT_LOCAL;
import static com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean.PRINT_PRJ_PC;
import static com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean.PRINT_USB;


/**
 * 在用戶登錄成功後，打開打印service，每隔3~5秒請求一次。請求到了數據就進行打印邏輯
 */
public class PrjService extends Service implements ReceiveListener {

//    private static final String TAG = PrjService.class.getSimpleName();

    private Disposable disposable;
    private Disposable wakeDisposable;
    private List<PrinterDeviceBean> printerDeviceBeans;
    private List<PrjBean.DataBean.Bean> printDatas = new ArrayList<>();
    //    private Map<String, List<PrjBean.DataBean.Bean>> listMap = new HashMap<>();
    private List<PrintCurrencyBean> printCurrencyBeans;//通用打印配置
    private Context mContext;
    private String TAG = "Prj";

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        initUsbPrint();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mContext = this;
        ThreadPoolManager.getInstence().putExecutableTasks(() -> {
            LoganManager.w_printer(TAG,"開始請求 PRJ 數據");
            getPrintList();
            //開始請求
            startGetPrjInfo();
        });
        return super.onStartCommand(intent, flags, startId);
    }

    private UsbPrinterFinder printerFinder;
    private List<UsbPrinter> mUsbPrinters;

    public void initUsbPrint() {
        printerFinder = new UsbPrinterFinder(mContext, new PrinterFinderCallback<UsbPrinter>() {
            @Override
            public void onStart() {

            }

            @Override
            public void onFound(UsbPrinter usbPrinter) {

            }

            @Override
            public void onFinished(List<UsbPrinter> usbPrinters) {
                mUsbPrinters = usbPrinters;
            }
        });
        //開啟監聽USB連接
        printerFinder.startFinder();
    }

    private void getPrintList() {
        if (printerDeviceBeans == null || printerDeviceBeans.size() == 0) {
            PrinterDeviceDaoUtils printerDeviceDaoUtils = new PrinterDeviceDaoUtils(this);
            printerDeviceBeans = printerDeviceDaoUtils.queryAllPrinterDeviceBean();
            printerDeviceDaoUtils.closeConnection();
        }
        printCurrencyBeans = MyPrintUtils.getPrintCurrencyBeans(this);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 開啟輪詢查詢prj數據
     */
    private void startGetPrjInfo() {
        cancel(wakeDisposable);
        cancel(disposable);
        if (!UserContext.newInstance().isLogin()) {
            return;
        }

        Observable.interval(10, 30, TimeUnit.SECONDS)
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        disposable = d;
                        LoganManager.w_printer(TAG,"startGetPrjInfo onSubscribe");
                    }

                    @Override
                    public void onNext(Long aLong) {
                        LoganManager.w_printer(TAG,"startGetPrjInfo onNext");
                        getPrjInfo();
                    }

                    @Override
                    public void onError(Throwable e) {
                        LoganManager.w_printer(TAG,"startGetPrjInfo onError：" + e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        LoganManager.w_printer(TAG,"startGetPrjInfo onComplete");
                    }
                });
    }

    /**
     * 請求prj數據
     */
    private void getPrjInfo() {
        OkHttp3Utils.get(HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + "printerRecording/get?restaurantId=" + RestaurantInfoManager.newInstance().getRestaurantId())
                .subscribeOn(Schedulers.io())//切换到io线程進行網絡請求
                .observeOn(Schedulers.io())//切換到io线程處理請求結果
                .subscribe(new Observer<String>() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        LoganManager.w_printer(TAG,"getPrjInfo onSubscribe");
                    }

                    @Override
                    public void onNext(String s) {
                        LoganManager.w_printer(TAG,"getPrjInfo onNext: " + s);
                        //請求到數據，停止輪詢，開始打印，在打印完之後再重新開始輪詢
//                        startPrint(s);
                        newPrint(s);
                        //開啟另一個定時，三十秒之後自動請求，避免上面的打印成功或失敗時沒有回調。
                        cancel(wakeDisposable);
                        Observable.timer(30, TimeUnit.SECONDS)
                                .subscribe(new Observer<Long>() {
                                    @Override
                                    public void onSubscribe(Disposable d) {
                                        wakeDisposable = d;
                                        LoganManager.w_printer(TAG,"getPrjInfo timer onSubscribe");
                                    }

                                    @Override
                                    public void onNext(Long aLong) {
                                        LoganManager.w_printer(TAG,"getPrjInfo timer onNext");
                                        startGetPrjInfo();
                                    }

                                    @Override
                                    public void onError(Throwable e) {
                                        LoganManager.w_printer(TAG,"getPrjInfo timer onError: " + e.getMessage());
                                    }

                                    @Override
                                    public void onComplete() {
                                        LoganManager.w_printer(TAG,"getPrjInfo timer onComplete");
                                    }
                                });
                    }

                    @Override
                    public void onError(Throwable e) {
                        LoganManager.w_printer(TAG,"getPrjInfo onError：" + e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        LoganManager.w_printer(TAG,"getPrjInfo onComplete");
                    }
                });
    }

    /**
     * 取消輪詢
     */
    private void cancel(Disposable disposable) {
        if (disposable != null) {
            disposable.dispose();
        }
    }

    private PrinterRoot printerInIt;

    private String json = "{\"success\":true,\"sysTime\":1595066909048,\"data\":{\"K1\":[{\"id\":2461,\"printerDeviceId\":87,\"status\":1,\"orderNo\":\"153201882821850443\",\"orderTime\":2020,\"sender\":\"\",\"person\":0,\"number\":1,\"orderDetailsTime\":\"Jul 10, 2020 11:59:29 AM\",\"orderDetailsId\":48863119,\"productName\":\"紅棗銀耳湯\",\"parentId\":48863115,\"type\":4,\"createTime\":1595066908689,\"productId\":5189,\"requests\":1,\"actualPrinterDeviceId\":87,\"takeFoodCode\":\"H462\",\"billNo\":\"0643\",\"orderType\":7,\"userName\":\"stefan001\"},{\"id\":2461,\"printerDeviceId\":87,\"status\":1,\"orderNo\":\"153201882821850443\",\"orderTime\":2020,\"sender\":\"\",\"person\":0,\"number\":1,\"orderDetailsTime\":\"Jul 10, 2020 11:59:29 AM\",\"orderDetailsId\":48863119,\"productName\":\"紅棗銀耳湯\",\"parentId\":48863115,\"type\":4,\"createTime\":1595066908689,\"productId\":5189,\"requests\":1,\"actualPrinterDeviceId\":87,\"takeFoodCode\":\"H462\",\"billNo\":\"0643\",\"orderType\":7,\"userName\":\"stefan001\"}]}}";

    /**
     * 開始打印
     */
//    private void startPrint(String json) {
//        printDatas.clear();
//        listMap.clear();
//        PrjBean prjBean = JsonUtils.parseObject(json, PrjBean.class);
//        if (prjBean == null || prjBean.getData() == null) {
//            return;
//        }
//        PrintCurrencyBean printCurrencyBean;
//        //有數據，取消輪詢，等待打印結束。
//        cancel(disposable);
//        try {
//            JSONObject jsonObject = new JSONObject(prjBean.getData());
//            //通过迭代器获取这段json当中所有的key值
//            Iterator keys = jsonObject.keys();
//            //然后通过一个循环取出所有的key值
//            while (keys.hasNext()) {
//                String key = String.valueOf(keys.next());
//                //最后就可以通过刚刚得到的key值去解析后面的json了
//                JSONArray dataJson = (JSONArray) jsonObject.get(key);
//                List<PrjBean.DataBean.Bean> datas = JsonUtils.parseArray(dataJson.toString(), PrjBean.DataBean.Bean.class);
//                listMap.put(key, datas);//打印位置和需要打印的數據
//                PrintPrjKitchen.getPrjMap().put(key, datas);
//                printDatas.addAll(datas);
//            }
//        } catch (JSONException e) {
//            e.printStackTrace();
//        }
//        if (printerInIt == null) {
//            printerInIt = PrinterRoot.getPrinterByType(PRINT_KITCHEN);
//        }
//        //獲取打印機列表
//        getPrintList();
////        for (Map.Entry<String, List<PrjBean.DataBean.Bean>> prjMap : listMap.entrySet()) {
////            for (PrinterDeviceBean deviceBean : printerDeviceBeans) {
////                if (prjMap.getKey().equalsIgnoreCase(deviceBean.getName())) {
////                    if ((deviceBean.getPrinterName() != null && deviceBean.getPrinterName().toLowerCase().contains("EPSON".toLowerCase()))
////                            && (deviceBean.getModel() != null && deviceBean.getModel().toLowerCase().contains("TM-U220B".toLowerCase()))) {
////                        //針式打印
////                        //Epson打印機打印，調用對應的方法
////                        EpsonPrint mPrinter = EpsonPrint.getInstance();
////                        mPrinter.initializeObject(GsaCloudApplication.getAppContext(), this);
////                        int paperWidth = 42;
////                        if (deviceBean.getPaperSpecification() != null) {
////                            paperWidth = (int) (Double.parseDouble(deviceBean.getPaperSpecification()) / 6);
////                        }
////                        List<List<PrintInfoBean>> prjBeans = PrintInfoBean.transPrjBean(prjMap.getValue(), prjMap.getKey(), deviceBean, printCurrencyBean);
////                        for (List<PrintInfoBean> prjPrintBean : prjBeans) {
////                            mPrinter.putPrintString(deviceBean.getIp(), paperWidth, prjPrintBean);
////                        }
////                    }
////                }
////            }
////        }
//        if (printerInIt != null) {
//            printerInIt.setmContext(this);
//            for (Map.Entry<String, List<Bitmap>> entry : printerInIt.getPrintBitmap(this, null).entrySet()) {
//                //遍歷所有的需要打印的內容
//                for (PrinterDeviceBean deviceBean : printerDeviceBeans) {
//                    //遍歷打印機列表，找到對應的打印機，沒找到的就不打印
//                    if (entry.getKey().toLowerCase().equals(deviceBean.getName().toLowerCase())) {
////                        if ((deviceBean.getPrinterName() != null && deviceBean.getPrinterName().toLowerCase().contains("EPSON".toLowerCase()))
////                                && (deviceBean.getModel() != null && deviceBean.getModel().toLowerCase().contains("TM-U220B".toLowerCase()))) {
////                            //針式打印機在上面處理
////                        } else {
//                        Log.e(TAG, entry.getKey() + "開始打印IP：" + deviceBean.getIp());
//                        printerInIt.ipDevicePrint(deviceBean, entry.getValue(), this, this);
//                        break;
////                        }
//                    }
//                }
//            }
//        }
//    }

    private int totalPrj;
    private int currentIndex;

    private void newPrint(String json) {
        printDatas.clear();
        Map<String, List<PrjBean.DataBean.Bean>> listMap = new HashMap<>();
        currentIndex = 0;
        totalPrj = 0;
        PrjBean prjBean = JsonUtils.parseObject(json, PrjBean.class);
        if (prjBean == null || prjBean.getData() == null) {
            LoganManager.w_printer(TAG,"newPrint  prjBean == null || prjBean.getData() == null ");
            return;
        }
        //有數據，取消輪詢，等待打印結束。
        cancel(disposable);
        //第一步：解析PRJ數據，格式為 Map<廚房位置，需要打印的數據>
        try {
            JSONObject jsonObject = new JSONObject(prjBean.getData());
            //通过迭代器获取这段json当中所有的key值
            Iterator keys = jsonObject.keys();
            //然后通过一个循环取出所有的key值
            while (keys.hasNext()) {
                String key = String.valueOf(keys.next());
                //最后就可以通过刚刚得到的key值去解析后面的json了
                JSONArray dataJson = (JSONArray) jsonObject.get(key);
                List<PrjBean.DataBean.Bean> datas = JsonUtils.parseArray(dataJson.toString(), PrjBean.DataBean.Bean.class);
                if (datas != null && datas.size() > 0) {
                    //打印位置和需要打印的數據
                    listMap.put(key, datas);
                    //有沒有不需要切紙的
                    boolean isHasNoCut = false;
                    for (PrjBean.DataBean.Bean bean : datas) {
                        if (bean.getStatus() == 2) {
                            //需要切紙，prj總數就+1
                            totalPrj++;
                        } else {
                            isHasNoCut = true;
                        }
                    }
                    if (isHasNoCut) {
                        //有不需要切紙的食物，prj總數+1
                        totalPrj++;
                    }
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
            LoganManager.w_printer(TAG,"newPrint  JSONException: " + e.getMessage());
        }
        setPrjIndex(listMap, totalPrj);
        initPrinterDevices();
        foreachPrint(listMap);
    }

    private Map<String, List<PrjBean.DataBean.Bean>> setPrjIndex(Map<String, List<PrjBean.DataBean.Bean>> listMap, int totalPrj) {
        LoganManager.w_printer(TAG,"setPrjIndex totalPrj: "+totalPrj);
        for (Map.Entry<String, List<PrjBean.DataBean.Bean>> prjMap : listMap.entrySet()) {
            for (PrjBean.DataBean.Bean bean : prjMap.getValue()) {
                bean.setTotalPrj(totalPrj);
                if (bean.getStatus() == 2) {
                    currentIndex++;
                } else {
                    if (currentIndex == 0) {
                        currentIndex = 1;
                    }
                }
                bean.setCurrentIndex(currentIndex);
            }
            currentIndex++;
        }
        return listMap;
    }

    private Map<String, List<PrjBean.DataBean.Bean>> printDataToMap(List<PrjBean.DataBean.Bean> printData) {
        Map<String, List<PrjBean.DataBean.Bean>> map = new HashMap<>();
        PrinterDeviceBean defaultPainter = null;
        initPrinterDevices();
        //獲得默認的打印機
        for (PrinterDeviceBean printerDeviceBean : printerDeviceBeans) {
            if (printerDeviceBean.getType() == 2) {
                defaultPainter = printerDeviceBean;
                break;
            }
        }

        //將prj集合通過打印位置轉成map
        for (PrjBean.DataBean.Bean bean : printData) {
            if (TextUtil.isNotEmptyOrNullOrUndefined(bean.getPrintPosition())) {
                List<PrjBean.DataBean.Bean> mapByKey = map.get(bean.getPrintPosition());
                if (mapByKey != null) {
                    mapByKey.add(bean);
                } else {
                    map.put(bean.getPrintPosition(), Collections.singletonList(bean));
                }
            } else if (defaultPainter != null) {
                //沒有打印位置的，由默認的位置去打印
                bean.setPrintPosition(defaultPainter.getName());
                List<PrjBean.DataBean.Bean> mapByKey = map.get(bean.getPrintPosition());
                if (mapByKey != null) {
                    mapByKey.add(bean);
                } else {
                    map.put(bean.getPrintPosition(), Collections.singletonList(bean));
                }
            } else {
                ToastUtils.show(this, "未配置打印機");
            }
        }
        return map;
    }

    private void foreachPrint(Map<String, List<PrjBean.DataBean.Bean>> listMap) {
        LoganManager.w_printer(TAG,"foreachPrint");
        //雙重遍歷可以以後優化
        //第二步：遍歷 Map<廚房位置，需要打印的數據>，通過廚房位置找到對應的打印機，並且通過數據拿到對應的通用配置
        for (Map.Entry<String, List<PrjBean.DataBean.Bean>> prjMap : listMap.entrySet()) {
            for (PrinterDeviceBean deviceBean : printerDeviceBeans) {
                //遍歷得到當前打印機，如果沒找到打印機，就不打印
                if (prjMap.getKey().toLowerCase().equals(deviceBean.getName().toLowerCase())) {
                    //如果PRJ數據的廚房位置和打印機名稱相同，則就是這台打印機打印
                    //通過需要打印的數據，得到通用配置
                    PrintCurrencyBean printCurrencyBean = getPrintCurrencyBean(prjMap.getValue());
                    //將通用配置設置給了對應的打印機對象後，得到新的打印機對象
                    PrinterDeviceBean configPrinterDeviceBean = MyPrintUtils.configPrinterProperties(printCurrencyBean, deviceBean);
                    // 判斷打印機的類型，調用不同的打印方式，
                    // 這裡已經拿到這台打印機需要打印的所有數據，為prjMp.getValue()，
                    // 生成對應的打印數據，除了針式打印機，其他都生成bitmap
                    generatePrintData(prjMap.getKey(), prjMap.getValue(), configPrinterDeviceBean);
                }
            }
        }
    }

    private void initPrinterDevices() {
        LoganManager.w_printer(TAG,"initPrinterDevices");
        //讀取打印機和通用配置，可以優化
        //獲取所有打印機
        PrinterDeviceDaoUtils printerDeviceDaoUtils = new PrinterDeviceDaoUtils(this);
        printerDeviceBeans = printerDeviceDaoUtils.queryAllPrinterDeviceBean();
        //獲取所有通用配置
        printCurrencyBeans = MyPrintUtils.getPrintCurrencyBeans(this);
    }

    private PrintCurrencyBean getPrintCurrencyBean(List<PrjBean.DataBean.Bean> beans) {
        PrintCurrencyBean printCurrencyBean = null;
        if (beans != null && beans.size() > 0) {
            if (beans.get(0).getOrderType() == 1 || beans.get(0).getOrderType() == 3) {
                //堂食，skyorder
                printCurrencyBean = MyPrintUtils.getPrintCurrencyBeanByType(mContext, 1);
            } else {
                //外賣
                printCurrencyBean = MyPrintUtils.getPrintCurrencyBeanByType(mContext, 2);
            }
        }
        return printCurrencyBean;
    }

    //打印回調了多少次都要記錄下來，所以一次只能傳遞一張prj過去，等到打印回調再打第二張
    private String EpsIds = "";

    /**
     * 生成用於打印的prj的Bitmap
     */
    private void generatePrintData(String key, List<PrjBean.DataBean.Bean> beans, PrinterDeviceBean printerDeviceBean) {

        LoganManager.w_printer(TAG,"generatePrintData PrinterDeviceBean deviceType: " + printerDeviceBean.getPrinterDeviceType()
                + "  ip: " + printerDeviceBean.getIp());

        if (isPinPrinter(printerDeviceBean) && printerDeviceBean.getPrinterDeviceType() == PRINT_IP) {
            //針式打印機並且打印機類型為IP打印，生成獨特的格式
            List<Map<String, Bitmap>> bitmapMaps = generatePrintMaps(key, beans, printerDeviceBean);
            EpsIds = getPrintIds(bitmapMaps);
            //第一個參數是機型，第二個參數是語言，
            Printer mPrinter = null;
            try {
                mPrinter = new Printer(Printer.TM_U220, Printer.MODEL_KOREAN, this);
                mPrinter.setReceiveEventListener(this);
            } catch (Epos2Exception e) {
                e.printStackTrace();
                updatePrjFailure(getPrintIds(bitmapMaps));
            }
            if (mPrinter != null) {
                try {
                    mPrinter.clearCommandBuffer();
                    for (int i = 0; i < bitmapMaps.size(); i++) {
                        for (Map.Entry<String, Bitmap> mapEntry : bitmapMaps.get(i).entrySet()) {
                            mPrinter.addImage(mapEntry.getValue(), 0, 0,
                                    mapEntry.getValue().getWidth(),
                                    mapEntry.getValue().getHeight(),
                                    Printer.COLOR_1,
                                    Printer.MODE_MONO_HIGH_DENSITY,//高密度
                                    Printer.HALFTONE_DITHER,//半色調抖動
                                    Printer.PARAM_DEFAULT,
                                    Printer.COMPRESS_NONE);//壓縮
                            mPrinter.addCut(Printer.CUT_FEED);
                        }
                    }
                } catch (Epos2Exception e) {
                    e.printStackTrace();
                    updatePrjFailure(getPrintIds(bitmapMaps));
                    mPrinter.clearCommandBuffer();
                }
                try {
                    mPrinter.connect("TCP:" + printerDeviceBean.getIp(), Printer.PARAM_DEFAULT);
                    mPrinter.sendData(Printer.PARAM_DEFAULT);
                } catch (Exception e) {
                    e.printStackTrace();
                    updatePrjFailure(getPrintIds(bitmapMaps));
                    while (true) {
                        try {
                            mPrinter.disconnect();
                            break;
                        } catch (final Exception ex) {
                            if (ex instanceof Epos2Exception) {
                                //Note: If printer is processing such as printing and so on, the disconnect API returns ERR_PROCESSING.
                                if (((Epos2Exception) ex).getErrorStatus() == Epos2Exception.ERR_PROCESSING) {

                                } else {
                                    break;
                                }
                            } else {
                                break;
                            }
                        }
                    }
                    mPrinter.clearCommandBuffer();
                    mPrinter.setReceiveEventListener(null);
                }
            } else {
                updatePrjFailure(EpsIds);
            }
        } else if (printerDeviceBean.getPrinterDeviceType() == PRINT_PRJ_PC) {

        } else {
            List<Map<String, Bitmap>> bitmapMaps = null;
            if (printerDeviceBean.getPrinterDeviceType() == PRINT_LOCAL && PrintConstans.PRINT_MODEL_WISEPOS.contains(Build.MODEL)) {
                //本機打印並且是BBPOS

            } else if (printerDeviceBean.getPrinterDeviceType() == PRINT_PRJ_PC) {
                //PRJ模式，生成PRJ文件到共享的電腦上
                ThreadPoolManager.getInstence().putExecutableTasks(() -> {
                    List<PrjBean.DataBean.Bean> noCutList = new ArrayList<>();
                    List<PrjBean.DataBean.Bean> cutList = new ArrayList<>();
                    for (PrjBean.DataBean.Bean bean : beans) {
                        if (bean.getStatus() == 2) {
                            //需要切紙
                            cutList.add(bean);
                        } else {
                            //不需要切紙
                            noCutList.add(bean);
                        }
                    }

                    String rootPath = "smb://" + printerDeviceBean.getPrintPath() + "/";
                    try {
                        SmbFile smbFile = new SmbFile(rootPath);
                        smbFile.connect();
                        if (smbFile.isDirectory()) {
                            for (int i = 0; i < cutList.size(); i++) {
                                String newFilePath = rootPath + System.currentTimeMillis() + ".prj";
                                SmbFile createFile = new SmbFile(newFilePath);
                                createFile.createNewFile();
                                if (createFile.exists()) {
                                    PrintStream ps = new PrintStream(new SmbFileOutputStream(createFile));

                                    // 往文件里写入字符串
                                    String prjInfo = "[printType]ptKitchen\n" +
                                            "[table]4\n" +
                                            "[date]落單時間:11-13 17:48\n" +
                                            "[waiter]n5 收銀員\n" +
                                            "[KP]K2烤爐\n" +
                                            "[PAX]人數:2\n" +
                                            "[line]\n" +
                                            "[food_1]1 包子(主項)\n" +
                                            "[food_1]1 包子(主項)\n" +
                                            "[table2]4\n";
                                    ps.println(prjInfo);
                                }
                            }
                            if (noCutList.size() > 0) {
                                String newFilePath = rootPath + System.currentTimeMillis() + ".prj";
                                SmbFile createFile = new SmbFile(newFilePath);
                                createFile.createNewFile();
                                if (createFile.exists()) {
                                    PrintStream ps = new PrintStream(new SmbFileOutputStream(createFile));
                                    PrjBean.DataBean.Bean bean = noCutList.get(0);

                                    String tableName = "";
                                    if (TextUtil.isEmptyOrNullOrUndefined(bean.getTableName())) {
                                        if (bean.getOrderType() == 7) {
                                            tableName = "自取";
                                        } else {
                                            tableName = "外賣";
                                        }
                                    } else {
                                        tableName = bean.getTableName();
                                    }
                                    // 往文件里写入字符串
//                                    String prjInfo = "[printType]ptKitchen\n" +
//                                            "[table]4\n" +
//                                            "[date]落單時間:11-13 17:48\n" +
//                                            "[waiter]n5 收銀員\n" +
//                                            "[KP]K2烤爐\n" +
//                                            "[PAX]人數:2\n" +
//                                            "[line]\n" +
//                                            "[food_1]1 包子(主項)\n" +
//                                            "[food_1]1 包子(主項)\n" +
//                                            "[table2]4\n";
                                    String prjInfo = "[printType]ptKitchen\n" +
                                            "[table]" + tableName + "\n" +
                                            "[date]落單時間:11-13 17:48\n" +
                                            "[waiter]n5 收銀員\n" +
                                            "[KP]K2烤爐\n" +
                                            "[PAX]人數:2\n" +
                                            "[line]\n" +
                                            "[food_1]1 包子(主項)\n" +
                                            "[food_1]1 包子(主項)\n" +
                                            "[table2]" + tableName + "\n";
                                    ps.println(prjInfo);
                                }
                            }

                        } else {
                            ToastUtils.show(this, "PRJ輸出路徑必須為文件夾");
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            } else {
                bitmapMaps = generatePrintMaps(key, beans, printerDeviceBean);
                switch (printerDeviceBean.getPrinterDeviceType()) {
                    case PRINT_IP:
                        //IP打印
                        ipPrint(printerDeviceBean, bitmapMaps);
                        break;
                    case PRINT_LOCAL:
                        //本地N5或Sunmi打印
                        locationPrint(bitmapMaps);
                        break;
                    case PRINT_USB:
                        //USB打印
                        usbPrint(bitmapMaps);
                        break;
                    default:
                        break;
                }
            }
        }
    }


    /**
     * ip設備打印
     */
    public void ipPrint(PrinterDeviceBean printerDeviceBean, List<Map<String, Bitmap>> bitmapMaps) {
        LoganManager.w_printer(TAG,"ipPrint: " + printerDeviceBean.getPrinterDeviceType()
                + "  ip: " + printerDeviceBean.getIp());
        for (int i = 0; i < bitmapMaps.size(); i++) {
            PrintExecutor executor = new PrintExecutor(printerDeviceBean);
            int finalI = i;
            executor.setOnPrjPrintResultListener((errorCode, ids) -> {
                switch (errorCode) {
                    case PrintSocketHolder.ERROR_0:
                        //更新狀態
                        LoganManager.w_printer(TAG,"ipPrint  errorCode ERROR_0 i: " + finalI);
                        updatePrjSuccess(ids);
                        Log.e(TAG, "打印成功");
                        break;
                    case PrintSocketHolder.ERROR_2:
                        LoganManager.w_printer(TAG,"ipPrint  errorCode ERROR_2 i: " + finalI);
                        Log.e(TAG, "创建Socket失败");
                        updatePrjFailure(ids);
                        break;
                }
            });
            LoganManager.w_printer(TAG,"ipPrint for doPrinterRequestAsync i:" + i);
            PrjPrintMaker maker = new PrjPrintMaker(bitmapMaps.get(i));
            executor.doPrinterRequestAsync(maker);
        }
    }

    /**
     * usb打印
     */
    public void usbPrint(List<Map<String, Bitmap>> bitmapMaps) {
        for (int i = 0; i < bitmapMaps.size(); i++) {
            for (Map.Entry<String, Bitmap> bitmapMap : bitmapMaps.get(i).entrySet()) {
                final String key = bitmapMap.getKey();
                UsbPrint usbPrint = new UsbPrint(mContext, (code, printId) -> {
                    //打印結果
                    if (code == SendResultCode.SEND_SUCCESS) {
                        Log.e("ddd", "打印成功");
                        updatePrjSuccess(key);
                    } else if (code == SendResultCode.SEND_FAILED) {
                        Log.e("ddd", "打印失敗");
                        updatePrjFailure(key);
                    }
                });
                if (mUsbPrinters != null && mUsbPrinters.size() > 0) {
                    EscCommand esc = new EscCommand();
                    ArrayList<byte[]> bytes = new ArrayList<>();
                    try {
                        bytes.addAll(new PrinterWriter58mm().getImageByte(bitmapMap.getValue()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    bytes.add(esc.getPrintAndFeedLines((byte) 8));
                    bytes.add(esc.getCutPaper());
                    bytes.add(esc.getCleanCache());
                    usbPrint.sendPrintCommand(mUsbPrinters.get(0), bytes);
                } else {
                    //打印失敗
                    updatePrjFailure(key);
                }
            }
        }
    }

    /**
     * 本機打印
     */
    public void locationPrint(List<Map<String, Bitmap>> bitmapMaps) {
        String model = Build.MODEL;
        if (PrintConstans.PRINT_MODEL_V2.contains(model)) {
            sunmiPrint(bitmapMaps);
        } else if (PrintConstans.PRINT_MODEL_N5.contains(model)) {
            //N5打印
            n5Print(bitmapMaps);
        } else if (PrintConstans.PRINT_MODEL_WISEPOS.contains(model)) {
            //BBPOS，生成data
            ToastUtils.show(this, "BBPOS不支持打印處方單");
        } else {
            updatePrjFailure(getPrintIds(bitmapMaps));
        }
    }

    private String getPrintIds(List<Map<String, Bitmap>> bitmapMaps) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < bitmapMaps.size(); i++) {
            for (Map.Entry<String, Bitmap> mapEntry : bitmapMaps.get(i).entrySet()) {
                stringBuilder.append(mapEntry.getKey());
                if (stringBuilder.toString().lastIndexOf(",") != stringBuilder.length() - 1) {
                    //如果最後一位不是逗號，才添加
                    stringBuilder.append(",");
                }
            }
        }
        return stringBuilder.toString();
    }

    private final static int SUNMI_PAPER_WIDTH = 360;//商米打印機紙張寬度
    private final static int N5_PAPER_WIDTH = 500;//N5打印機紙張寬度

    /**
     * 商米打印
     */
    private void sunmiPrint(List<Map<String, Bitmap>> bitmapMaps) {
        //商米打印
        for (int i = 0; i < bitmapMaps.size(); i++) {
            for (Map.Entry<String, Bitmap> mapEntry : bitmapMaps.get(i).entrySet()) {
                final String ids = mapEntry.getKey();
                AidlUtil.getInstance().printBitmap(mapEntry.getValue(), new InnerResultCallbcak() {
                    @Override
                    public void onRunResult(boolean isSuccess) {
                        //返回接⼝执⾏的情况(并⾮真实打印):成功或失败
                        if (isSuccess) {
                            updatePrjSuccess(ids);
                        } else {
                            updatePrjFailure(ids);
                        }
                    }

                    @Override
                    public void onReturnString(String result) {
                        //部分接⼝会异步返回查询数据
                    }

                    @Override
                    public void onRaiseException(int code, String msg) {
                        //接⼝执⾏失败时，返回的异常状态
                    }

                    @Override
                    public void onPrintResult(int code, String msg) {
                        //事务模式下真实的打印结果返回
                    }
                });
            }
        }
    }

    /**
     * n5打印
     */
    private void n5Print(List<Map<String, Bitmap>> bitmapMaps) {
        for (int i = 0; i < bitmapMaps.size(); i++) {
            for (Map.Entry<String, Bitmap> mapEntry : bitmapMaps.get(i).entrySet()) {
                final String ids = mapEntry.getKey();
                try {
                    PrinterUtil.appendImage(mapEntry.getValue(), PrinterConstant.ALIGN_CENTER);
                    PrinterUtil.appendPrnStr("\n", 24, PrinterConstant.ALIGN_CENTER, false);
                    PrinterUtil.appendPrnStr("\n", 24, PrinterConstant.ALIGN_CENTER, false);
                    PrinterUtil.startPrint(true, new IOnPrintCallback.Stub() {
                        @Override
                        public void onPrintResult(int i) {
                            if (i == 0) {
                                //打印成功
                                updatePrjSuccess(ids);
                            } else {
                                //打印失敗
                                updatePrjFailure(ids);
                            }
                        }

                        @Override
                        public IBinder asBinder() {
                            return this;
                        }
                    });
                } catch (RemoteException e) {
                    e.printStackTrace();
                    updatePrjFailure(ids);
                }
            }
        }
    }

    /**
     * 生成打印需要的Map集合，Map的key是這張PRJ的所有PRJ記錄的id，然後會同時打印多張，所以是一個List集合
     *
     * @param key   打印位置
     * @param beans 打印的食品和其他的一些信息
     */
    private List<Map<String, Bitmap>> generatePrintMaps(String key, List<PrjBean.DataBean.Bean> beans, PrinterDeviceBean printerDeviceBean) {
        PrintPrjKitchen printPrjKitchen = new PrintPrjKitchen();
        //這個Map的key是這張PRJ的所有PRJ記錄的id，然後會同時打印多張，所以是一個List集合
        List<Map<String, Bitmap>> bitmapMaps = new ArrayList<>();
        List<PrjBean.DataBean.Bean> noCut = new ArrayList<>();
        //不帶*號，所有同樣廚房位置的食品都在一張紙上
        for (PrjBean.DataBean.Bean bean : beans) {
            if (bean.getStatus() == 2) {
                Map<String, Bitmap> map = new HashMap<>();
                //帶*，需要切紙的，單獨放一張紙
                map.put(bean.getId() + "", printPrjKitchen.getKitChenPrintBitmap(mContext, key, bean, printerDeviceBean));
                bitmapMaps.add(map);
            } else {
                noCut.add(bean);
            }
        }
        if (noCut.size() > 0) {
            Map<String, Bitmap> map = new HashMap<>();
            //遍歷拿到這張紙所有的PRJ記錄ID
            StringBuilder stringBuffer = new StringBuilder();
            for (PrjBean.DataBean.Bean noCutData : noCut) {
                stringBuffer.append(noCutData.getId());
                stringBuffer.append(",");
            }
            map.put(stringBuffer.toString(), printPrjKitchen.getKitChenPrintBitmap(mContext, key, noCut, printerDeviceBean));
            bitmapMaps.add(map);
        }
        return bitmapMaps;
    }

    /**
     * 是否為針式打印機
     *
     * @param printerDeviceBean 打印機實體類
     * @return true是
     */
    private boolean isPinPrinter(PrinterDeviceBean printerDeviceBean) {
        return (printerDeviceBean != null && printerDeviceBean.getPrinterName() != null && printerDeviceBean.getPrinterName().toLowerCase().contains("EPSON".toLowerCase()))
                && (printerDeviceBean.getModel() != null && printerDeviceBean.getModel().toLowerCase().contains("TM-U220B".toLowerCase()));
    }

    private void updatePrjSuccess(String ids) {
        updatePrjState(UpdateBean.ALREADY_PRINT, ids);
    }

    private void updatePrjFailure(String ids) {
        updatePrjState(UpdateBean.NO_PRINT, ids);
    }

    /**
     * @param printState 打印狀態 1未打印 2打印中 3已打印
     */
    private void updatePrjState(int printState, String ids) {
        if (printDatas == null) {
            return;
        }
        Long time = null;
        if (printState == 3) {
            time = TimeUtils.getCurrentTimeInLong();
        }

        List<UpdateBean> updateBeans = new ArrayList<>();
        String[] idArrays = ids.split(",");
        for (String id : idArrays) {
            updateBeans.add(new UpdateBean(id, printState, time));
        }
        String json = JsonUtils.toJson(updateBeans);
        Log.e(TAG, "" + json);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json);

        //打印過後，直接再讀數據，不用管是否已更新狀態。
        startGetPrjInfo();
        OkHttp3Utils.post(HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + "printerRecording/update", requestBody)
                .subscribeOn(Schedulers.io())//切换到io线程進行網絡請求
                .observeOn(AndroidSchedulers.mainThread())//切換到主線程處理請求結果
                .subscribe(new Observer<String>() {

                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(String s) {
                        Log.e(TAG, "修改打印狀態：" + s);
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onComplete() {

                    }
                });
        EpsIds = "";
    }

    @Override
    public void onPtrReceive(Printer printer, int i, PrinterStatusInfo printerStatusInfo, String s) {
        //針式打印回調
        if (i == 0) {
            //打印成功
            updatePrjSuccess(EpsIds);
        } else {
            //打印失敗
            updatePrjFailure(EpsIds);
        }
        startGetPrjInfo();
    }
}
