package com.asreader.asr033w.asr033w.protocol;

import android.util.Log;
import com.asreader.asr033w.asr033w.ASR033WCallBack;
import com.asreader.asr033w.asr033w.asr033wenum.ASR033WConnectMethod;
import com.asreader.asr033w.asr033w.asr033wenum.ASR033WSubCommandType;
import com.asreader.asr033w.asr033w.info.ASR033WReaderInfo;
import com.asreader.asr033w.asr033w.network.ASR033WNetworkMQTT;
import com.asreader.asr033w.asr033w.network.ASR033WNetworkTCP;
import com.asreader.asr033w.asr033w.asr033wenum.ASR033WResultCode;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import com.asreader.asr033w.asr033w.protocol.ASR033WStruct.rcp_033w_packet_length;
import com.asreader.asr033w.asr033w.util.ASR033WBitConvert;

public class ASR033WProtocol {
    private ASR033WNetworkTCP _currentTCP;
    private ASR033WNetworkMQTT _currentMQTT;
    private ASR033WReaderInfo asr033WReaderInfo;
    private ASR033WCallBack callBack;
    private ASR033WConnectMethod currentConnectMethod = ASR033WConnectMethod.ASR033WConnectMethod_TCP;

    private static final byte PACKET_PREFIX = 0x7E; // '~'
    private static final byte[] PACKET_SUFFIX = new byte[]{0x0D, 0x0A}; // "\r\n"
    private static final byte PACKET_ENED = 0x0D; // "\r"
    private static final byte PACKET_ENEA = 0x0A; // "\n"

    private static final byte CMD_SET_PARAMETER = 0x77; // 'w'
    private static final byte CMD_SET_RESULT = 0x57; // 'W'
    private static final byte CMD_GET_PARAMETER = 0x72; // 'r'
    private static final byte CMD_PARAMETER_VALUE = 0x52; // 'R'
    private static final byte CMD_COMMAND = 0x61; // 'a'
    private static final byte CMD_COMMAND_ACK = 0x41; // 'A'
    private static final byte CMD_EVENT = 0x65; // 'e'
    private static final byte CMD_DEBUG = 0x64; // 'd'
    private static final byte CMD_HID = (byte) 0xAA;

    public static final byte PARAM_HARDWARE_VERSION = 0x48; // 'H'
    public static final byte PARAM_FIRMWARE_VERSION = 0x76; // 'v'
    public static final byte PARAM_MAC_ADDRESS = 0x4D; // 'M'

    public static final byte PARAM_REGION = 0x67; // 'g'
    public static final byte PARAM_SERIAL_NUMBER = 0x53; // 'S'
    public static final byte PARAM_UHF_VERSION = 0x6b; // 'k'

    public static final byte SUBCMD_INVENTORY = 0x66; // 'f'
    public static final byte SUBCMD_STOP_OPERATION = 0x73; // 's'

    private static final int POS_CMD = 1;
    private static final int POS_SUB_CMD = 2;
    private static final int POS_DATA = 3;
    private static final int POS_RESULT_DATA = 10;

    private static final int DEFAULT_TIMEOUT = 3500;

    private static final byte EVENT_TAG_VALUE = 0x54; // 'T'
    private static final byte EVENT_HTTAG_VALUE = 0x48; // 'U'


    private static final byte[] STATUS_SUCCESS = new byte[]{0x30, 0x30, 0x30, 0x30};

    public ASR033WProtocol(ASR033WNetworkTCP asr033WNetworkTCP, ASR033WNetworkMQTT asr033WNetworkMQTT) {
        _currentTCP = asr033WNetworkTCP;
        _currentMQTT = asr033WNetworkMQTT;
        asr033WReaderInfo = ASR033WReaderInfo.getInstance();
    }

    public void setCallBack(ASR033WCallBack callBack) {
        this.callBack = callBack;
    }

    private int _bufHead = 0;
    private int tagLen = 0;
    private byte[] _buf = new byte[2048];

