package com.joe.print.mvp.print;

import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.gingersoft.gsa.cloud.base.application.GsaCloudApplication;
import com.gingersoft.gsa.cloud.base.utils.AidlUtil;
import com.gingersoft.gsa.cloud.constans.Constans;
import com.gingersoft.gsa.cloud.base.utils.other.SPUtils;
import com.gingersoft.gsa.cloud.base.utils.toast.ToastUtils;
import com.gingersoft.gsa.cloud.base.utils.view.ImageUtils;
import com.gingersoft.gsa.cloud.base.utils.view.LayoutToBitmapUtils;
import com.gingersoft.gsa.cloud.base.widget.DialogUtils;
import com.gingersoft.gsa.cloud.constans.PrintConstans;
import com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean;
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.PrinterWriter80mm;
import com.hyweb.n5.lib.constant.PrinterConstant;
import com.hyweb.n5.lib.util.PrinterUtil;
import com.hyweb.n5.server.aidl.IOnPrintCallback;
import com.jess.arms.utils.ArmsUtils;
import com.joe.print.R;
import com.joe.print.mvp.ui.activity.PrintActivity;
import com.joe.print.mvp.ui.adapter.DialogPrinterListAdapter;
import com.joe.print.mvp.ui.adapter.PrintFailListAdapter;
import com.joe.print.mvp.ui.adapter.PrintFileBitmapAdapter;
import com.sunmi.peripheral.printer.InnerResultCallbcak;
import com.yanzhenjie.recyclerview.widget.DefaultItemDecoration;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public abstract class PrinterRoot implements PrintSocketHolder.OnStateChangedListener, PrintExecutor.OnPrintResultListener {

    public final static int PRINT_TEST = -1;//測試頁
    public final static int PRINT_SERVE = 0;//上菜紙
    public final static int PRINT_SLIP = 1;//印單
    public final static int PRINT_BILL = 2;//結賬單
    public final static int PRINT_KITCHEN = 3;//送單，廚房單
    public final static int PRINT_CLEAN_MACHINE = 4;//清機報表
    public final static int PRINT_OTHER_ORDER = 5;//接單內容打印

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

    final static String DELIMITER = "/";//將打印位置區分開來。。。。，暫時只有廚房單用到

    private Context mContext;
    private PrintListener printListener;
    private Map<String, List<Bitmap>> listMap;

    public static PrinterRoot getPrinterByType(int type) {
        if (type == PRINT_TEST) {
            return new PrintTest();
        } else if (type == PRINT_SERVE) {
            return new PrintServe();
        } else if (type == PRINT_SLIP) {
            return new PrintSlip();
        } else if (type == PRINT_BILL) {
            return new PrintBill();
        } else if (type == PRINT_KITCHEN) {
            return new PrintKitchen();
        } else if (type == PRINT_CLEAN_MACHINE) {
            return new PrintCleanMachine();
        } else if (type == PRINT_OTHER_ORDER) {
            return new PrintOtherOrder();
        }
        return null;
    }

    public PrinterRoot setmContext(Context mContext) {
        this.mContext = mContext;
        return this;
    }

    public PrinterRoot setPrintListener(PrintListener printListener) {
        this.printListener = printListener;
        return this;
    }

    public void setText(View parentView, int viewId, String text) {
        ((TextView) parentView.findViewById(viewId)).setText(text);
    }

    /**
     * 把Map中Key相同，则各个value添加到一起，汇总值
     * 把partMap数据合并放到resultMap中。
     *
     * @param resultMap
     */
    public static <T, F, K extends Collection<F>> Map merge2ResultMap(Map<T, K> resultMap, T key2, K value) {
        for (Map.Entry<T, K> entry : resultMap.entrySet()) {
            T key = entry.getKey();
            if (resultMap.containsKey(key2)) {
                Objects.requireNonNull(resultMap.get(key2)).addAll(value);
            } else {
                resultMap.put(key2, value);
            }
        }
        return resultMap;
    }

    /**
     * @param mContext
     * @return key為打印位置，值為圖片集合的map
     */
    public abstract Map<String, List<Bitmap>> getPrintBitmap(Context mContext);

    public void print(List<PrinterDeviceBean> deviceBeans) {
        listMap = getPrintBitmap(mContext);
        printSize = listMap.size();
        if (printSize <= 0) {
            printListener.printFile();
            return;
        }
        //當配置打印多張時執行
//        int x = 2;
//        for (int i = 0; i < x; i++) {
//            for (Map.Entry<String, List<Bitmap>> entry : listMap.entrySet()) {
//                merge2ResultMap(listMap, entry.getKey(), entry.getValue());
//            }
//        }
        // 先不管有沒有默認打印位置
        // 第一步先查看map中的key是不是空的""，有key就要獲取打印機列表，找到對應打印機打印。如果沒有找到打印機再去找默認打印機
        // 如果沒有key，則是走默認打印機流程
        for (Map.Entry<String, List<Bitmap>> entry : listMap.entrySet()) {
            if (entry.getKey().equals("") || entry.getKey().equals("null")) {
                //沒有打印位置，獲取默認打印位置
                defaultPrint(deviceBeans, entry.getValue());
            } else {
                //有打印位置
                hasPrinterSettingPrint(entry.getKey(), entry.getValue(), deviceBeans);
            }
        }
    }


    /**
     * 本機打印
     */
    public void locationPrint(List<Bitmap> bitmaps, PrintListener listener) {
        //本機打印
        if (bitmaps == null || bitmaps.size() <= 0) {
            ToastUtils.show(mContext, "未獲取到打印內容");
            listener.printFile();
            return;
        }
        String model = Build.MODEL;
        if (GsaCloudApplication.mV2.contains(model)) {
            sunmiPrint(bitmaps, listener);
        } else if (GsaCloudApplication.mN5.contains(model)) {
            //N5打印
            n5Print(bitmaps, listener);
        } else {
            ToastUtils.show(mContext, "暫不支持本機型打印");
            listener.printFile();
        }
    }

    /**
     * n5打印
     *
     * @param bitmaps
     * @param listener
     */
    private void n5Print(List<Bitmap> bitmaps, PrintListener listener) {
        try {
            try {
                for (int i = 0; i < bitmaps.size(); i++) {
                    PrinterUtil.appendImage(ImageUtils.zoomDrawable(bitmaps.get(i), N5_PAPER_WIDTH), PrinterConstant.ALIGN_CENTER);
                    PrinterUtil.appendPrnStr("\n", 24, PrinterConstant.ALIGN_CENTER, false);
                    PrinterUtil.appendPrnStr("\n", 24, PrinterConstant.ALIGN_CENTER, false);
                }
                startN5Print(listener);
            } catch (NullPointerException e) {
                ToastUtils.show(mContext, "打印失敗，請重試");
//                finish();
                listener.printFile();
            }
        } catch (RemoteException e) {
            e.printStackTrace();
            listener.printFile();
        }
    }

    /**
     * 商米打印
     *
     * @param bitmaps
     * @param listener
     */
    private void sunmiPrint(List<Bitmap> bitmaps, PrintListener listener) {
        //商米打印
        AidlUtil.getInstance().printBitmaps(bitmaps, SUNMI_PAPER_WIDTH, new InnerResultCallbcak() {
            @Override
            public void onRunResult(boolean isSuccess) {
                //返回接⼝执⾏的情况(并⾮真实打印):成功或失败
                if (isSuccess) {
                    ToastUtils.show(mContext, "打印成功");
                    listener.printSuccess();
                } else {
                    ToastUtils.show(mContext, "打印失敗");
                    listener.printFile();
                }
            }

            @Override
            public void onReturnString(String result) {
                //部分接⼝会异步返回查询数据
                Log.e("eee", "onReturnString：" + result);
            }

            @Override
            public void onRaiseException(int code, String msg) {
                //接⼝执⾏失败时，返回的异常状态
                Log.e("eee", "打印異常狀態碼：" + code + "---MSG:" + msg);
            }

            @Override
            public void onPrintResult(int code, String msg) {
                //事务模式下真实的打印结果返回
                Log.e("eee", "打印結果：" + code + "---MSG:" + msg);
            }
        });
    }


    private void startN5Print(PrintListener listener) {
        try {
            PrinterUtil.startPrint(true, new IOnPrintCallback.Stub() {
                @Override
                public void onPrintResult(int i) {
                    if (i == 0) {
                        //打印成功
                        listener.printSuccess();
                    } else {
                        //打印失敗
                        listener.printFile();
                    }
                }

                @Override
                public IBinder asBinder() {
                    return this;
                }
            });
        } catch (RemoteException e) {
            e.printStackTrace();
            //打印失敗
            listener.printFile();
        }
    }

    //需要打印的次數，根據打印位置來計數，帶*號的之後需要另算。
    private int printSize;
    private List<PrinterDeviceBean> printerDeviceBeans;

    /**
     * 有打印位置的打印方式
     *
     * @param key                打印位置
     * @param bitmaps            需要打印的圖片
     * @param printerDeviceBeans ip打印機集合
     */
    private void hasPrinterSettingPrint(String key, List<Bitmap> bitmaps, List<PrinterDeviceBean> printerDeviceBeans) {
        this.printerDeviceBeans = printerDeviceBeans;
        //獲取用於打印的圖片
        if (printerDeviceBeans.size() == 1) {
            //如果只有一台ip打印機設備，直接全部打印在這台設備上
            ipDevicePrint(printerDeviceBeans.get(0), bitmaps);
        } else {
            //通過打印位置生成多張用於打印的bitmap
            //在打印機列表中找到當前key,
            boolean isPrint = false;//是否已經打印
            for (int i = 0; i < printerDeviceBeans.size(); i++) {

                int lastIndex = key.lastIndexOf(DELIMITER);
                key = key.substring(lastIndex + 1);

                if (key.replaceAll("\\*", "").trim().equalsIgnoreCase(printerDeviceBeans.get(i).getName())) {
                    //如果key(打印位置)去掉*號和打印機名稱相同，則在這台機器上打印
                    ipDevicePrint(printerDeviceBeans.get(i), bitmaps);
                    isPrint = true;
                    break;
                }
            }
            // 循環完也沒找到對應的打印機，則使用默認打印位置，或直接本機打印
            if (!isPrint) {
                // 獲取默認打印位置
                // 獲得用戶默認的打印方式
                defaultPrint(printerDeviceBeans, bitmaps);
            }
        }
    }

    /**
     * 根據打印機位置獲取打印內容
     *
     * @param key 打印位置
     * @return 打印內容
     */
    private List<Bitmap> getPrintBitmapByPrintLocation(String key) {
        if (listMap != null) {
            for (Map.Entry<String, List<Bitmap>> entry : listMap.entrySet()) {
                if (!entry.getKey().equals("")) {
                    //有打印位置
                    String newKey = entry.getKey();
                    int lastIndex = newKey.lastIndexOf(DELIMITER);
                    newKey = newKey.substring(lastIndex + 1);
                    if (newKey.equals(key)) {
                        return entry.getValue();
                    }
                }
            }
        }
        return null;
    }

    private void defaultPrint(List<PrinterDeviceBean> printerDeviceBeans, List<Bitmap> bitmaps) {
        if (Objects.equals(SPUtils.get(mContext, Constans.DEFAULT_PRINT_METHOD, ""), "")) {
            // 如果沒有默認打印位置，彈出彈窗讓用戶選擇是本機打印還是ip打印
            new DialogUtils(mContext, R.layout.print_dialog_select_device) {
                @Override
                public void initLayout(ViewHepler hepler, Dialog dialog) {
                    //本地打印
                    hepler.setViewClick(R.id.local_print, v -> {
                        locationPrint(bitmaps, new PrintListener() {
                            @Override
                            public void printStateChanged(int state) {

                            }

                            @Override
                            public void printSuccess() {
                                cutPrintSize();
                            }

                            @Override
                            public void printFile() {
                                cutPrintSize();
                            }
                        });
                        //修改默認打印方式為本地
                        SPUtils.put(mContext, Constans.DEFAULT_PRINT_METHOD, Constans.LOCAL_PRINT);
                        dialog.dismiss();
                    });
                    //ip打印
                    hepler.setViewClick(R.id.internet_print, v -> {
                        //修改默認打印方式為IP打印
                        SPUtils.put(mContext, Constans.DEFAULT_PRINT_METHOD, Constans.IP_PRINT);
                        //彈出彈窗，讓用戶選擇ip打印機
                        showIpPrintDeviceList(printerDeviceBeans, bitmaps);
                    });
                }
            }.createDialogView().show();
        } else if (Objects.equals(SPUtils.get(mContext, Constans.DEFAULT_PRINT_METHOD, ""), Constans.LOCAL_PRINT)) {
            // 默認打印方式為本地，進行本地打印
            locationPrint(bitmaps, new PrintListener() {
                @Override
                public void printStateChanged(int state) {

                }

                @Override
                public void printSuccess() {
                    setPrintState(PrintActivity.FINISH);
                }

                @Override
                public void printFile() {
                    setPrintState(PrintActivity.FINISH);
                }
            });
        } else if (Objects.equals(SPUtils.get(mContext, Constans.DEFAULT_PRINT_METHOD, ""), Constans.IP_PRINT)) {
            // 默認打印方式為ip打印，調用ip打印方法
            // 獲取默認ip打印機，
            if (!Objects.equals(SPUtils.get(mContext, PrintConstans.DEFAULT_PRINT_IP, ""), "")) {
                //有默認打印機
                PrinterDeviceBean printerDeviceBean = new PrinterDeviceBean();
                printerDeviceBean.setIp((String) SPUtils.get(mContext, PrintConstans.DEFAULT_PRINT_IP, ""));
                printerDeviceBean.setPort((Integer) SPUtils.get(mContext, PrintConstans.DEFAULT_PRINT_PORT, 0));
                printerDeviceBean.setType((Integer) SPUtils.get(mContext, PrintConstans.DEFAULT_PRINT_PAPER, 0));
                ipDevicePrint(printerDeviceBean, bitmaps);
            } else {
                //沒有默認打印機，彈出彈窗，讓用戶選擇ip打印機
                showIpPrintDeviceList(printerDeviceBeans, bitmaps);
            }
        }
    }

    private void showIpPrintDeviceList(List<PrinterDeviceBean> printerDeviceBeans, List<Bitmap> bitmaps) {
        if (printerDeviceBeans != null && printerDeviceBeans.size() > 0) {
            if (printerDeviceBeans.size() == 1) {
                //只有一台ip打印機時，直接打印
                ipDevicePrint(printerDeviceBeans.get(0), bitmaps);
            } else {
                new DialogUtils(mContext, R.layout.print_dialog_internet_list) {
                    @Override
                    public void initLayout(ViewHepler hepler, Dialog dialog) {
                        RecyclerView mRvPrintList = hepler.getView(R.id.rv_print_list);
                        DialogPrinterListAdapter printListAdapter = new DialogPrinterListAdapter(printerDeviceBeans, mContext);
                        mRvPrintList.setLayoutManager(new LinearLayoutManager(mContext));
                        //分割线
                        mRvPrintList.addItemDecoration(new DefaultItemDecoration(ContextCompat.getColor(mContext, R.color.line_color)));
                        mRvPrintList.setAdapter(printListAdapter);
                        printListAdapter.setOnItemClickListener((adapter, view, position) -> {
                            //用戶選擇的ip打印機打印
                            ipDevicePrint(printerDeviceBeans.get(position), bitmaps);
                            dialog.dismiss();
                        });
                    }
                }
                        .setHeight(ArmsUtils.getScreenHeidth(mContext) / 2)
                        .createDialogView()
                        .show();
            }
        } else {
            //沒有打印機，讓用戶去添加
            setPrintState(PrintActivity.ADD_PRINT_DEVICE);
        }
    }


    public void ipDevicePrint(PrinterDeviceBean printerDeviceBean, List<Bitmap> bitmaps) {
        ipDevicePrint(printerDeviceBean, bitmaps, this, this);
    }

    /**
     * ip設備打印
     */
    public void ipDevicePrint(PrinterDeviceBean printerDeviceBean, List<Bitmap> bitmaps, PrintSocketHolder.OnStateChangedListener stateChangedListener, PrintExecutor.OnPrintResultListener resultListener) {
        int paperType = printerDeviceBean.getType();//打印紙類型 1：58mm，2：:80mm
        int printWidth;//打印出來的內容寬度
        if (paperType == 1) {
            paperType = PrinterWriter58mm.TYPE_58;
            printWidth = 580 - 20;//兩邊留20的空隙
        } else {
            paperType = PrinterWriter80mm.TYPE_80;
            printWidth = 800 - 20;//兩邊留20的空隙
        }
        PrintExecutor executor = new PrintExecutor(printerDeviceBean, paperType);
        executor.setOnStateChangedListener(stateChangedListener);
        executor.setOnPrintResultListener(resultListener);
        List<Bitmap> zoomBitmaps = new ArrayList<>();
        if (bitmaps != null) {
            for (int i = 0; i < bitmaps.size(); i++) {
                zoomBitmaps.add(ImageUtils.zoomDrawable(bitmaps.get(i), printWidth));
            }
        }
        IpPrintMaker maker = new IpPrintMaker(mContext, printWidth, zoomBitmaps);
        executor.doPrinterRequestAsync(maker);
    }

    /**
     * view轉bitmap
     *
     * @param context
     * @param view
     * @return
     */
    final Bitmap viewToBitmap(Context context, View view) {
        LayoutToBitmapUtils.layoutView(context, view);//先测量view
        //轉為bitmap
        return LayoutToBitmapUtils.loadBitmapFromView(view);
    }

    //打印失敗設備map
    private List<PrinterDeviceBean> printerFileDevices = new ArrayList<>();

    @Override
    public void onResult(int errorCode, PrinterDeviceBean printerDeviceBean) {
        switch (errorCode) {
            case PrintSocketHolder.ERROR_0:
                Log.e("eee", "打印成功");
                cutPrintSize();
                break;
            case PrintSocketHolder.ERROR_1:
                Log.e("eee", "生成測試頁面數據失敗");
                break;
            case PrintSocketHolder.ERROR_2:
                Log.e("eee", "連接打印機失敗" + System.currentTimeMillis());
            case PrintSocketHolder.ERROR_3:
                Log.e("eee", "獲取輸出流失敗");
                printerFileDevices.add(printerDeviceBean);
                cutPrintSize();
                break;
            case PrintSocketHolder.ERROR_4:
                Log.e("eee", "寫入測試頁面數據失敗");
                break;
            case PrintSocketHolder.ERROR_5:
                Log.e("eee", "必要的參數不能為空");
                break;
        }
        setPrintState(errorCode);
    }

    @Override
    public void onStateChanged(int state, PrinterDeviceBean printerDeviceBean) {
        setPrintState(state);
    }

    private void setPrintState(int state) {
        if (printListener != null) {
            printListener.printStateChanged(state);
        }
    }

    private void cutPrintSize() {
        Log.e("eee", "個數：" + printSize);
        printSize--;
        if (printSize <= 0) {
            Log.e("eee", "打印完了");
            //全部打印完了
            if (printerFileDevices != null && printerFileDevices.size() > 0) {
                //有打印失敗的.關閉activity中的加載框
                setPrintState(PrintActivity.DIMISS_LOADING);
                //显示彈窗，將失敗的打印機信息和食品組顯示出來。
                new DialogUtils(mContext, R.layout.dialog_print_fail_list) {
                    @Override
                    public void initLayout(ViewHepler hepler, Dialog dialog) {
                        hepler.getView(R.id.iv_close_dialog).setOnClickListener(v -> dialog.dismiss());
                        RecyclerView recyclerView = hepler.getView(R.id.rv_print_fail_list);
                        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
                        PrintFailListAdapter adapter = new PrintFailListAdapter(printerFileDevices);
                        recyclerView.addItemDecoration(new DefaultItemDecoration(ContextCompat.getColor(mContext, R.color.line_color)));
                        adapter.setOnItemClickListener((adapter12, view, position) -> {
                            //顯示打印失敗的食品。
                            if (printerFileDevices.get(position).getName() != null) {
                                PrintFileBitmapAdapter bitmapAdapter = new PrintFileBitmapAdapter(getPrintBitmapByPrintLocation(printerFileDevices.get(position).getName()));
                                recyclerView.setAdapter(bitmapAdapter);
                            }
                        });
                        adapter.setSwitchPrintListenter(position -> {
                            if (printerDeviceBeans != null && printerDeviceBeans.size() > 0) {
                                //切換打印機打印。跳轉到選擇打印機列表
                                DialogPrinterListAdapter adapter1 = new DialogPrinterListAdapter(printerDeviceBeans, mContext);
                                //分割线
                                recyclerView.setAdapter(adapter1);
                                adapter1.setOnItemClickListener((adapter2, view, position1) -> {
                                    printerFileDevices.set(position, printerDeviceBeans.get(position1));
                                    adapter.setData(position, printerDeviceBeans.get(position1));
                                    recyclerView.setAdapter(adapter);
                                });
                            } else {
                                //沒有打印機，讓用戶去添加
                                setPrintState(PrintActivity.ADD_PRINT_DEVICE);
                            }
                        });
                        adapter.setTryAgainPrintListenter(position -> {
                            //重試打印。
                            ipDevicePrint(printerFileDevices.get(position), getPrintBitmapByPrintLocation(printerFileDevices.get(position).getName()), (state, printerDeviceBean) -> {
                                //打印機打印狀態切換回調
                            }, (errorCode, printerDeviceBean) -> {
                                switch (errorCode) {
                                    case PrintSocketHolder.ERROR_0:
                                        //打印成功
                                        adapter.remove(position);
                                        Log.e("eee", "打印成功");
                                        if (adapter.getItemCount() <= 0) {
                                            //所有都打印成功
                                            printListener.printSuccess();
                                        }
                                        break;
                                    case PrintSocketHolder.ERROR_2:
                                    case PrintSocketHolder.ERROR_3:
                                        //打印失敗
                                        adapter.setItemStatus(position, 0);
                                        Log.e("eee", "打印失敗");
                                        break;
                                }
                            });
                        });
                        recyclerView.setAdapter(adapter);
                    }
                }
                        .setWidth((int) (ArmsUtils.getScreenWidth(mContext) * 0.66))
                        .setHeight((int) (ArmsUtils.getScreenHeidth(mContext) * 0.66))
                        .createDialogView()
                        .setCanceledOnTouchOutside(false)
                        .setOnDismissListener(dialog -> setPrintState(PrintActivity.FINISH))
                        .show();
            } else {
                //都打印成功了
                Log.e("eee", "打印成功");
                printListener.printSuccess();
            }
        }
    }
}
