package h.com.serialportapi.comn;

import android.util.Log;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import h.com.serialportapi.comn.message.LogManager;
import h.com.serialportapi.comn.message.RecvMessage;
import h.com.serialportapi.comn.message.SendMessage;

public class Reader {
    private final String TAG = Reader.class.getSimpleName();
    private final boolean _DEBUG = true;
    private final long TIMEOUT = 1000;

    private final byte ERR_SUCCESS = (byte) 0x00;
    private final byte ERR_FAILURE = (byte) 0x01;
    private final byte ERR_IO      = (byte) 0x02;
    private final byte ERR_TIMEOUT = (byte) 0x03;
    private final byte ERR_FORMAT  = (byte) 0x04;

    private final byte CMD_ISO14443A                = (byte) 0x22;  //寻ISO14443A卡
    private final byte CMD_ISO14443B                = (byte) 0x41;  //寻ISO14443B卡
    private final byte CMD_ISO15693                 = (byte) 0xA1;  //寻ISO15693卡

    private final byte CMD_PiccAuthKey              = (byte) 0x16;  //M1卡密钥认证
    private final byte CMD_PiccRead                 = (byte) 0x17;  //M1卡，Ultralight/c，Ntag系列卡 读数据
    private final byte CMD_PiccWrite                = (byte) 0x18;  //M1卡写数据
    private final byte CMD_PiccWriteUl              = (byte) 0x19;  //Ultralight/c,ntag系列卡写数据
    private final byte CMD_PiccUlAuth               = (byte) 0x31;  //Ultralightc,ntag系列卡认证密钥
    private final byte CMD_PiccUlSetkey             = (byte) 0x32;  //Ultralightc,ntag系列卡修改密钥
    private final byte CMD_PiccInitVL               = (byte) 0x1A;  //M1初始化电子钱包
    private final byte CMD_PiccValueOper            = (byte) 0x1B;  //M1电子钱包值操作
    private final byte CMD_PiccBackup               = (byte) 0x1C;  //M1电子钱包备份
    private final byte CMD_PiccReadValue            = (byte) 0x1D;  //M1电子钱包读余额
    private final byte CMD_PiccReset                = (byte) 0x21;  //射频复位
    private final byte CMD_PiccRequestATS           = (byte) 0x2A;  //ISO14443A_RATS
    private final byte CMD_PiccAPDU                 = (byte) 0x2C;  //ISO14443A_APDU
    private final byte CMD_PiccTransfer             = (byte) 0x2E;  //卡数据交互

    private final byte CMD_DESAuthenticate          = (byte) 0x81;  //Desfire 认证
    private final byte CMD_DESGetKeySetting         = (byte) 0x82;  //Desfire 获取Keyseting
    private final byte CMD_DESChangKey              = (byte) 0x83;  //Desfire 修改密钥
    private final byte CMD_DESChangKeySetting       = (byte) 0x84;  //Desfire 修改Keyseting
    private final byte CMD_DESGetKeyVersion         = (byte) 0x85;  //Desfire 获取密钥版本
    private final byte CMD_DESCreateApplication     = (byte) 0x86;  //Desfire 创建APP
    private final byte CMD_DESDeleteApplication     = (byte) 0x87;  //Desfire 删除
    private final byte CMD_DESGetApplicationIDs     = (byte) 0x88;  //Desfire 获取APP
    private final byte CMD_DESSelectApplication     = (byte) 0x89;  //Desfire 选择APP
    private final byte CMD_DESFormatPicc            = (byte) 0x8A;  //Desfire 卡格式化