    public void receiveData(byte[] receiveData) {
        Log.d("ASR033WProtocol", "receiveData : " + byteArrayToHexString(receiveData));
        if (receiveData.length > 0 && callBack != null) {
            callBack.receivedASR033WData(receiveData);
            analysis(receiveData);
        }
    }
    private void analysis(byte[] receiveData) {
        try {
            int len = receiveData.length;
            for(int i = 0;i<len ; i++){
                _buf[_bufHead] = receiveData[i] ;
                _bufHead++;
                if (_bufHead >= 7){
                    if ((_buf[1] & 0xFF) == CMD_EVENT && (_buf[2] & 0xFF) == EVENT_TAG_VALUE){//Inventory
                        tagLen = ((_buf[3] & 0xFF) & 0xFF) ;
                    } else if ((_buf[1] & 0xFF) == CMD_COMMAND_ACK && (_buf[2] & 0xFF) == 0x72){//read
                        tagLen = ((_buf[3] & 0xFF) & 0xFF) ;
                    } else if ((_buf[1] & 0xFF) == CMD_HID){//HID
                        tagLen = ((_buf[2] & 0xFF) <<8) + (_buf[3] & 0xFF);
                    }else {
                        tagLen = 0;
                    }
                    if (_bufHead < tagLen){
                        continue;
                    }
                    if ((_buf[_bufHead - 2] & 0xFF) == PACKET_ENED && (_buf[_bufHead - 1] & 0xFF) == PACKET_ENEA){
                        byte[] bytes = new byte[_bufHead];
                        System.arraycopy(_buf, 0, bytes, 0, _bufHead);
                        _bufHead = 0;
                        _buf= new byte[2048];
                        decodePacket(bytes);
                    }
                }
            }
        }catch (Exception e){
            Log.d("ASR033WProtocol","e：" + e.getMessage());
        }
    }
    private void decodePacket(byte[] packet){
        try {
            int length = packet.length;
            byte header = packet[0];
            byte endD = packet[length - 2];
            byte endA = packet[length - 1];
            if (header == PACKET_PREFIX && endD == PACKET_ENED && endA == PACKET_ENEA) {
                byte packetEvent = packet[1];
                byte packetType = packet[2];
                if (packetEvent == CMD_PARAMETER_VALUE) {
                    byte[] newPacket = new byte[length - 3];
                    System.arraycopy(packet, 1, newPacket, 0, length - 3);
                    if (packetType == PARAM_MAC_ADDRESS) {
                        byte[] statusByte = getPacketStatus(newPacket);
                        if (Arrays.equals(statusByte, STATUS_SUCCESS)) {
                            byte[] valueByte = getPacketValue(newPacket);
                            try {
                                String value = ASR033WBitConvert.getString(valueByte);
                                if (callBack != null) {
                                    callBack.receivedASR033WMacAddress(value);
                                }
                            } catch (Exception e) {
                                if (callBack != null) {
                                    callBack.receivedASR033WMacAddress("");
                                }
                            }
                        } else {
                            if (callBack != null) {
                                callBack.receivedASR033WMacAddress("");
                            }
                        }
                    } else if (packetType == PARAM_HARDWARE_VERSION) {
                        byte[] statusByte = getPacketStatus(newPacket);
                        if (Arrays.equals(statusByte, STATUS_SUCCESS)) {
                            byte[] valueByte = getPacketValue(newPacket);
                            try {
                                String version = ASR033WBitConvert.getString(valueByte);
                                if (callBack != null) {
                                    callBack.receivedASR033WHardwareVersion(version);
                                }
                            } catch (Exception e) {
                                if (callBack != null) {
                                    callBack.receivedASR033WHardwareVersion("");
                                }
                            }
                        } else {
                            if (callBack != null) {
                                callBack.receivedASR033WHardwareVersion("");
                            }
                        }
                    } else if (packetType == PARAM_FIRMWARE_VERSION) {
                        byte[] statusByte = getPacketStatus(newPacket);
                        if (Arrays.equals(statusByte, STATUS_SUCCESS)) {
                            byte[] valueByte = getPacketValue(newPacket);
                            try {
                                String version = ASR033WBitConvert.getString(valueByte);
                                if (callBack != null) {
                                    callBack.receivedASR033WFirmwareVersion(version);
                                }
                            } catch (Exception e) {
                                if (callBack != null) {
                                    callBack.receivedASR033WFirmwareVersion("");
                                }
                            }
                        } else {
                            if (callBack != null) {
                                callBack.receivedASR033WFirmwareVersion("");
                            }
                        }
                    } else if (packetType == PARAM_REGION) {
                        byte[] statusByte = getPacketStatus(newPacket);
                        if (Arrays.equals(statusByte, STATUS_SUCCESS)) {
                            byte[] valueByte = getPacketValue(newPacket);
                            try {
                                String value = ASR033WBitConvert.getString(valueByte);
                                HashMap<String, String> dictionary = new HashMap<>();
                                dictionary.put("0", "GB");
                                dictionary.put("1", "GB1");
                                dictionary.put("2", "GB2");
                                dictionary.put("3", "North America");
                                dictionary.put("4", "Europe");
                                dictionary.put("5", "Japan250mW");
                                dictionary.put("9", "TEST");
                                dictionary.put("10", "Japan(1W)");
                                dictionary.put("11", "Australia");
                                dictionary.put("16", "Japan(200mW)");
                                Set<String> keys = dictionary.keySet();
                                for (String key: keys) {
                                    if (key.equals(value)) {
                                        value = dictionary.get(key);
                                    }
                                }
                                if (callBack != null) {
                                    callBack.receivedASR033WRegionName(value);
                                }
                            } catch (Exception e) {
                                if (callBack != null) {
                                    callBack.receivedASR033WRegionName("");
                                }
                            }
                        } else {
                            if (callBack != null) {
                                callBack.receivedASR033WRegionName("");
                            }
                        }
                    } else if (packetType == PARAM_SERIAL_NUMBER) {
                        byte[] statusByte = getPacketStatus(newPacket);
                        if (Arrays.equals(statusByte, STATUS_SUCCESS)) {
                            byte[] valueByte = getPacketValue(newPacket);
                            try {
                                String version = ASR033WBitConvert.getString(valueByte);
                                if (callBack != null) {
                                    callBack.receivedASR033WSerialNumber(version);
                                }
                            } catch (Exception e) {
                                if (callBack != null) {
                                    callBack.receivedASR033WSerialNumber("");
                                }
                            }
                        } else {
                            if (callBack != null) {
                                callBack.receivedASR033WSerialNumber("");
                            }
                        }
                    } else if (packetType == PARAM_UHF_VERSION) {
                        byte[] statusByte = getPacketStatus(newPacket);
                        if (Arrays.equals(statusByte, STATUS_SUCCESS)) {
                            byte[] valueByte = getPacketValue(newPacket);
                            try {
                                String version = ASR033WBitConvert.getString(valueByte);
                                if (callBack != null) {
                                    callBack.receivedASR033WRFModuleVersion(version);
                                }
                            } catch (Exception e) {
                                if (callBack != null) {
                                    callBack.receivedASR033WRFModuleVersion("");
                                }
                            }
                        } else {
                            if (callBack != null) {
                                callBack.receivedASR033WRFModuleVersion("");
                            }
                        }
                    }
                } else if (packetEvent == CMD_HID) {
                    if (packet.length > 4) {
                        int hidLen = ((packet[2] & 0xFF) <<8) + (packet[3] & 0xFF);
                        byte[] hidPacket = new byte[hidLen];
                        System.arraycopy(packet, 4, hidPacket, 0, hidLen);
                        if (callBack != null) {
                            callBack.receivedASR033WHIDData(hidPacket);
                        }
                    }
                } else if (packetEvent == CMD_EVENT) {
                    if (packetType == EVENT_TAG_VALUE) {
                        String pc = "";
                        String epc = "";
                        String tid = "";
                        int antenna = 0;
                        int rssi = 0;
                        double phase = 0;
                        double frequency = 0;

                        int index = 3;
                        int len = packet[index] & 0xFF;
                        index++;
                        int antByte = packet[index] & 0xFF;
                        index++;
                        switch (antByte) {
                            case 1:
                                antenna = 1;
                                break;
                            case 2:
                                antenna = 2;
                                break;
                            case 4:
                                antenna = 3;
                                break;
                            case 8:
                                antenna = 4;
                                break;
                            case 16:
                                antenna = 5;
                                break;
                            case 32:
                                antenna = 6;
                                break;
                            case 64:
                                antenna = 7;
                                break;
                            case 128:
                                antenna = 8;
                                break;
                            default:
                                break;
                        }
                        int epcLength = (packet[index] &0xF8 )>> 3;
                        byte[] pcByte = {(byte) (packet[index] & 0xFF),(byte) (packet[index+1] & 0xFF)};
                        index = index + 2;
                        pc = byteArrayToHexString(pcByte);

                        byte[] epcByte = new byte[epcLength *2];
                        System.arraycopy(packet, index, epcByte, 0, epcLength *2);
                        index = index + epcLength * 2;

                        epc = byteArrayToHexString(epcByte);

                        byte[] rssiByte ={(byte) (packet[index] & 0xFF),(byte) (packet[index+1] & 0xFF),(byte) (packet[index+2] & 0xFF)};
                        index = index + 3;

                        try {
                            rssi = Integer.parseInt(ASR033WBitConvert.getString(rssiByte));
                        } catch (Exception e) {
                            rssi = 0;
                        }

                        phase = ((packet[index] & 0xFF) / 128.0) * 360.0;
                        index++;


                        double frequencyInt = ((packet[index] & 0xFF) <<24)+((packet[index + 1] & 0xFF)<<16)+((packet[index + 2] & 0xFF)<<8)+(packet[index + 3] & 0xFF);
                        index = index + 4;
                        frequency = frequencyInt / 1000.0;

                        if (len > index + 2){
                            int tidLength = (packet[index] & 0xFF << 8) + packet[index + 1] & 0xFF;
                            index = index + 2;
                            byte[] tidPacket = new byte[tidLength];
                            System.arraycopy(packet, index, tidPacket, 0, tidLength);
                            tid = byteArrayToHexString(tidPacket);
                        }

                        if (callBack != null){
                            callBack.receivedASR033WInventoryPC(pc, epc, tid, antenna, rssi, phase, frequency);
                        }
                    }
                } else if (packetEvent == CMD_COMMAND_ACK) {
                    ASR033WSubCommandType commandType = ASR033WSubCommandType.valueOf(packetType);
                    if (callBack != null) {
                        callBack.receivedASR033WActionChanged(commandType);
                    }
                } else {

                }
            }
        } catch (Exception e){

        }
    }

