"""Proxmark3 NG wire protocol constants and CRC."""
import struct
from enum import IntEnum

# --- Magic values ---
CMD_PREAMBLE_MAGIC = 0x61334d50   # "PM3a"
RESP_PREAMBLE_MAGIC = 0x62334d50  # "PM3b"
CMD_POSTAMBLE_NOCRC = 0x3361      # "a3"
RESP_POSTAMBLE_NOCRC = 0x3362     # "b3"

PM3_CMD_DATA_SIZE = 512

# --- Frame formats (little-endian) ---
CMD_PREAMBLE_FMT = "<IHH"    # magic(4) + length_ng(2) + cmd(2) = 8 bytes
CMD_PREAMBLE_SIZE = 8
CMD_POSTAMBLE_FMT = "<H"     # crc(2) = 2 bytes
CMD_POSTAMBLE_SIZE = 2

RESP_PREAMBLE_FMT = "<IHbbH"  # magic(4) + length_ng(2) + status(1) + reason(1) + cmd(2) = 10 bytes
RESP_PREAMBLE_SIZE = 10
RESP_POSTAMBLE_FMT = "<H"
RESP_POSTAMBLE_SIZE = 2

# --- MIX frame: 3x uint64 args at start of payload ---
MIX_ARGS_FMT = "<QQQ"
MIX_ARGS_SIZE = 24

# --- CRC-16/A (ISO 14443-3) ---
_CRC_TABLE: list[int] = []

def _reflect8(b: int) -> int:
    r = 0
    for i in range(8):
        if b & (1 << i):
            r |= 1 << (7 - i)
    return r

def _reflect16(v: int) -> int:
    r = 0
    for i in range(16):
        if v & (1 << i):
            r |= 1 << (15 - i)
    return r

def _init_crc_table() -> None:
    global _CRC_TABLE
    if _CRC_TABLE:
        return
    poly = 0x1021
    _CRC_TABLE = [0] * 256
    for i in range(256):
        c = _reflect8(i) << 8
        crc = 0
        for _ in range(8):
            if (crc ^ c) & 0x8000:
                crc = ((crc << 1) ^ poly) & 0xFFFF
            else:
                crc = (crc << 1) & 0xFFFF
            c = (c << 1) & 0xFFFF
        _CRC_TABLE[i] = _reflect16(crc)

def crc16_a(data: bytes) -> int:
    """CRC-16/A: poly=0x1021, init=0xC6C6, refin=true, refout=true, xorout=0x0000."""
    _init_crc_table()
    crc = _reflect16(0xC6C6)
    for b in data:
        crc = ((crc >> 8) ^ _CRC_TABLE[(crc & 0xFF) ^ b]) & 0xFFFF
    return crc


# --- Error codes ---
class PM3Status(IntEnum):
    SUCCESS = 0
    EUNDEF = -1
    EINVARG = -2
    EDEVNOTSUPP = -3
    ETIMEOUT = -4
    EOPABORTED = -5
    ENOTIMPL = -6
    ERFTRANS = -7
    EIO = -8
    EOVFLOW = -9
    ESOFT = -10
    EFLASH = -11
    EMALLOC = -12
    EFILE = -13
    ENOTTY = -14
    EINIT = -15
    EWRONGANSWER = -16
    EOUTOFBOUND = -17
    ECARDEXCHANGE = -18
    EAPDU_ENCODEFAIL = -19
    EAPDU_FAIL = -20
    EFAILED = -21
    EPARTIAL = -22
    ETEAROFF = -23
    ECRC = -24
    ESTATIC_NONCE = -25
    ENOPACS = -26
    ELENGTH = -27
    ENOKEY = -28
    ECRYPTO = -29
    ENOFILE = -30
    ENODATA = -98
    EFATAL = -99


