import ctypes
from ctypes import Structure, c_uint64, c_int64, c_uint32, c_uint16, c_uint8, c_float

AXI_WRITE_REG = 1
AXI_READ_REG = 2
AXI_READ_RESP = 3
ACK_MSG = 4
NACK_MSG = 5
AXI_WRITE_REG_BURST = 6
RF_SPI_WRITE = 7
CONFIG_FLASH_WRITE = 8
SET_AD9081_DAC_NCO = 128
SET_AD9081_ADC_NCO = 129
SET_LANE_MAP = 130
AD9081_REG_WRITE = 131
AD9081_REG_READ = 132

ACK_FLAG_VALID_PACKET = 0x01
ACK_FLAG_VALID_EXECUTION = 0x02
ACK_FLAG_GOT_FRAME_END = 0x04

MAX_BURST_LENGTH = 512

HDR_FLAG_REQ_ACK = 0x01

class CpiHeader(Structure):
    _pack_ = 1
    _fields_ = [
        ("sync", c_uint32),
        ("num_pulses", c_uint32),
        ("num_samples", c_uint32),
        ("start_sample", c_uint32),
        ("tx_num_samples", c_uint32),
        ("tx_start_sample", c_uint32),
        ("pri", c_uint32),
        ("inter_cpi", c_uint32),

        # ("spare", c_uint32),  # Populated by FPGA
        ("pps_sec", c_uint64),  # Populated by FPGA
        ("pps_frac_sec", c_uint64),  # Populated by FPGA
        ("system_time", c_uint64),  # Populated by FPGA

        ("tx_lo_offset", c_float),
        ("rx_lo_offset", c_float),
        ("data1", c_uint32 * 240)  # Populated by user

    ]


class Header(Structure):
    _pack_ = 1
    _fields_ = [
        ("fsync", c_uint32),
        ("type", c_uint16),
        ("flags", c_uint16),
        ("length", c_uint16)
    ]

    # Have to put this here to get pycharm autocomplete to work, don't like this, but autocomplete is too convenient
    def __init__(self):
        self.fsync = 0xAABBCCDD
        self.type = 0
        self.flags = 0
        self.length = 0


def init_header(self, msg_id):
    self.header = Header()
    self.header.type = msg_id
    self.header.length = ctypes.sizeof(self)


class WriteRegType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("address", c_uint32),
        ("data", c_uint32)
    ]

    def __init__(self, msg_id=AXI_WRITE_REG):
        init_header(self, msg_id)
        self.address = 0
        self.data = 0


class WriteRegBurstType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("address", c_uint32),
        ("length", c_uint32),
        ("data", c_uint32 * MAX_BURST_LENGTH)
    ]

    def __init__(self, msg_id=AXI_WRITE_REG_BURST):
        init_header(self, msg_id)
        self.address = 0
        self.length = 0

class ConfigFlashWriteType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("address", c_uint32),
        ("length", c_uint32),
        ("data", c_uint8 * MAX_BURST_LENGTH)
    ]

    def __init__(self, msg_id=CONFIG_FLASH_WRITE):
        init_header(self, msg_id)
        self.address = 0
        self.length = 0


class AckType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("flags", c_uint32)
    ]

    def __init__(self):
        init_header(self, ACK_MSG)
        self.flags = 0


class ReadRequestType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("address", c_uint32)
    ]

    def __init__(self, msg_id=AXI_READ_REG):
        init_header(self, msg_id)
        self.address = 0


class ReadResponseType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("data", c_uint32)
    ]

    def __init__(self):
        init_header(self, AXI_READ_RESP)
        self.data = 0


class DacNcoConfigType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("channel", c_uint32),
        ("frequency", c_int64)
    ]

    def __init__(self):
        init_header(self, SET_AD9081_DAC_NCO)
        self.channel = 0
        self.frequency = 0

class AdcNcoConfigType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("channel", c_uint32),
        ("frequency", c_int64)
    ]

    def __init__(self):
        init_header(self, SET_AD9081_ADC_NCO)
        self.channel = 0
        self.frequency = 0

class RfSpiWriteType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("dev_sel", c_uint32),
        ("num_bits", c_uint32),
        ("data", c_uint32)
    ]

    def __init__(self):
        init_header(self, RF_SPI_WRITE)
        self.dev_sel = 0
        self.num_bits = 0
        self.data = 0

class LaneMapType(Structure):
    _pack_ = 1
    _fields_ = [
        ("header", Header),
        ("lane_map", c_uint8 * 8)
    ]

    def __init__(self):
        init_header(self, SET_LANE_MAP)
