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.text.TextUtils;
import android.util.Log;

import androidx.annotation.Nullable;

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.printer.AidlUtil;
import com.gingersoft.gsa.cloud.common.printer.plugins.PrinterPlugins;
import com.gingersoft.gsa.cloud.common.utils.JsonUtils;
import com.gingersoft.gsa.cloud.common.utils.gson.GsonUtils;
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.EpsonPrint;
import com.joe.print.mvp.print.PrintPrjKitchen;
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 com.xuexiang.rxutil2.rxjava.RxJavaUtils;

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

import java.io.IOException;
import java.io.PrintStream;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observer;
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 Disposable pollDisposable;
    private Disposable wakeDisposable;
    private Context mContext;
    private String TAG = "Prj";

    @Override
    public void onCreate() {
        super.onCreate();
        LoganManager.w_printer(TAG, "PrjService onCreate");
        mContext = this;
        initUsbPrint();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mContext = this;
        LoganManager.w_printer(TAG, "PrjService onStartCommand");
        //開始請求
        startGetPrjInfo();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        printerFinder.unregisterReceiver();
        PrintSocketHolder.getInstance().closeSocket();
        //關閉針式連接
        EpsonPrint.getInstance().disconnectPrinter();
        EpsonPrint.getInstance().finalizeObject();
        LoganManager.w_printer(TAG, "PrjService onDestroy");
    }

    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();
    }

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

    /**
     * 開啟輪詢查詢prj數據
     */
    private void startGetPrjInfo() {
        cancel(wakeDisposable);
        cancel(pollDisposable);
        if (!UserContext.newInstance().isLogin()) {
            LoganManager.w_printer(TAG, "PrjService 用户未登录!");
            return;
        }
        pollDisposable = RxJavaUtils.polling(10, 30, TimeUnit.SECONDS)
                .subscribe(aLong -> {
                    LoganManager.w_printer(TAG, "獲取Prj數據-->");
                    //輪詢時，關閉打印機連接，避免一直佔用打印機socket連接
                    EpsonPrint.getInstance().disconnectPrinter();
                    EpsonPrint.getInstance().finalizeObject();
                    //獲取prj數據
                    getPrjInfo();
                });
    }

    /**
     * 請求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, "獲取Prj數據 Disposable");
                    }

                    @Override
                    public void onNext(String prjInfo) {
                        LoganManager.w_printer(TAG, "獲取Prj數據 onNext: " + prjInfo);
                        //請求到數據，停止輪詢，開始打印，在打印完之後再重新開始輪詢
                        newPrint(prjInfo);
                        //開啟另一個定時，三十秒之後自動請求，避免上面的打印成功或失敗時沒有回調。
                        cancel(wakeDisposable);
                        wakeDisposable = RxJavaUtils.delay(30, TimeUnit.SECONDS)
                                .subscribe(aLong -> {
                                    LoganManager.w_printer(TAG, "輪詢獲取Prj數據-->");
                                    startGetPrjInfo();
                                });
                    }

                    @Override
                    public void onError(Throwable e) {
                        LoganManager.w_printer(TAG, "獲取Prj數據 onError：" + e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        LoganManager.w_printer(TAG, "獲取Prj數據 onComplete");
                    }
                });
    }

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

    private int totalPrj;
    private int currentIndex;

    private void newPrint(String json) {
        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(pollDisposable);
        //第一步：解析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);
                    //計算本次打印的prj總張數有沒有不需要切紙的
                    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);
        foreachPrint(listMap, getPrinterDevices());
    }

    private Map<String, List<PrjBean.DataBean.Bean>> setPrjIndex(Map<String, List<PrjBean.DataBean.Bean>> listMap, int totalPrj) {
        for (Map.Entry<String, List<PrjBean.DataBean.Bean>> prjMap : listMap.entrySet()) {
            //上一個對象是否切紙
            boolean lastIsCute = false;
            for (PrjBean.DataBean.Bean bean : prjMap.getValue()) {
                bean.setTotalPrj(totalPrj);
                if (bean.getStatus() == 2) {
                    //要切紙，紙張數+1
                    currentIndex++;
                    lastIsCute = true;
                } else {
                    if (currentIndex == 0) {
                        currentIndex = 1;
                    } else if (lastIsCute) {
                        //如果上一張切紙，那這一張下標就要+1
                        currentIndex++;
                    }
                    lastIsCute = false;
                }
                bean.setCurrentIndex(currentIndex);
            }
            currentIndex++;
        }
        return listMap;
    }

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

    /**
     * 獲取打印設備
     *
     * @return 打印機列表
     */
    private List<PrinterDeviceBean> getPrinterDevices() {
        //讀取打印機和通用配置，可以優化
        //獲取所有打印機
        PrinterDeviceDaoUtils printerDeviceDaoUtils = new PrinterDeviceDaoUtils(this);
        LoganManager.w_printer(TAG, "获取打印設備信息");
        return printerDeviceDaoUtils.queryAllPrinterDeviceBean();
    }

    /**
     * 獲取通用打印配置
     *
     * @param beans prj數據
     * @return 通用配置
     */
    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的Bitmap
     */
    private void generatePrintData(String key, List<PrjBean.DataBean.Bean> beans, PrinterDeviceBean printerDeviceBean) {

        int orderType = 1;
        if (beans != null && beans.size() > 0) {
            orderType = beans.get(0).getOrderType();
        }
        PrinterPlugins.getOnPrinterFlowHandler().onPrinterDataBefore(orderType, PrintConstans.PRINT_KITCHEN, GsonUtils.GsonString(beans), GsonUtils.GsonString(printerDeviceBean));

        if (isPinPrinter(printerDeviceBean) && printerDeviceBean.getPrinterDeviceType() == PRINT_IP) {
            //針式打印
            stylusPrinting(key, beans, printerDeviceBean);
        } else if (printerDeviceBean.getPrinterDeviceType() == PRINT_PRJ_PC) {
            //prj模式，生成文件到共享電腦上
            prjToPc(beans, printerDeviceBean);
        } else if (printerDeviceBean.getPrinterDeviceType() == PRINT_LOCAL && PrintConstans.PRINT_MODEL_WISEPOS.contains(Build.MODEL)) {
            //本機打印並且是BBPOS

        } else {
            List<Map<String, Bitmap>> bitmapMaps = generatePrintMaps(key, beans, printerDeviceBean);
            switch (printerDeviceBean.getPrinterDeviceType()) {
                case PRINT_IP:
                    LoganManager.w_printer(TAG, "開始IP打印-->");
                    //IP打印
                    ipPrint(printerDeviceBean, bitmapMaps);
                    break;
                case PRINT_LOCAL:
                    //本地N5或Sunmi打印
                    locationPrint(bitmapMaps);
                    break;
                case PRINT_USB:
                    LoganManager.w_printer(TAG, "開始USB打印-->");
                    //USB打印
                    usbPrint(bitmapMaps);
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * PRJ模式，生成PRJ文件到共享的電腦上
     *
     * @param beans             prj數據
     * @param printerDeviceBean 打印設備
     */
    private void prjToPc(List<PrjBean.DataBean.Bean> beans, PrinterDeviceBean printerDeviceBean) {
        //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();
            }
        });
    }

    /**
     * 針式打印機
     *
     * @param key               廚房位置
     * @param beans             prj數據
     * @param printerDeviceBean 打印機
     */
    private void stylusPrinting(String key, List<PrjBean.DataBean.Bean> beans, PrinterDeviceBean printerDeviceBean) {
        //針式打印機並且打印機類型為IP打印，生成獨特的格式
        List<Map<String, Bitmap>> bitmapMaps = generatePrintMaps(key, beans, printerDeviceBean);
        //將打印的圖片保存到手機中
        hookPrinterBitmap(bitmapMaps, beans);

        boolean initResult = EpsonPrint.getInstance().initializeObject(this, this, errorCode -> updatePrjState(errorCode, getPrintIds(bitmapMaps)));
        if (!initResult) {
            //初始化打印機失敗
            PrinterPlugins.getOnPrinterFlowHandler().connectionError(new ConnectException("初始化針式打印機失敗"));
            updatePrjState(UpdateBean.FAIL_EPSON_INIT, getPrintIds(bitmapMaps));
            return;
        }
        PrinterPlugins.getOnPrinterFlowHandler().connectionBefore("針式打印機" + bitmapMaps.size(), printerDeviceBean.getIp(), printerDeviceBean.getPort(), 0, 0);
        Log.d("eee", "針式打印機本次PRJ數量：" + bitmapMaps.size());
        for (int i = 0; i < bitmapMaps.size(); i++) {
            for (Map.Entry<String, Bitmap> mapEntry : bitmapMaps.get(i).entrySet()) {
                EpsonPrint.getInstance().putPrintData(printerDeviceBean.getIp(), mapEntry.getValue(), mapEntry.getKey());
            }
        }
    }

    /**
     * ip設備打印
     */
    public void ipPrint(PrinterDeviceBean printerDeviceBean, List<Map<String, Bitmap>> bitmapMaps) {
        for (int i = 0; i < bitmapMaps.size(); i++) {
//            if (executor == null) {
//                executor = new PrintExecutor().setOnPrjPrintResultListener((errorCode, ids) -> {
//                    switch (errorCode) {
//                        case PrintSocketHolder.ERROR_0:
//                            //更新狀態
//                            updatePrjSuccess(ids);
//                            break;
//                        case PrintSocketHolder.ERROR_2:
//                            updatePrjFailure(ids);
//                            break;
//                    }
//                });
//            }
//            PrjPrintMaker maker = new PrjPrintMaker(bitmapMaps.get(i), printerDeviceBean.getIp(), printerDeviceBean.getPort());
//            executor.doPrinterRequestAsync(maker);
            PrintExecutor.getInstance().setOnPrjPrintResultListener(this::updatePrjState);
            PrjPrintMaker maker = new PrjPrintMaker(bitmapMaps.get(i), printerDeviceBean.getIp(), printerDeviceBean.getPort());
            PrintExecutor.getInstance().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) {
                        PrinterPlugins.getOnPrinterFlowHandler().onPrintSuccess();
                        updatePrjSuccess(key);
                    } else if (code == SendResultCode.SEND_FAILED) {
                        PrinterPlugins.getOnPrinterFlowHandler().onPrintError(new Exception("發送打印數據失敗"), code);
                        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)) {
            //商米打印
            LoganManager.w_printer(TAG, "開始商米打印-->");
            sunmiPrint(bitmapMaps);
        } else if (PrintConstans.PRINT_MODEL_N5.contains(model)) {
            //N5打印
            LoganManager.w_printer(TAG, "開始N5打印-->");
            n5Print(bitmapMaps);
        } else if (PrintConstans.PRINT_MODEL_WISEPOS.contains(model)) {
            //BBPOS，生成data
            ToastUtils.show(this, "BBPOS不支持打印廚房單");
        } else {
            updatePrjState(UpdateBean.FAIL_LOCAL_PRINT_NOT_DEVICE, getPrintIds(bitmapMaps));
        }
    }

    /**
     * 獲取本次打印的prj id
     *
     * @return prj的id集合
     */
    private String getPrintIdsByPrjData(List<PrjBean.DataBean.Bean> prjBeans) {
        StringBuilder stringBuilder = new StringBuilder();
        for (PrjBean.DataBean.Bean prjBean : prjBeans) {
            stringBuilder.append(prjBean.getId());
            if (stringBuilder.toString().lastIndexOf(",") != stringBuilder.length() - 1) {
                //如果最後一位不是逗號，才添加
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 獲取本次打印的prj id
     *
     * @param bitmapMaps
     * @return
     */
    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()).append(",");
            }
        }
        if (stringBuilder.length() <= 0) {
            return "";
        }
        //最後一位是逗號，去掉
        return stringBuilder.substring(0, stringBuilder.length() - 1);
    }

    /**
     * 商米打印
     */
    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) {
                        LoganManager.w_printer(TAG, "商米onRunResult: " + isSuccess);
                        //返回接⼝执⾏的情况(并⾮真实打印):成功或失败
                        if (isSuccess) {
                            updatePrjSuccess(ids);
                        } else {
                            updatePrjFailure(ids);
                        }
                    }

                    @Override
                    public void onReturnString(String result) {
                        //部分接⼝会异步返回查询数据
                        LoganManager.w_printer(TAG, "商米onReturnString: " + result);
                    }

                    @Override
                    public void onRaiseException(int code, String msg) {
                        //接⼝执⾏失败时，返回的异常状态
                        PrinterPlugins.getOnPrinterFlowHandler().onPrintError(new Exception(msg), code);
                        LoganManager.w_printer(TAG, "商米onRaiseException: " + code + msg);
                        switch (code) {
                            case 3:
                                //通訊異常
                                updatePrjState(UpdateBean.FAIL_PRINT_BY_NOT_CONNECT, ids);
                                break;
                            case 4:
                                //缺紙
                                updatePrjState(UpdateBean.FAIL_NOT_PAPER, ids);
                                break;
                            case 5:
                                //過熱
                                updatePrjState(UpdateBean.FAIL_SUNMI_PRINT_HOT, ids);
                                break;
                            case 6:
                                //蓋子未合上
                                updatePrjState(UpdateBean.FAIL_LID_NOT_CLOSED_SUNMI, ids);
                                break;
                            case 7:
                                //切刀異常
                                updatePrjState(UpdateBean.FAIL_CUT_ABNORMAL_SUNMI, ids);
                                break;
                            case 9:
                                //黑標異常
                                updatePrjState(UpdateBean.FAIL_SUNMI_NO_BLACK_MARK_DETECTED, ids);
                                break;
                            default:
                                updatePrjState(UpdateBean.FAIL_PRINT_BY_NOT_FIND_DEVICE_SUNMI, ids);
                                break;
                        }
                    }

                    @Override
                    public void onPrintResult(int code, String msg) {
                        //事务模式下真实的打印结果返回
                        LoganManager.w_printer(TAG, "商米onPrintResult: " + 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) {
                            LoganManager.w_printer(TAG, "N5 onPrintResult: " + i);
                            if (i == 0) {
                                //打印成功
                                updatePrjSuccess(ids);
                            } else {
                                //打印失敗
                                PrinterPlugins.getOnPrinterFlowHandler().onPrintError(new Exception(UpdateBean.getStateByCode(i)), i);
                                updatePrjState(i, ids);
                            }
                        }

                        @Override
                        public IBinder asBinder() {
                            LoganManager.w_printer(TAG, "N5 asBinder");
                            return this;
                        }
                    });
                } catch (RemoteException e) {
                    e.printStackTrace();
                    PrinterPlugins.getOnPrinterFlowHandler().onPrintError(e, i);
                    LoganManager.w_printer(TAG, "N5 RemoteException :" + e.getLocalizedMessage());
                    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(",");
            }
            Bitmap prjBitmap = printPrjKitchen.getKitChenPrintBitmap(mContext, key, noCut, printerDeviceBean);
            map.put(stringBuffer.toString(), prjBitmap);
            bitmapMaps.add(map);
        }
        hookPrinterBitmap(bitmapMaps, beans);
        return bitmapMaps;
    }

    private void hookPrinterBitmap(List<Map<String, Bitmap>> bitmapMaps, List<PrjBean.DataBean.Bean> beans) {
        if (bitmapMaps.size() > 0) {
            String prjName = String.valueOf(System.currentTimeMillis());
            int orderType = 1;
            if (beans.size() > 0) {
                PrjBean.DataBean.Bean prjBean = beans.get(0);
                if (prjBean != null) {
                    if (!TextUtils.isEmpty(prjBean.getBillNo())) {
                        prjName = prjBean.getBillNo();
                    } else {
                        prjName = prjBean.getOrderNo();
                    }
                    orderType = prjBean.getOrderType();
                }
            }
            List<Bitmap> bitmapList = new ArrayList<>();
            for (int i = 0; i < bitmapMaps.size(); i++) {
                Set<Map.Entry<String, Bitmap>> bitmapSet = bitmapMaps.get(i).entrySet();
                Iterator<Map.Entry<String, Bitmap>> bitmapIterator = bitmapSet.iterator();
                while (bitmapIterator.hasNext()) {
                    bitmapList.add(bitmapIterator.next().getValue());
                }
            }
            PrinterPlugins.getOnPrinterFlowHandler().onPrinterBitmapBefore(orderType, PrintConstans.PRINT_KITCHEN, prjName, bitmapList);
        }
    }

    /**
     * 是否為針式打印機
     *
     * @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);
    }

    /**
     * 打印失敗，將打印狀態改為未打印，並不是改為打印失敗，失敗是由後台判斷打印失敗次數超過最大限制之後改為打印失敗的
     *
     * @param ids prj的id集合
     */
    private void updatePrjFailure(String ids) {
        updatePrjState(UpdateBean.NO_PRINT, ids);
    }

    /**
     * @param printState 打印狀態 1未打印 2打印中 3已打印
     */
    private void updatePrjState(int printState, String ids) {
        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线程進行網絡請求
                .subscribe(new Observer<String>() {

                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(String s) {
                        LoganManager.w_printer(TAG, "修改Prj打印狀態---end----: " + json + s);
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

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