# --- Command IDs ---
class Cmd(IntEnum):
    # Bootloader
    DEVICE_INFO = 0x0000
    FINISH_WRITE = 0x0003
    HARDWARE_RESET = 0x0004
    START_FLASH = 0x0005
    CHIP_INFO = 0x0006
    BL_VERSION = 0x0007

    # Debug
    DEBUG_PRINT_STRING = 0x0100
    DEBUG_PRINT_INTEGERS = 0x0101
    DEBUG_PRINT_BYTES = 0x0102

    # System
    NACK = 0x00FE
    ACK = 0x00FF
    BUFF_CLEAR = 0x0105
    READ_MEM = 0x0106
    VERSION = 0x0107
    STATUS = 0x0108
    PING = 0x0109
    READ_MEM_DOWNLOAD = 0x010A
    READ_MEM_DOWNLOADED = 0x010B
    DOWNLOAD_EML_BIGBUF = 0x0110
    DOWNLOADED_EML_BIGBUF = 0x0111
    CAPABILITIES = 0x0112
    QUIT_SESSION = 0x0113
    SET_DBGMODE = 0x0114
    STANDALONE = 0x0115
    WTX = 0x0116
    TIA = 0x0117
    BREAK_LOOP = 0x0118
    SET_TEAROFF = 0x0119
    LED_CONTROL = 0x011A
    GET_DBGMODE = 0x0120

    # FPGA
    FPGA_MAJOR_MODE_OFF = 0x0500

    # LF
    LF_TI_READ = 0x0202
    LF_TI_WRITE = 0x0203
    LF_ACQ_RAW_ADC = 0x0205
    LF_MOD_THEN_ACQ_RAW_ADC = 0x0206
    DOWNLOAD_BIGBUF = 0x0207
    DOWNLOADED_BIGBUF = 0x0208
    LF_UPLOAD_SIM_SAMPLES = 0x0209
    LF_SIMULATE = 0x020A
    LF_HID_WATCH = 0x020B
    LF_HID_SIMULATE = 0x020C
    LF_SET_DIVISOR = 0x020D
    LF_SIMULATE_BIDIR = 0x020E
    SET_ADC_MUX = 0x020F
    LF_HID_CLONE = 0x0210
    LF_EM410X_CLONE = 0x0211
    LF_T55XX_READBL = 0x0214
    LF_T55XX_WRITEBL = 0x0215
    LF_T55XX_RESET_READ = 0x0216
    LF_PCF7931_READ = 0x0217
    LF_EM4X_READWORD = 0x0218
    LF_EM4X_WRITEWORD = 0x0219
    LF_IO_WATCH = 0x021A
    LF_EM4X_PROTECTWORD = 0x021B
    LF_EM410X_WATCH = 0x021C
    LF_SAMPLING_SET_CONFIG = 0x021D
    LF_FSK_SIMULATE = 0x021E
    LF_ASK_SIMULATE = 0x021F
    LF_PSK_SIMULATE = 0x0220
    LF_AWID_WATCH = 0x0221
    LF_VIKING_CLONE = 0x0222
    LF_PCF7931_WRITE = 0x0223
    LF_T55XX_WAKEUP = 0x0224
    LF_COTAG_READ = 0x0225
    LF_T55XX_SET_CONFIG = 0x0226
    LF_SAMPLING_PRINT_CONFIG = 0x0227
    LF_SAMPLING_GET_CONFIG = 0x0228
    LF_EM4X_LOGIN = 0x0229
    LF_EM4X_BF = 0x022A
    LF_T55XX_CHK_PWDS = 0x0230
    LF_T55XX_DANGERRAW = 0x0231
    LF_NRZ_SIMULATE = 0x0232
    LF_SNIFF_RAW_ADC = 0x0360
    LF_HITAG_SNIFF = 0x0370

    # HF General
    HF_ACQ_RAW_ADC = 0x0301
    HF_ISO15693_ACQ_RAW_ADC = 0x0300
    HF_ISO15693_READER = 0x0310
    HF_ISO15693_SIMULATE = 0x0311
    HF_ISO15693_SNIFF = 0x0312
    HF_ISO15693_COMMAND = 0x0313
    HF_ISO15693_FINDAFI = 0x0315
    HF_ISO15693_CSETUID = 0x0316

    HF_ISO14443A_SNIFF = 0x0383
    HF_ISO14443A_SIMULATE = 0x0384
    HF_ISO14443A_READER = 0x0385
    HF_ISO14443A_GET_CONFIG = 0x03B1
    HF_ISO14443A_SET_CONFIG = 0x03B2

    HF_ISO14443B_COMMAND = 0x0305
    HF_ISO14443B_SIMULATE = 0x0381
    HF_ISO14443B_SNIFF = 0x0382

    # Tuning
    MEASURE_ANTENNA_TUNING = 0x0400
    MEASURE_ANTENNA_TUNING_HF = 0x0401
    MEASURE_ANTENNA_TUNING_LF = 0x0402
    LISTEN_READER_FIELD = 0x0420
    HF_DROPFIELD = 0x0430
    HF_DECAY = 0x0440

    # MIFARE Classic
    HF_MIFARE_EML_MEMCLR = 0x0601
    HF_MIFARE_EML_MEMSET = 0x0602
    HF_MIFARE_EML_MEMGET = 0x0603
    HF_MIFARE_SIMULATE = 0x0610
    HF_MIFARE_READER = 0x0611
    HF_MIFARE_NESTED = 0x0612
    HF_MIFARE_ACQ_ENCRYPTED_NONCES = 0x0613
    HF_MIFARE_ACQ_NONCES = 0x0614
    HF_MIFARE_STATIC_NESTED = 0x0615
    HF_MIFARE_READBL = 0x0620
    HF_MIFARE_READSC = 0x0621
    HF_MIFARE_WRITEBL = 0x0622
    HF_MIFARE_CHKKEYS = 0x0623
    HF_MIFARE_SETMOD = 0x0624
    HF_MIFARE_CHKKEYS_FAST = 0x0625
    HF_MIFARE_CHKKEYS_FILE = 0x0626
    HF_MIFARE_VALUE = 0x0627
    HF_MIFARE_READBL_EX = 0x0628
    HF_MIFARE_WRITEBL_EX = 0x0629
    HF_MIFARE_SNIFF = 0x0630
    HF_MIFARE_MFKEY = 0x0631
    HF_MIFARE_PERSONALIZE_UID = 0x0632
    HF_MIFARE_CSETBL = 0x0605
    HF_MIFARE_CGETBL = 0x0606
    HF_MIFARE_CIDENT = 0x0607

    # MIFARE Ultralight
    HF_MIFAREU_READBL = 0x0720
    HF_MIFAREU_READCARD = 0x0721
    HF_MIFAREU_WRITEBL = 0x0722
    HF_MIFARE_NACK_DETECT = 0x0730

    # HF Sniff/Plot
    HF_SNIFF = 0x0800
    HF_PLOT = 0x0801
    FPGAMEM_DOWNLOAD = 0x0802
    FPGAMEM_DOWNLOADED = 0x0803

    UNKNOWN = 0xFFFF