    private final byte CMD_DESGetDESVersion         = (byte) 0x8B;  //Desfire 获取卡版本信息
    private final byte CMD_DESGetFileIDs            = (byte) 0x8c;  //Desfire获取文件ID
    private final byte CMD_DESGetFileSettings       = (byte) 0x8D;  //Desfire获取文件设置
    private final byte CMD_DESChangeFileSettings    = (byte) 0x8E;  //Desfire 修改文件设置
    private final byte CMD_DESCreateStdDataFile     = (byte) 0x8F;  //Desfire 创建标准数据文件
    private final byte CMD_DESCreateBackupDataFile  = (byte) 0x90;  //Desfire 创建备份文件
    private final byte CMD_DESCreateValueFile       = (byte) 0x91;  //Desfire 创建钱包文件
    private final byte CMD_DESCreateLinearRecordFile= (byte) 0x92;  //Desfire 创建线性记录文件
    private final byte CMD_DESCreateCyclicRecordFile= (byte) 0x93;  //Desfire 创建循环记录文件
    private final byte CMD_DESDeleteDESFile         = (byte) 0x94;  //Desfire 删除文件
    private final byte CMD_DESEV1ReadData           = (byte) 0x95;  //Desfire 标准数据文件/备份文件读数据
    private final byte CMD_DESEV1WriteData          = (byte) 0x96;  //Desfire 标准数据文件/备份文件写数据

    private final byte CMD_DESEV1GetValue           = (byte) 0x97;  //Desfire 读钱包
    private final byte CMD_DESEV1_CREDIT            = (byte) 0x98;  //Desfire 钱包CREDIT
    private final byte CMD_DESEV1_DEBIT             = (byte) 0x99;  //Desfire 钱包DEBIT
    private final byte CMD_DESEV1_LIMITEDCREDIT     = (byte) 0x9A;  //Desfire 钱包LIMITEDCREDIT

    private final byte CMD_DESEV1WriteRecord        = (byte) 0x9B;  //Desfire 写记录文件
    private final byte CMD_DESEV1ReadRecord         = (byte) 0x9C;  //Desfire 读记录文件
    private final byte CMD_DESClearRecordFile       = (byte) 0x9D;  //Desfire 清除记录

    private final byte CMD_DES_CommitTransaction    = (byte) 0x9E;  //Desfire 备份文件、钱包文件、记录文件 操作确认
    private final byte CMD_DES_AbortTransaction     = (byte) 0x9F;  //Desfire 备份文件、钱包文件、记录文件 操作取消

    private final byte CMD_ISO15693_Select          = (byte) 0xA6;  //ISO15693 选卡
    private final byte CMD_ISO15693_StayQuiet       = (byte) 0xA2;  //ISO15693 休眠
    private final byte CMD_ISO15693_Read_Block      = (byte) 0xA3;  //ISO15693 读block
    private final byte CMD_ISO15693_Write_Block     = (byte) 0xA4;  //ISO15693 写block
    private final byte CMD_ISO15693_Lock_Block      = (byte) 0xA5;  //ISO15693 锁block
    private final byte CMD_ISO15693_Write_AFI       = (byte) 0xA8;  //ISO15693 写AFI
    private final byte CMD_ISO15693_Lock_AFI        = (byte) 0xA9;  //ISO15693 锁AFI
    private final byte CMD_ISO15693_Write_DSFID     = (byte) 0xAA;  //ISO15693 写DSFID
    private final byte CMD_ISO15693_Lock_DSFID      = (byte) 0xAB;  //ISO15693 锁DSFID
    private final byte CMD_ISO15693_Get_SysInfor    = (byte) 0xAC;  //ISO15693 获取卡信息

    private BufferedInputStream mInputStream;
    private OutputStream mOutputStream;
    private boolean isListener = false;

    public Reader(InputStream is, OutputStream os) {
        mInputStream = new BufferedInputStream(is);
        mOutputStream = os;
    }

    public void setLisenter(boolean isListener) {
        this.isListener = isListener;
    }

    private byte makeCrc(byte[] data, int startPos, int endPos) {
        byte crc = 0;

        for (int i = startPos; i < endPos; i++) {
            crc ^= data[i];
        }
        return crc;
    }