    private byte[] getPacketStatus(byte[] newPacket) {
        byte[] statusPacket = new byte[4];
        System.arraycopy(newPacket, 2, statusPacket, 0, 4);
        String statusString = byteArrayToHexString(statusPacket);
        Log.i("QQQ", "statusString   " + statusString);
        return statusPacket;
    }

    private byte[] getPacketValue(byte[] newPacket) {
        byte[] valuePacket = new byte[newPacket.length - 6];
        System.arraycopy(newPacket, 6, valuePacket, 0, newPacket.length - 6);
        String valueString = byteArrayToHexString(valuePacket);
        Log.i("QQQ", "valueString   " + valueString);
        return valuePacket;
    }

    public ASR033WResultCode getRegionName() {
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_GET_PARAMETER;
        buf[2] = PARAM_REGION;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        return sendData(buf);
    }

    public ASR033WResultCode getSerialNumber() {
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_GET_PARAMETER;
        buf[2] = PARAM_SERIAL_NUMBER;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        return sendData(buf);
    }

    public ASR033WResultCode getHardwareVersion() {
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_GET_PARAMETER;
        buf[2] = PARAM_HARDWARE_VERSION;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        return sendData(buf);
    }

    public ASR033WResultCode getRFModuleVersion() {
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_GET_PARAMETER;
        buf[2] = PARAM_UHF_VERSION;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        return sendData(buf);
    }

