package com.mq.mylibrary.tool;

import android.util.Base64;

import com.fntech.RInt;
import com.fntech.m10a.gpio.M10A_GPIO;
import com.mq.mylibrary.CardData;
import com.mq.mylibrary.base.BaseApp;
import com.mq.mylibrary.base.BaseCard;
import com.mq.mylibrary.rfid.io.cmd._14443ACommApi;
import com.mq.mylibrary.rfid.io.service.ERROR;
import com.mq.mylibrary.rfid.io.service.IOManager;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 包名: com.mq.mylibrary.tool
 * 类名: M10aRFIDTool
 * 作用描述: 普洱读写卡工具类
 * 创建人: mQ
 * 创建时间: 2020/3/27 14:52
 * 更新人:
 * 更新时间: 2020/3/27 14:52
 * 更新说明:
 * 版本号: 1.0
 */
public class M10aRFIDTool implements BaseCard {

    private M10aRFIDTool() {
    }

    private static class Helper {
        private static final M10aRFIDTool instance = new M10aRFIDTool();
    }

    public static M10aRFIDTool get() {
        return Helper.instance;
    }

    private RInt card_type = new RInt(0);
    private byte[] uid = new byte[7];

    /**
     * 模块上电，打开串口
     */
    @Override
    public void open() {
        M10A_GPIO.PowerOn();    //上电
        M10A_GPIO._14443_SwitchSerialPort(); //电子开关选择gpio
        IOManager.INSTANCE.addRfidListener(null, "/dev/ttyHSL1"); //打开串口
    }

    /**
     * 模块下电
     */
    @Override
    public void close() {
        M10A_GPIO.PowerOff(); //下电
        IOManager.INSTANCE.removeRfidListener();  //关闭串口
    }