    public int optFunction(byte command, byte[] inData, int inLen, byte[] outData, int[] outLen) {
        int size;
        int readLen = 0;
        int recvLen = 0;
        long startTime;
        long currTime;
        long endTime;

        byte[] wBuffer = new byte[inLen + 5];
        byte[] rBuffer = new byte[1024];
        byte[] mBuffer = new byte[1024];

        try {
            if (mOutputStream != null || mInputStream != null) {
                wBuffer[0] = (byte) 0x50;
                wBuffer[1] = (byte) ((inLen >> 8) & 0xFF);
                wBuffer[2] = (byte) ((inLen) & 0xFF);
                wBuffer[3] = command;
                if (inData != null && inLen != 0) {
                    System.arraycopy(inData, 0, wBuffer, 4, inLen);
                }
                wBuffer[4 + inLen] = makeCrc(wBuffer, 0, 4 + inLen);
                mOutputStream.write(wBuffer);

                if (isListener) {
                    String hexstr = "";
                    for (int i = 0; i < wBuffer.length; i++) {
                        hexstr += String.format("%02X", wBuffer[i]);
                    }
                    LogManager.instance().post_s(new SendMessage(hexstr));
                    if (_DEBUG) {
                        Log.i(TAG, hexstr);
                    }
                }

                currTime = startTime = System.currentTimeMillis();
                endTime = startTime + TIMEOUT;
                while (currTime < endTime) {
                    if (mInputStream.available() > 0) {
                        size = mInputStream.read(mBuffer);
                        if (recvLen > 0) {
                            System.arraycopy(mBuffer, 0, rBuffer, readLen, size);
                            readLen += size;
                            if (readLen >= recvLen) {
                                break;
                            }
                        } else {
                            if (readLen == 0) {
                                for (int i = 0; i < size; i++) {
                                    if (mBuffer[i] == 0x50 || mBuffer[i] == (byte) 0xF0) {
                                        System.arraycopy(mBuffer, i, rBuffer, 0, size - i);
                                        readLen += size - i;
                                        break;
                                    }
                                }
                            } else {
                                System.arraycopy(mBuffer, 0, rBuffer, readLen, size);
                                readLen += size;
                            }
                            if (readLen >= 3) {
                                recvLen = ((rBuffer[1] << 8) | rBuffer[2]) + 5;
                                if (readLen >= recvLen) {
                                    break;
                                }
                            }
                        }
                    }
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    currTime = System.currentTimeMillis();
                }

                if (currTime >= endTime) {
                    return ERR_TIMEOUT;
                } else if (makeCrc(rBuffer, 0, recvLen) != 0) {
                    if (isListener) {
                        String hexstr = "";
                        for (int i = 0; i < recvLen; i++) {
                            hexstr += String.format("%02X", rBuffer[i]);
                        }
                        LogManager.instance().post_r(new RecvMessage(hexstr));
                    }
                    return ERR_FORMAT;
                } else {
                    if (isListener) {
                        String hexstr = "";
                        for (int i = 0; i < recvLen; i++) {
                            hexstr += String.format("%02X", rBuffer[i]);
                        }
                        LogManager.instance().post_r(new RecvMessage(hexstr));
                    }
                    if (rBuffer[0] == 0x50) {
                        outLen[0] = recvLen - 5;
                        System.arraycopy(rBuffer, 4, outData, 0, outLen[0]);
                        return ERR_SUCCESS;
                    } else {
                        return ERR_FAILURE;
                    }
                }
            } else {
                return ERR_IO;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return ERR_IO;
        }
    }

    public int PiccActivate(byte ucRst_1ms, byte ucReqCode, byte[] pATQ, byte[] pSak, byte[] pUIDLen, byte[] pUID) {
        byte[] wData = new byte[2];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucRst_1ms;
        wData[1] = ucReqCode;
        int result = optFunction(CMD_ISO14443A, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pATQ != null) {
            pATQ[0] = rData[0];
            pATQ[1] = rData[1];
        }
        if (pSak != null) {
            pSak[0] = rData[2];
        }
        if (pUIDLen != null && pUID != null) {
            pUIDLen[0] = rData[3];
            System.arraycopy(rData, 4, pUID, 0, pUIDLen[0]);
        }
        return result;
    }

    public int PiccActivateB(byte ucRst_1ms, byte ucAFI, byte ucMethod, byte[] pUIDLen, byte[] pUID) {
        byte[] wData = new byte[4];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucRst_1ms;
        wData[1] = ucAFI;
        wData[2] = ucMethod;
        wData[3] = 0x00;
        int result = optFunction(CMD_ISO14443B, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pUIDLen != null && pUID != null) {
            pUIDLen[0] = (byte) rLen[0];
            System.arraycopy(rData, 0, pUID, 0, pUIDLen[0]);
        }
        return result;
    }

    public int ISO15693_Inventory(byte flags, byte AFI, byte masklength, byte[] uid, byte[] resplen, byte[] resp) {
        byte[] wData = new byte[3];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = flags;
        wData[1] = AFI;
        wData[2] = masklength;
        if (masklength > 0 && uid != null) {
            System.arraycopy(uid, 0, wData, 3, masklength);
        }
        int result = optFunction(CMD_ISO15693, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null && resp != null) {
            resplen[0] = (byte) rLen[0];
            System.arraycopy(rData, 0, resp, 0, resplen[0]);
        }
        return result;
    }

    public int Picc_AuthKey(byte auth_mode, byte addr, byte[] pSnr, byte[] pKey) {
        byte[] wData = new byte[12];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = auth_mode;
        wData[1] = addr;
        System.arraycopy(pSnr, 0, wData, 2, 4);
        System.arraycopy(pKey, 0, wData, 6, 6);
        int result = optFunction(CMD_PiccAuthKey, wData, wData.length, rData, rLen);
        return result;
    }

    public int Picc_Read(byte ucBlock, byte[] pBuf) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucBlock;
        int result = optFunction(CMD_PiccRead, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pBuf != null) {
            System.arraycopy(rData, 0, pBuf, 0, 16);
        }
        return result;
    }