    public ASR033WResultCode getFirmwareVersion() {
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_GET_PARAMETER;
        buf[2] = PARAM_FIRMWARE_VERSION;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        return sendData(buf);
    }

    public ASR033WResultCode startInventoryAntenna1(byte[] data){
        int dataLen = data.length;
        byte[] buf = new byte[5 + dataLen];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_COMMAND;
        buf[2] = SUBCMD_INVENTORY;
        if (data != null) {
            if (data.length > 0) {
                System.arraycopy(data, 0, buf, 3, data.length);
            }
        }
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3 + dataLen, PACKET_SUFFIX.length);
        ASR033WResultCode resultCode = sendData(buf);
        return resultCode;
    }

    public ASR033WResultCode stopInventory(){
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_COMMAND;
        buf[2] = SUBCMD_STOP_OPERATION;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        ASR033WResultCode resultCode = sendData(buf);
        return resultCode;
    }
    public ASR033WResultCode getMacAddress() {
        byte[] buf = new byte[5];
        buf[0] = PACKET_PREFIX;
        buf[1] = CMD_GET_PARAMETER;
        buf[2] = PARAM_MAC_ADDRESS;
        System.arraycopy(PACKET_SUFFIX, 0, buf, 3, PACKET_SUFFIX.length);
        return sendData(buf);
    }
    public ASR033WResultCode sendData(byte[] sendData) {
        Log.d("ASR033WProtocol", "sendData : " + byteArrayToHexString(sendData));
        if (currentConnectMethod == ASR033WConnectMethod.ASR033WConnectMethod_TCP) {
            if (_currentTCP.isConnected) {
                _currentTCP.sendData(sendData);
                return ASR033WResultCode.ASR033WResultNoError;
            } else {
                return ASR033WResultCode.ASR033WResultNotConnected;
            }
        } else {
            if (_currentMQTT.isConnected) {
                _currentMQTT.sendData(sendData);
                return ASR033WResultCode.ASR033WResultNoError;
            } else {
                return ASR033WResultCode.ASR033WResultNotConnected;
            }
        }
    }
    public void setASR033WConnectMethod(ASR033WConnectMethod connectMethod) {
        currentConnectMethod = connectMethod;
    }
    private String byteArrayToHexString(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xFF & b);
            if (hex.length() == 1) {
                // 如果是一位的话，要补0
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
    private byte[] hexStringToByteArray(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i+1), 16));
        }
        return data;
    }
    public UnsignedShort getCRC16WithPacketLength(byte[] picket,UnsignedShort picketLength){
        char i, chChar;
        short wCRC = 0;
        for (int index = 1; index <= picketLength.getValue(); index++){
            chChar = (char)picket[index];
            wCRC ^= ((new UnsignedShort(chChar).getValue()) << 8);
            for (i = 0; i < 8; i++) {
                if ((wCRC & 0x8000) ==0x8000){
                    wCRC = (short) ((wCRC << 1) ^ 0x1021);//CRC_16_CCITT = 0x1021;
                }else{
                    wCRC <<= 1;
                }
            }
        }
        return new UnsignedShort(wCRC);
    }

    public HashMap<String, byte[]> interceptData(byte[] data, int type, int length) {
        HashMap<String, byte[]> dictionary = new HashMap<>();
        try {
            if (type == 0) {
                rcp_033w_packet_length rcpIntercept = new rcp_033w_packet_length();
                rcpIntercept.setData(data);
                int intercept_len = rcpIntercept.pl_length ;
                byte[] payload = new byte[intercept_len];
                System.arraycopy(rcpIntercept.payload, 0, payload, 0, intercept_len);
                byte[] interceptData = payload;
                byte[] mutableData =  new byte[data.length - intercept_len - 2];
                System.arraycopy(data, intercept_len + 2, mutableData, 0, data.length - intercept_len -2);
                dictionary.put(ASR033WDefine.ASR033W_PACKET_INTERCEPT_DATA, interceptData);
                dictionary.put(ASR033WDefine.ASR033W_PACKET_REMAIN_DATA, mutableData);
            } else {
                byte[] interceptData = new byte[length];
                System.arraycopy(data, 0, interceptData, 0, length);
                byte[] mutableData = new byte[data.length - length];
                System.arraycopy(data, length, mutableData, 0, data.length - length);
                dictionary.put(ASR033WDefine.ASR033W_PACKET_INTERCEPT_DATA, interceptData);
                dictionary.put(ASR033WDefine.ASR033W_PACKET_REMAIN_DATA, mutableData);
            }
        } catch (Exception e) {
            dictionary = new HashMap<>();
            Log.d("ASR033WProtocol","Exception dictionary :" + e.toString());

        }
        Log.d("ASR033WProtocol","dictionary :" + dictionary);
        return dictionary;
    }

    public byte[] SubData(byte[] original, int length) {
        byte[] det = new byte[original.length - length];
        System.arraycopy(original, length, det, 0, original.length - length);
        return det;
    }
    public byte[] SubData(byte[] original, int offset,int length) {
        byte[] det = new byte[length];
        System.arraycopy(original, offset, det, 0, length);
        return det;
    }
    public String calculationIPData(byte[] data){
        try {
            int a = data[0] & 0xFF;
            int b = data[1] & 0xFF;
            int c = data[2] & 0xFF;
            int d = data[3] & 0xFF;
            String string = String.format("%d.%d.%d.%d", a, b, c, d);
            return string;
        } catch (Exception e) {
            return "";
        }
    }
    public int byteArrayToInt(byte[] bytes) {
        int value = 0;
        for (int i = 0; i < bytes.length; i++) {
            value |= (bytes[i] & 0xFF) << (8 * i);
        }
        return value;
    }
}