package com.miya.print;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.os.IBinder;
import android.os.RemoteException;

import com.miya.print.exception.PrinterException;
import com.miya.print.utils.BitmapUtils;
import com.miya.print.utils.BytesUtil;
import com.miya.print.utils.PrintLogger;
import com.miya.print.utils.ESCUtil;
import com.sunmi.extprinterservice.ExtPrinterService;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class SunmiK1Printer extends BasePrinter {

    final static String TAG = SunmiK1Printer.class.getSimpleName();

    private static final String SERVICE＿PACKAGE = "com.sunmi.extprinterservice";
    private static final String SERVICE＿ACTION = "com.sunmi.extprinterservice.PrinterService";

    private ExtPrinterService extPrinterService;
    private ServiceConnection connService = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            extPrinterService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            PrintLogger.i(TAG, "商米K1服务连接成功");
            extPrinterService = ExtPrinterService.Stub.asInterface(service);
            try {
                int ret = extPrinterService.printerInit();
                if (ret == 0) {
                    PrintLogger.i(TAG, "商米K1服务printerInit成功");
                } else {
                    PrintLogger.i(TAG, "商米K1服务printerInit失败");
                }
            } catch (RemoteException e) {
                e.printStackTrace();
                PrintLogger.i(TAG, "商米K1服务printerInit失败:" + e.getMessage());
            }
        }
    };
    ;

    @Override
    public boolean init(Context context) {
        PrintLogger.i(TAG, "开始初始化商米K1打印机");
        this.context = context;
        Intent intent = new Intent();
        intent.setPackage(SERVICE＿PACKAGE);
        intent.setAction(SERVICE＿ACTION);
        context.getApplicationContext().startService(intent);
        boolean result = context.getApplicationContext().bindService(intent, connService, Context.BIND_AUTO_CREATE);
        if (result == false) {
            PrintLogger.i(TAG, "商米K1打印机初始化失败");
            return false;
        }
        PrintLogger.i(TAG, "商米K1打印机初始化成功");
        return true;
    }

    @Override
    public boolean release() {
        try {
            if (this.context != null && connService != null) {
                this.context.unbindService(connService);
            }
            context = null;
            extPrinterService = null;
            PrintLogger.i(TAG, "商米K1销毁成功");
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            PrintLogger.e(TAG, ex.getMessage());
        }
        return false;
    }

    /**
     * 虽然商米机器提供了加粗字体功能，但为了统一接口，暂时抹掉字体加粗选项
     * 虽然商米机器提供了下划线字体功能，但为了统一接口，暂时抹掉下划线字体选项
     *
     * @param align
     * @param size
     * @param content
     * @param isFeed
     * @return
     */
    @Override
    public int printText(int align, int size, String content, boolean isFeed) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = setAlign(align);
            ret = extPrinterService.printText(content);
            if (isFeed) {
                ret = feedPaper();
            }
            return changeStatus2Custom(ret);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Override
    public int setAlign(int align) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = PrinterStatusEnum.CODE_SUCCESS.getStatus();
            if (this.align != align &&
                    (align == 0 || align == 1 || align == 2)) {
                this.align = align;
                ret = extPrinterService.setAlignMode(align);
            }
            return changeStatus2Custom(ret);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    /**
     * 打印条形码 symbology条码类型
     *
     * @param code   打印条形码 symbology条码类型
     * @param type   条形码类型 0：UPC-A 1：UPC-E 2：EAN13 3：EAN8 4:CODE39 5:ITF 6:CODABAR 7:CODE93 8:CODE128
     * @param width  条形码宽度 2-6 像素 （若条码宽度设置过宽，整个条码超过纸张宽度将不会输出条码内容）
     * @param height 条形码⾼度 1-255 像素
     * @param hriPos HRI位置 0：不打印 1：条形码上⽅ 2：条形码下⽅ 3：条形码上下⽅
     * @desc 条码类型说明：
     * code39 最⻓打印 13 个数字
     * ⽅法 int setFontZoom(int hori, int veri)
     * 说明 由于打印机硬字库限制，字体⼤⼩仅能倍数放⼤，本⽅法可以控制字体在横向和纵向⽅向的放⼤
     * 倍数
     * 输⼊ hori 、veri 的范围为1-8，表示在横向、纵向上字体的放⼤倍数，若设置在范围外将返回错误参数
     * 返回 ⻅接⼝返回值
     * ⽅法 int printColumnsText(String[] colsTextArr, int[] colsWidthArr, int[] colsAlign)
     * 说明
     * 以表格⽅式输出打印内容，每个数组表示在此列上的数据及格式，需要多次调⽤此⽅法才可以达
     * 到表格输出的样式效果
     * ⚠ 注意：调⽤此⽅法后会将之前设置样式初始化！
     * 输⼊
     * colsTextArr: 每列要打印的内容，⽀持中⽂及ascii码
     * colsWidthArr：每列可容纳的最⼤字符数量，字符数以ascii码个数为单位（⼀个中⽂等于两个ascii
     * 码数量），当⽂本内容超出可容纳的最⼤数量时将移动到本列下⼀⾏，当所有列的最⼤字符数量
     * 超出⼀⾏所能容纳的字符数时，将不会打印
     * colsAlign：每列内容的对⻬⽅式，仅当内容字符数⼩于最⼤字符数量时才有效果
     * 返回 ⻅接⼝返回值
     * code93 最⻓打印 17 个数字
     * code128 code128 分三类：{A,{B,{C；A 类：包⼤写字⺟、数字、标 点等，B 类：⼤⼩写字⺟，数
     * 字；C 类：纯数字；默认 B 类编码，若要使本编码需在内容前⾯加“{A”、“{B” 、“{C”声明使⽤的类
     * 型，可以混合使⽤，例如： “{A2344A”，”{C123123”，”{A1A{B13Bxc{C12”。
     * ean8 要求 8 位数字（最后⼀位校验位），有效⻓度 8 个数字
     * ean13 有效⻓度 13 个数字，其中最后⼀位为校验位
     * ITF 要求输⼊数字，且有效⼩于 14 位，必须是偶数位
     */
    @Override
    public int printBarcode(int align, String content, boolean isFeed) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            /**
             * 打印一维条形码
             * data 条码内容
             * height 条码高度
             * width 条码宽度
             * textposition 条码内容位置
             */
            //这里的高度固定传100，宽度传2，条码内容文字传2
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            buffer.write(new byte[]{0x1D, 0x48, (byte) 2,
                    0x1D, 0x77, (byte) 2, 0x1D, 0x68, (byte) 100, 0x0A});
            byte[] barcode;
            barcode = BytesUtil.getBytesFromDecString(content);
            buffer.write(new byte[]{0x1D, 0x6B, 0x49, (byte) (barcode.length + 2), 0x7B, (byte) (0x43)});
            buffer.write(barcode);
            int ret = extPrinterService.sendRawData(buffer.toByteArray());
            return changeStatus2Custom(ret);
        } catch (Exception e) {
            e.printStackTrace();
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Override
    public int printQrcode(int align, String content, boolean isFeed) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = setAlign(align);
            /**
             * code 要打印的⼆维码内容，默认为utf-8字符集
             * modeSize ⼆维码块⼤⼩ 1-16 像素点
             * errorlevel ⼆维码纠错等级 0-3 四个等级
             */
            ret = extPrinterService.printQrCode(content, 6, 3);
            //强制走两行纸才能生效
            ret = extPrinterService.lineWrap(3);
            if (isFeed) {
                ret = feedPaper();
            }
            return changeStatus2Custom(ret);
        } catch (RemoteException ex) {
            ex.printStackTrace();
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, ex.getMessage());
        }
    }

    @Override
    public int printImage(int align, Bitmap bm, boolean isFeed) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = setAlign(align);
            //判断bitmap宽高
            Bitmap actualBm = null;
            if (bm.getWidth() > MAX_IMG_WIDTH_80MM) {
                actualBm = BitmapUtils.scaleBitmap(bm, MAX_IMG_WIDTH_80MM, 0);
                /**
                 * 将bitmap图转成光栅位图⽅式打印
                 * 此⽅法适合打印宽度在打印纸内的图⽚
                 * 数据过⼤时串⼝流控模式会阻塞，⽆流控时会丢数据
                 */
                ret = extPrinterService.printBitmap(actualBm, 0);
            } else {
                /**
                 * 将bitmap图转成光栅位图⽅式打印
                 * 此⽅法适合打印宽度在打印纸内的图⽚
                 * 数据过⼤时串⼝流控模式会阻塞，⽆流控时会丢数据
                 */
                ret = extPrinterService.printBitmap(bm, 0);
            }
            if (actualBm != null) {
                actualBm.recycle();
            }
            //打印图片会默认强制走纸一行
            return changeStatus2Custom(ret);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Override
    public int getPrinterStatus() {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = extPrinterService.getPrinterStatus();
            return changeStatus2Custom(ret);
        } catch (RemoteException e) {
            e.printStackTrace();
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Override
    public int feedPaper() {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = extPrinterService.lineWrap(1);
            return changeStatus2Custom(ret);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Override
    public int cutPaper() {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            int ret = extPrinterService.cutPaper(0, 0);
            return changeStatus2Custom(ret);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    /**
     * 将商米的状态码翻译成PrinterStatusEnum中的标准状态码
     * 注：内部会自动抛出不允许的异常状态
     *
     * @param status 商米返回的状态码
     * @return PrinterStatusEnum中的标准状态码
     */
    private int changeStatus2Custom(int status) {
        switch (status) {
            case 0:
                //正常状态
                return PrinterStatusEnum.CODE_SUCCESS.status;
            case 1:
                //打印机开盖
                throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "打印机开盖");
            case 2:
                //缺纸
                throw new PrinterException(PrinterStatusEnum.CODE_NO_PAPER.status, PrinterStatusEnum.CODE_NO_PAPER.getDesc());
            case 3:
                return PrinterStatusEnum.CODE_FEW_PAPER.status;
            case 4:
                //打印头过热
                return PrinterStatusEnum.CODE_PRINT_HEAD_HOT.status;
            case -1:
                //数据传输错误,请检查连接并重新发送
                throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "打印机离线或打印机未准备好");
            case -2:
                throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "缓存已满，不不接收打印数据");
            case -3:
                throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "发送数据异常");
            case -4:
                throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "发送命令或参数错误");
            default:
                return PrinterStatusEnum.CODE_FAILED.status;
        }
    }

    /**
     * 连接服务
     *
     * @param context context
     */
    @Deprecated
    public void connectPrinterService(Context context) {
        this.context = context.getApplicationContext();
        Intent intent = new Intent();
        intent.setPackage(SERVICE＿PACKAGE);
        intent.setAction(SERVICE＿ACTION);
        context.getApplicationContext().startService(intent);
        context.getApplicationContext().bindService(intent, connService, Context.BIND_AUTO_CREATE);
    }

    /**
     * 断开服务q
     *
     * @param context context
     */
    @Deprecated
    public void disconnectPrinterService(Context context) {
        if (extPrinterService != null) {
            context.getApplicationContext().unbindService(connService);
            extPrinterService = null;
        }
    }

    @Deprecated
    public boolean isConnect() {
        return extPrinterService != null;
    }

    @Deprecated
    public void LF() {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            extPrinterService.lineWrap(1);

        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Deprecated
    public void cutPage() {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");

        }
        try {
            extPrinterService.cutPaper(0, 0);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Deprecated
    public void setAlignMode(int mode) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            extPrinterService.setAlignMode(mode);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    /**
     * 设置打印浓度
     */
    private int[] darkness = new int[]{0x0600, 0x0500, 0x0400, 0x0300, 0x0200, 0x0100, 0,
            0xffff, 0xfeff, 0xfdff, 0xfcff, 0xfbff, 0xfaff};

    public void setDarkness(int index) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }

        int k = darkness[index];
        try {
            extPrinterService.sendRawData(ESCUtil.setPrinterDarkness(k));

        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }


    /**
     * 初始化打印机
     */
    @Deprecated
    public void initPrinter() {
        if (extPrinterService == null) {
            return;
        }
        try {
            extPrinterService.printerInit();
        } catch (RemoteException e) {

        }
    }

    /**
     * 打印二维码
     */
    @Deprecated
    public void printQr(String data, int modulesize, int errorlevel) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            extPrinterService.setAlignMode(1);
            extPrinterService.printQrCode(data, modulesize, errorlevel);
            extPrinterService.lineWrap(2);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    /**
     * 打印条形码 symbology条码类型
     *
     * @param code   打印条形码 symbology条码类型
     * @param type   条形码类型 0：UPC-A 1：UPC-E 2：EAN13 3：EAN8 4:CODE39 5:ITF 6:CODABAR 7:CODE93 8:CODE128
     * @param width  条形码宽度 2-6 像素 （若条码宽度设置过宽，整个条码超过纸张宽度将不会输出条码内容）
     * @param height 条形码⾼度 1-255 像素
     * @param hriPos HRI位置 0：不打印 1：条形码上⽅ 2：条形码下⽅ 3：条形码上下⽅
     * @desc 条码类型说明：
     * code39 最⻓打印 13 个数字
     * ⽅法 int setFontZoom(int hori, int veri)
     * 说明 由于打印机硬字库限制，字体⼤⼩仅能倍数放⼤，本⽅法可以控制字体在横向和纵向⽅向的放⼤
     * 倍数
     * 输⼊ hori 、veri 的范围为1-8，表示在横向、纵向上字体的放⼤倍数，若设置在范围外将返回错误参数
     * 返回 ⻅接⼝返回值
     * ⽅法 int printColumnsText(String[] colsTextArr, int[] colsWidthArr, int[] colsAlign)
     * 说明
     * 以表格⽅式输出打印内容，每个数组表示在此列上的数据及格式，需要多次调⽤此⽅法才可以达
     * 到表格输出的样式效果
     * ⚠ 注意：调⽤此⽅法后会将之前设置样式初始化！
     * 输⼊
     * colsTextArr: 每列要打印的内容，⽀持中⽂及ascii码
     * colsWidthArr：每列可容纳的最⼤字符数量，字符数以ascii码个数为单位（⼀个中⽂等于两个ascii
     * 码数量），当⽂本内容超出可容纳的最⼤数量时将移动到本列下⼀⾏，当所有列的最⼤字符数量
     * 超出⼀⾏所能容纳的字符数时，将不会打印
     * colsAlign：每列内容的对⻬⽅式，仅当内容字符数⼩于最⼤字符数量时才有效果
     * 返回 ⻅接⼝返回值
     * code93 最⻓打印 17 个数字
     * code128 code128 分三类：{A,{B,{C；A 类：包⼤写字⺟、数字、标 点等，B 类：⼤⼩写字⺟，数
     * 字；C 类：纯数字；默认 B 类编码，若要使本编码需在内容前⾯加“{A”、“{B” 、“{C”声明使⽤的类
     * 型，可以混合使⽤，例如： “{A2344A”，”{C123123”，”{A1A{B13Bxc{C12”。
     * ean8 要求 8 位数字（最后⼀位校验位），有效⻓度 8 个数字
     * ean13 有效⻓度 13 个数字，其中最后⼀位为校验位
     * ITF 要求输⼊数字，且有效⼩于 14 位，必须是偶数位
     */
    @Deprecated
    public void printBarCode(String code, int type, int width, int height, int hriPos) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            extPrinterService.printBarCode(code, type, width, height, hriPos);
            extPrinterService.lineWrap(2);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }


    /**
     * 打印一维条形码
     * data 条码内容
     * height 条码高度
     * width 条码宽度
     * textposition 条码内容位置
     */
    @Deprecated
    public void printLongBarCode(String data, int height, int width, int textposition) throws RemoteException, IOException {


        if (width < 2 || width > 6) {
            width = 2;
        }
        if (textposition < 0 || textposition > 3) {
            textposition = 0;
        }
        if (height < 1 || height > 255) {
            height = 162;
        }
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        buffer.write(new byte[]{0x1D, 0x48, (byte) textposition,
                0x1D, 0x77, (byte) width, 0x1D, 0x68, (byte) height, 0x0A});

        byte[] barcode;
        barcode = BytesUtil.getBytesFromDecString(data);
        buffer.write(new byte[]{0x1D, 0x6B, 0x49, (byte) (barcode.length + 2), 0x7B, (byte) (0x43)});
        buffer.write(barcode);


        extPrinterService.sendRawData(buffer.toByteArray());
    }

    /**
     * 打印文字
     */
    @Deprecated
    public void printText(String content, float size, boolean isBold, boolean isUnderLine) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }
        try {
            if (isBold) {
                extPrinterService.sendRawData(ESCUtil.boldOn());
            } else {
                extPrinterService.sendRawData(ESCUtil.boldOff());
            }

            if (isUnderLine) {
                extPrinterService.sendRawData(ESCUtil.underlineWithOneDotWidthOn());
            } else {
                extPrinterService.sendRawData(ESCUtil.underlineOff());
            }

            extPrinterService.printText(content);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    /*
     *打印图片
     */
    @Deprecated
    public void printBitmap(Bitmap bitmap) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");

        }

        try {
            extPrinterService.setAlignMode(1);
            /**
             * 将bitmap图转成光栅位图⽅式打印
             * 此⽅法适合打印宽度在打印纸内的图⽚
             * 数据过⼤时串⼝流控模式会阻塞，⽆流控时会丢数据
             */
            extPrinterService.printBitmap(bitmap, 0);
            extPrinterService.lineWrap(2);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Deprecated
    public String updatePrinterState() throws RemoteException {
        if (extPrinterService == null) {
            return "打印服务已断开!";
        }
        int state = extPrinterService.getPrinterStatus();

        if (state == -1) {
            return "打印机脱机或打印服务还未连接打印机";
        }
        if (state == 0) {
            return "正常";
        }
        if (state == 1) {
            return "打印机开盖";
        }
        if (state == 2) {
            return "打印机缺纸";
        }
        if (state == 3) {
            return "打印机即将缺纸";
        }
        if (state == 4) {
            return "打印机过热";
        }
        return "获取状态异常";
    }


    /*
     * 空打三行！
     */
    @Deprecated
    public void print3Line() {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }

        try {
            extPrinterService.lineWrap(3);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }

    @Deprecated
    public void sendRawData(byte[] data) {
        if (extPrinterService == null) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, "服务已断开!");
        }

        try {
            extPrinterService.sendRawData(data);
        } catch (RemoteException e) {
            throw new PrinterException(PrinterStatusEnum.CODE_FAILED.status, e.getMessage());
        }
    }
}