    public int Picc_Write(byte ucBlock, byte[] pBuf) {
        byte[] wData = new byte[17];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucBlock;
        System.arraycopy(pBuf, 0, wData, 1, 16);
        int result = optFunction(CMD_PiccWrite, wData, wData.length, rData, rLen);
        return result;
    }

    public int Picc_ULWrite(byte ucBlock, byte[] pBuf) {
        byte[] wData = new byte[5];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucBlock;
        System.arraycopy(pBuf, 0, wData, 1, 4);
        int result = optFunction(CMD_PiccWriteUl, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccULAuth(byte[] pKEY) {
        byte[] wData = new byte[16];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        System.arraycopy(pKEY, 0, wData, 0, 16);
        int result = optFunction(CMD_PiccUlAuth, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccSetKey(byte[] pKEY) {
        byte[] wData = new byte[16];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        System.arraycopy(pKEY, 0, wData, 0, 16);
        int result = optFunction(CMD_PiccUlSetkey, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccInitVL(byte ucBlock, byte[] pBuf) {
        byte[] wData = new byte[5];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucBlock;
        System.arraycopy(pBuf, 0, wData, 1, 4);
        int result = optFunction(CMD_PiccInitVL, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccValueOper(byte ucOperMode, byte ucBlock, byte[] pValue, byte ucTransBlock) {
        byte[] wData = new byte[7];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucOperMode;
        wData[1] = ucBlock;
        System.arraycopy(pValue, 0, wData, 2, 4);
        wData[6] = ucTransBlock;
        int result = optFunction(CMD_PiccValueOper, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccBackup(byte ucBlock, byte ucTransBlock) {
        byte[] wData = new byte[2];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucBlock;
        wData[1] = ucTransBlock;
        int result = optFunction(CMD_PiccBackup, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccReadValue(byte ucBlock, byte[] pBuf) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = ucBlock;
        int result = optFunction(CMD_PiccReadValue, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pBuf != null) {
            System.arraycopy(rData, 0, pBuf, 0, 4);
        }
        return result;
    }

    public int PiccReset(byte _ms) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = _ms;
        int result = optFunction(CMD_PiccReset, wData, wData.length, rData, rLen);
        return result;
    }

    public int PiccRequestATS(byte ucRFU, byte[] pATSLen, byte[] pATS) {
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        int result = optFunction(CMD_PiccRequestATS, null, 0, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pATSLen != null) {
            pATSLen[0] = (byte) (rLen[0] - 1);
        }
        if (pATS != null) {
            System.arraycopy(rData, 1, pATS, 0, rLen[0]);
        }
        return result;
    }

    public int PiccAPDU(int usSendLen, byte[] pSendBuf, int[] pRcvLen, byte[] pRcvBuf) {
        byte[] wData = new byte[usSendLen];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        System.arraycopy(pSendBuf, 0, wData, 0, usSendLen);
        int result = optFunction(CMD_PiccAPDU, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pRcvLen != null) {
            pRcvLen[0] = rLen[0];
        }
        if (pRcvBuf != null) {
            System.arraycopy(rData, 0, pRcvBuf, 0, rLen[0]);
        }
        return result;
    }

    public int PiccTransfer(int usSendLen, byte[] pSendBuf, int[] pRcvLen, byte[] pRcvBuf) {
        byte[] wData = new byte[usSendLen];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        System.arraycopy(pSendBuf, 0, wData, 0, usSendLen);
        int result = optFunction(CMD_PiccTransfer, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pRcvLen != null) {
            pRcvLen[0] = rLen[0];
        }
        if (pRcvBuf != null) {
            System.arraycopy(rData, 0, pRcvBuf, 0, rLen[0]);
        }
        return result;
    }

    public int DESAuthenticate(byte KeyNO, byte[] pKey) {
        byte[] wData = new byte[17];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = KeyNO;
        System.arraycopy(pKey, 0, wData, 1, 16);
        int result = optFunction(CMD_DESAuthenticate, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESGetKeySetting(byte[] pKeySetting, byte[] pMaxKeyNum) {
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        int result = optFunction(CMD_DESGetKeySetting, null, 0, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pKeySetting != null) {
            pKeySetting[0] = rData[0];
        }
        if (pMaxKeyNum != null) {
            pMaxKeyNum[0] = rData[1];
        }
        return result;
    }

    public int DESChangKey(byte KeyNo, byte KeySettings, byte[] pNewKey, byte[] pOldKey) {
        byte[] wData = new byte[34];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = KeyNo;
        wData[1] = KeySettings;
        System.arraycopy(pNewKey, 0, wData, 2, 16);
        System.arraycopy(pOldKey, 0, wData, 18, 16);
        int result = optFunction(CMD_DESChangKey, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESChangKeySetting(byte KeySetting) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = KeySetting;
        int result = optFunction(CMD_DESChangKeySetting, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESGetKeyVersion(byte KeyNO, byte[] pKeyVersion) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[]  rLen = new int[1];

        wData[0] = KeyNO;
        int result = optFunction(CMD_DESGetKeyVersion, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pKeyVersion != null) {

            pKeyVersion[0] = rData[0];
        }
        return result;
    }

    public int DESCreateApplication(byte[] AID, byte KeySetting, byte KeyNo) {
        byte[] wData = new byte[5];
        byte[] rData = new byte[1024];
        int[]  rLen = new int[1];

        wData[0] = AID[0];
        wData[1] = AID[1];
        wData[2] = AID[2];
        wData[3] = KeySetting;
        wData[4] = KeyNo;
        int result = optFunction(CMD_DESCreateApplication, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESDeleteApplication(byte[] AID) {
        byte[] wData = new byte[3];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = AID[0];
        wData[1] = AID[1];
        wData[2] = AID[2];
        int result = optFunction(CMD_DESDeleteApplication, wData, wData.length, rData, rLen);
        return result;
    }

    public int GetApplicationIDs(byte[] pAIDs, byte[] pAIDno) {
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        int result = optFunction(CMD_DESGetApplicationIDs, null, 0, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pAIDno != null) {
            pAIDno[0] = rData[0];
        }
        if (pAIDs != null) {
            System.arraycopy(rData, 1, pAIDs, 0, rLen[0] - 1);
        }
        return result;
    }

    public int DESSelectApplication(byte[] AID) {
        byte[] wData = new byte[3];
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        wData[0] = AID[0];
        wData[1] = AID[1];
        wData[2] = AID[2];
        int result = optFunction(CMD_DESSelectApplication, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESFormatPicc() {
        byte[] rData = new byte[1024];
        int[] rLen = new int[1];

        int result = optFunction(CMD_DESFormatPicc, null, 0, rData, rLen);
        return result;
    }

    public int DESGetDESVersion(byte[] relen, byte[] rebuf) {
        byte[] rData = new byte[1024];
        int[]  rLen = new int[1];

        int result = optFunction(CMD_DESGetDESVersion, null, 0, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (relen != null) {
            relen[0] = (byte) (rLen[0] & 0xff);
        }
        if (rebuf != null) {
            System.arraycopy(rData, 0, rebuf, 0, rLen[0]);
        }
        return result;
    }

    public int DESGetFileIDs(byte[] pIDs, byte[] pFileIDs) {
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        int result = optFunction(CMD_DESGetFileIDs, null, 0, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pIDs != null) {
            pIDs[0] = rData[0];
        }
        if (pIDs[0] != 0x00) {
            System.arraycopy(rData, 0, pFileIDs, 0, pIDs[0]);
        }
        return result;
    }

    public int DESGetFileSettings(byte FileID, byte[] pInfoLen, byte[] pFileInfo) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        int result = optFunction(CMD_DESGetFileSettings, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (pInfoLen != null) {
            pInfoLen[0] = (byte)rLen[0];
        }
        if (pFileInfo != null) {
            System.arraycopy(rData,0, pFileInfo,0, rLen[0]);
        }
        return result;
    }

    public int DESChangeFileSettings(byte FileID, byte NewComSet, byte[] NewAccessRights) {
        byte[] wData = new byte[4];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = NewComSet;
        wData[2] = NewAccessRights[0];
        wData[3] = NewAccessRights[1];
        int result = optFunction(CMD_DESChangeFileSettings, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESCreateStdDataFile(byte FileID, byte ComSet, byte[] AccessRights, byte[] FileSize) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = AccessRights[0];
        wData[3] = AccessRights[1];
        wData[4] = FileSize[0];
        wData[5] = FileSize[1];
        int result = optFunction(CMD_DESCreateStdDataFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESCreateBackupDataFile(byte FileID, byte ComSet, byte[] AccessRights, byte[] FileSize) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = AccessRights[0];
        wData[3] = AccessRights[1];
        wData[4] = FileSize[0];
        wData[5] = FileSize[1];
        int result = optFunction(CMD_DESCreateBackupDataFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESCreateValueFile(byte FileID, byte ComSet, byte[] AccessRights, byte[] LowerLimit, byte[] UpperLimit, byte[] Value, byte LimitCredit) {
        byte[] wData = new byte[17];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = AccessRights[0];
        wData[3] = AccessRights[1];
        wData[4] = LowerLimit[0];
        wData[5] = LowerLimit[1];
        wData[6] = LowerLimit[2];
        wData[7] = LowerLimit[3];
        wData[8] = UpperLimit[0];
        wData[9] = UpperLimit[1];
        wData[10] = UpperLimit[2];
        wData[11] = UpperLimit[3];
        wData[12] = Value[0];
        wData[13] = Value[1];
        wData[14] = Value[2];
        wData[15] = Value[3];
        wData[16] = LimitCredit;
        int result = optFunction(CMD_DESCreateValueFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESCreateLinearRecordFile(byte FileID, byte ComSet, byte[] AccessRights, byte[] FileSize, byte[] RecordsNum) {
        byte[] wData = new byte[8];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = AccessRights[0];
        wData[3] = AccessRights[1];
        wData[4] = FileSize[0];
        wData[5] = FileSize[1];
        wData[6] = RecordsNum[0];
        wData[7] = RecordsNum[1];
        int result = optFunction(CMD_DESCreateLinearRecordFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESCreateCyclicRecordFile(byte FileID, byte ComSet, byte[] AccessRights, byte[] FileSize, byte[] RecordsNum) {
        byte[] wData = new byte[8];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = AccessRights[0];
        wData[3] = AccessRights[1];
        wData[4] = FileSize[0];
        wData[5] = FileSize[1];
        wData[6] = RecordsNum[0];
        wData[7] = RecordsNum[1];
        int result = optFunction(CMD_DESCreateCyclicRecordFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESDeleteDESFile(byte FileID) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        int result = optFunction(CMD_DESDeleteDESFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESEV1ReadData(byte FileID, byte ComSet, byte[] Offset, byte[] Length, byte[] pBuf, int[] RcvLen) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = Offset[0];
        wData[3] = Offset[1];
        wData[4] = Length[0];
        wData[5] = Length[1];
        int result = optFunction(CMD_DESEV1ReadData, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (RcvLen != null) {
            RcvLen[0] = rLen[0];
        }
        if (pBuf != null) {
            System.arraycopy(rData,0,pBuf,0,rLen[0]);
        }
        return result;
    }

    public int DESEV1WriteData(byte FileID, byte ComSet, byte[] Offset, byte[] Length, byte[] pBuf) {
        int len = Math.min((Length[0] | (Length[1] << 8) & 0xFFFF),  pBuf.length);
        byte[] wData = new byte[len + 6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = Offset[0];
        wData[3] = Offset[1];
        wData[4] = Length[0];
        wData[5] = Length[1];
        System.arraycopy(pBuf,0, wData,6, len);
        int result = optFunction(CMD_DESEV1WriteData, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESEV1GetValue(byte FileID, byte ComSet, byte[] Value) {
        byte[] wData = new byte[2];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        int result = optFunction(CMD_DESEV1GetValue, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (Value != null) {
            System.arraycopy(rData,0, Value,0, 4);
        }
        return result;
    }

    public int DESEV1_CREDIT(byte FileID, byte ComSet, byte[] Value) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = Value[0];
        wData[3] = Value[1];
        wData[4] = Value[2];
        wData[5] = Value[3];
        int result = optFunction(CMD_DESEV1_CREDIT, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESEV1_DEBIT(byte FileID, byte ComSet, byte[] Value) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = Value[0];
        wData[3] = Value[1];
        wData[4] = Value[2];
        wData[5] = Value[3];
        int result = optFunction(CMD_DESEV1_DEBIT, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESEV1_LIMITEDCREDIT(byte FileID, byte ComSet, byte[] Value) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = Value[0];
        wData[3] = Value[1];
        wData[4] = Value[2];
        wData[5] = Value[3];
        int result = optFunction(CMD_DESEV1_LIMITEDCREDIT, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESEV1WriteRecord(byte FileID, byte ComSet, byte[] Offset, byte[] Length, byte[] pBuf) {
        int len = Math.min((Length[0] | (Length[1] << 8) & 0xFFFF),  pBuf.length);
        byte[] wData = new byte[len + 6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = Offset[0];
        wData[3] = Offset[1];
        wData[4] = Length[0];
        wData[5] = Length[1];
        System.arraycopy(pBuf,0, wData,6, len);
        int result = optFunction(CMD_DESEV1WriteRecord, wData, wData.length, rData, rLen);
        return result;
    }

    public int DESEV1ReadRecord(byte FileID, byte ComSet, byte[] RecordNo, byte[] RecordNum, byte[] pBuf, int[] RcvLen) {
        byte[] wData = new byte[6];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        wData[1] = ComSet;
        wData[2] = RecordNo[0];
        wData[3] = RecordNo[1];
        wData[4] = RecordNum[0];
        wData[5] = RecordNum[1];
        int result = optFunction(CMD_DESEV1ReadRecord, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (RcvLen != null) {
            RcvLen[0] = rLen[0];
        }
        if (pBuf != null) {
            System.arraycopy(rData,0,pBuf,0,rLen[0]);
        }
        return result;
    }

    public int DESClearRecordFile(byte FileID) {
        byte[] wData = new byte[1];
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        wData[0] = FileID;
        int result = optFunction(CMD_DESClearRecordFile, wData, wData.length, rData, rLen);
        return result;
    }

    public int DES_CommitTransaction() {
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        int result = optFunction(CMD_DES_CommitTransaction, null, 0, rData, rLen);
        return result;
    }

    public int DES_AbortTransaction() {
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        int result = optFunction(CMD_DES_AbortTransaction, null, 0, rData, rLen);
        return result;
    }

    public int ISO15693_Select(byte flag, byte[] uid, byte[] pBuf, int[] RcvLen) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flag & 0x24) == (byte)0x20) {
            wData = new byte[9];
            wData[0] = flag;
            System.arraycopy(uid,0,wData,1,8);
        } else {
            wData = new byte[1];
            wData[0] = flag;
        }
        int result = optFunction(CMD_ISO15693_Select, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (RcvLen != null) {
            RcvLen[0] = rLen[0];
        }
        if (pBuf != null) {
            System.arraycopy(rData,0,pBuf,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_StayQuiet(byte flag, byte[] uid, byte[] pBuf, int[] RcvLen) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flag & 0x24) == (byte)0x20) {
            wData = new byte[9];
            wData[0] = flag;
            System.arraycopy(uid,0,wData,1,8);
        } else {
            wData = new byte[1];
            wData[0] = flag;
        }
        int result = optFunction(CMD_ISO15693_StayQuiet, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (RcvLen != null) {
            RcvLen[0] = rLen[0];
        }
        if (pBuf != null) {
            System.arraycopy(rData,0,pBuf,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Read_Block(byte flags, byte blnr, byte nbl, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[11];
            wData[0] = flags;
            wData[1] = blnr;
            wData[2] = nbl;
            System.arraycopy(uid,0,wData,3,8);
        } else {
            wData = new byte[3];
            wData[0] = flags;
            wData[1] = blnr;
            wData[2] = nbl;
        }
        int result = optFunction(CMD_ISO15693_Read_Block, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null){
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0,resp,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Write_Block(byte flags, byte blnr, byte nbl, byte[] uid, byte[] dtw, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[15];
            wData[0] = flags;
            wData[1] = blnr;
            wData[2] = nbl;
            System.arraycopy(uid,0,wData,3,8);
            System.arraycopy(dtw,0,wData,11,4);
        } else {
            wData = new byte[7];
            wData[0] = flags;
            wData[1] = blnr;
            wData[2] = nbl;
            System.arraycopy(dtw,0,wData,3,4);
        }
        int result = optFunction(CMD_ISO15693_Write_Block, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Lock_Block(byte flags, byte blnr, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[10];
            wData[0] = flags;
            wData[1] = blnr;
            System.arraycopy(uid,0,wData,2,8);
        } else {
            wData = new byte[2];
            wData[0] = flags;
            wData[1] = blnr;
        }
        int result = optFunction(CMD_ISO15693_Lock_Block, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Write_AFI(byte flags, byte AFI, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[10];
            wData[0] = flags;
            wData[1] = AFI;
            System.arraycopy(uid,0,wData,2,8);
        } else {
            wData = new byte[2];
            wData[0] = flags;
            wData[1] = AFI;
        }
        int result = optFunction(CMD_ISO15693_Write_AFI, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Lock_AFI(byte flags, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[9];
            wData[0] = flags;
            System.arraycopy(uid,0,wData,1,8);
        } else {
            wData = new byte[1];
            wData[0] = flags;
        }
        int result = optFunction(CMD_ISO15693_Lock_AFI, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

    public int  ISO15693_Write_DSFID(byte flags, byte DSFID, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[10];
            wData[0] = flags;
            wData[1] = DSFID;
            System.arraycopy(uid,0,wData,2,8);
        } else {
            wData = new byte[2];
            wData[0] = flags;
            wData[1] = DSFID;
        }
        int result = optFunction(CMD_ISO15693_Write_DSFID, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Lock_DSFID(byte flags, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[9];
            wData[0] = flags;
            System.arraycopy(uid,0,wData,1,8);
        } else {
            wData = new byte[1];
            wData[0] = flags;
        }
        int result = optFunction(CMD_ISO15693_Lock_DSFID, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

    public int ISO15693_Get_SysInfor(byte flags, byte[] uid, int[] resplen, byte[] resp) {
        byte[] wData;
        byte[] rData = new byte[1024];
        int[]  rLen  = new int[1];

        if ( (flags & 0x24) == (byte)0x20) {
            wData = new byte[9];
            wData[0] = flags;
            System.arraycopy(uid,0,wData,1,8);
        } else {
            wData = new byte[1];
            wData[0] = flags;
        }
        int result = optFunction(CMD_ISO15693_Get_SysInfor, wData, wData.length, rData, rLen);
        if (result != ERR_SUCCESS) {
            return result;
        }
        if (resplen != null) {
            resplen[0] = rLen[0];
        }
        if (resp != null) {
            System.arraycopy(rData,0, resp,0, rLen[0]);
        }
        return result;
    }

}