    /**
     * 读卡
     */
    @Override
    public CardData readCard() {
        CardData result = new CardData();
        StringBuilder sbResult = new StringBuilder();

        //获取版本号
        String ver = _14443ACommApi.ISO14443A_ReadVersion(); //读版本
        if (ver == null) {
            result.state = "3";
            result.msg = "此设备没有M10A读卡功能";
            return result;
        }
        //读取UID
        RInt len = new RInt(0);
        int ret = _14443ACommApi.ISO14443A_InitMifare(card_type, uid, len); //初始化M1卡 获取UID
        if (ret != 0) {
            result.state = "3";
            result.msg = "没有检测到IC卡，请重试！" + ERROR.format((byte) ret);
            return result;
        }
        result.state = "0";
        List<byte[]> dataList = new ArrayList<>();

        try {
            //读取64个块中的数据
            for (int i = 0; i < 16; i++) {
                for (int j = 0; j < 4; j++) {
                    byte byBlock = (byte) (i * 4 + j);
                    byte[] byBuf = new byte[16];
                    //认证M1卡
                    ret = _14443ACommApi.ISO14443A_Authentication((byte) 0,
                            byBlock, uid, Tools.hexStringToBytes("FFFFFFFFFFFF"));
                    if (ret != 0) {
                        result.state = "3";
                        result.msg = "认证失败，错误码：" + ERROR.format((byte) ret);
                        return result;
                    } else {
                        //读取
                        ret = _14443ACommApi.ISO14443A_ReadBlock(byBlock, byBuf, card_type.value); //读块
                        if (ret != 0) {
                            result.state = "3";
                            result.msg = "读卡失败,错误码：" + ERROR.format((byte) ret);
                            return result;
                        } else {
                            String strRead = new String(byBuf, "gb2312");
                            //0区0块为制造商占用块 不取数据
                            //每区最后一块为控制块 不取数据
                            if (i != 0 || j != 0)
                                if (j < 3) {
                                    sbResult.append(strRead);
                                    dataList.add(byBuf);
                                }

                        }

                    }
                }
            }
            if (dataList.size() == 0) {
                result.state = "4";
                result.msg = "卡内无数据";
                return result;
            }
            String readStr = sbResult.toString();
            LogTool.i("读取结果:" + readStr);
            // 计算字节长度
            int byteCount = 0;
            for (byte[] bytes : dataList) {
                byteCount += bytes.length;
            }
            LogTool.d("数据源的字节长度:" + byteCount);

            // 将所有字节存到byte数组中
            byte[] byteResult = new byte[byteCount];
            int byteIndex = 0;
            for (byte[] bytes : dataList) {
                for (byte b : bytes) {
                    byteResult[byteIndex] = b;
                    byteIndex++;
                }
            }

            // 字节转为字符串
            String sourceData = new String(byteResult, "gb2312");
            LogTool.d("数据源的内容:" + sourceData);

            // 截取业务数据
            String newData = null;
            if (sourceData.contains("*")) {
                newData = sourceData.substring(0, sourceData.indexOf("*"));
            } else {
                newData = sourceData;
            }

            // 原数据是否加密
            boolean isEncryption = !newData.contains("|");
            BaseApp.saveIsEncryption(isEncryption);// 存储加密状态

            // 加密数据进行解密
            String cardInfo = null;
            if (isEncryption) {
                byte[] debugBytes = Base64.decode(newData.getBytes(), Base64.DEFAULT);
                cardInfo = new String(debugBytes, "gb2312");
            } else {
                cardInfo = newData;
            }

            LogTool.d("解析后的业务数据：" + cardInfo);

            StringBuffer buffer = new StringBuffer();
            String[] split = cardInfo.split("\\|");
            buffer.append("字段数:")
                    .append(split.length)
                    .append("\n");
            for (int i = 0; i < split.length; i++) {
                buffer.append(split[i])
                        .append("\n");
            }

            result.resultData = cardInfo;
            return result;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            result.state = "4";
            result.msg = "数据解码错误，请检查卡内数据是否正确";
            return result;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        result.state = "4";
        result.msg = "读卡失败";
        return result;
    }

    /**
     * 写卡
     */
    @Override
    public String writeCard(String newCardInfo) {
        String result = "";
        //获取版本号
        String ver = _14443ACommApi.ISO14443A_ReadVersion(); //读版本
        if (ver == null) {
            return "此设备没有M10A读卡功能";
        }
        //读取UID
        RInt len = new RInt(0);
        int ret = _14443ACommApi.ISO14443A_InitMifare(card_type, uid, len); //初始化M1卡 获取UID
        if (ret != 0) {
            result = "没有检测到IC卡，请重试！" + ERROR.format((byte) ret);
            return result;
        }
        //处理数据
        try {

            String readData = null;
            if (BaseApp.getIsEncryption()) {
                try {
                    readData = Base64.encodeToString(newCardInfo.getBytes("gb2312"), Base64.DEFAULT)
                            + "*000";
                } catch (UnsupportedEncodingException ex) {
                    ex.printStackTrace();
                    result = "数据加密失败";
                    return result;
                }
            } else {
                readData = newCardInfo + "*000";
            }
            LogTool.d("加密后数据:" + readData);

            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(readData);
            readData = m.replaceAll("");
            LogTool.d("格式化后数据:" + readData);


            byte[] bytes = new byte[0];
            try {
                bytes = readData.getBytes("gb2312");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                result = "字节转换失败";
                return result;
            }
            LogTool.i("数据源的字节数:" + bytes.length);
            int count = bytes.length / 16;//根据数据长度计算循环次数 4/16
            int remainLen = bytes.length % 16;
            if (remainLen > 0)
                count += 1;
            LogTool.i("循环次数:" + count);
            LogTool.i("余:" + count);

            // 将源字节拆成16个为一组
            List<byte[]> resultList = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                byte[] newByte = new byte[16];
                if (i < count - 1) {
                    System.arraycopy(bytes, i * 16, newByte, 0, 16);
                } else {
                    // 当最后不足16位时 用0补足
                    int end = bytes.length - (i * 16);
                    System.arraycopy(bytes, i * 16, newByte, 0, end);
                    for (int j = 0; j < 16 - end; j++) {
                        newByte[j + end] = "0".getBytes()[0];
                    }
                }
                resultList.add(newByte);
                LogTool.i("字节分组--" + i + ":" + Arrays.toString(newByte));
                try {
                    LogTool.i("字节分组--" + i + "内容:" + new String(newByte, "gb2312"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    result = "字节转换失败";
                    return result;
                }
            }

            int line = 0;
            //写入数据到16个区,每区4个块中。
            for (int i = 0; i < 16; i++) {
                for (int j = 0; j < 4; j++) {
                    byte byBlock = (byte) (i * 4 + j);//块号
                    //0区0块是厂商信息
                    //每区第4块是加密块 不写入数据
                    if (i != 0 || j != 0)
                        if (j < 3) {
                            //认证M1卡
                            ret = _14443ACommApi.ISO14443A_Authentication((byte) 0, byBlock, uid, Tools.hexStringToBytes("FFFFFFFFFFFF"));
                            if (ret != 0) {
                                result = "写入失败，错误码：" + ERROR.format((byte) ret);
                                return result;
                            } else {
                                //写入
                                //截取加密字符串
                                if (line <= count - 1) {
                                    byte[] resultByte = resultList.get(line);
                                    LogTool.i("索引:" + line);

                                    ret = _14443ACommApi.ISO14443A_WriteBlock(byBlock, resultByte, card_type.value); //写单块
                                    line++;
                                    if (ret != 0) {
                                        result = "写入失败，错误码：" + ERROR.format((byte) ret);
                                        return result;
                                    }

                                }

                            }
                        }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            result = "写入失败，请重新放卡";
        }
        return result;
    }

    @Override
    public String clearCard() {
        String tag = "";
        //获取版本号
        String ver = _14443ACommApi.ISO14443A_ReadVersion(); //读版本
        if (ver == null) {
            return "此设备没有M10A读卡功能";
        }
        //读取UID
        RInt len = new RInt(0);
        int ret = _14443ACommApi.ISO14443A_InitMifare(card_type, uid, len); //初始化M1卡 获取UID
        if (ret != 0) {
            tag = "没有检测到IC卡，请重试！" + ERROR.format((byte) ret);
            return tag;
        }
        //处理数据
        try {
            String encodeData = "0000000000000000";//每个块写入的数据
            //写入数据到16个区,每区4个块中。
            for (int i = 0; i < 16; i++) {
                for (int j = 0; j < 4; j++) {
                    byte byBlock = (byte) (i * 4 + j);//块号
                    //0区0块是厂商信息
                    //每区第4块是加密块 不写入数据
                    if (i == 0 && j == 0) break;
                    if (j == 3) break;
                    //认证M1卡
                    ret = _14443ACommApi.ISO14443A_Authentication((byte) 0, byBlock, uid, Tools.hexStringToBytes("FFFFFFFFFFFF"));
                    if (ret != 0) {
                        tag = "写入失败，错误码：" + ERROR.format((byte) ret);
                        return tag;
                    } else {
                        //写入
                        ret = _14443ACommApi.ISO14443A_WriteBlock(byBlock, encodeData.getBytes("gb2312"), card_type.value); //写单块
                        if (ret != 0) {
                            tag = "写入失败，错误码：" + ERROR.format((byte) ret);
                            return tag;
                        }
                    }
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return tag;
    }


}
