from .rdadebug import write_register_int8
from .rdadebug import read_register_int8
from .rdadebug import write_block
from .rdadebug import compute_check
from .eprint import eprint

def h2p_command(msg):
    """ Format a frame for an h2p command

    The CPS software sends commands to a special debug register
    00000005.  Writing a value to this register throws an interupt
    which is picked up by a function on the device.

    0x00 : Command finished, clears semaphore
    0xA5 : Process command with RxByHostPortCB
    0xEE : Reboot
    0xFF : Handle with boot_HstCmdBasicHandler

    @param msg: the message to send
    @return: the frame to send

    """
    return write_register_int8(0x5, msg)

def set_uart_to_normal():
    """ Set device uart to host mode

    The CPS software sends repeated requests to set internal register
    00000003 to 0x80 which has the effect of locking the UART to debug
    mode

    @return: the frame to send

    """
    return write_register_int8(3, 0x00)

def set_uart_to_host():
    """ Set device uart to host mode

    The CPS software sends repeated requests to set internal register
    00000003 to 0x80 which has the effect of locking the UART to debug
    mode

    @return: the frame to send

    """
    return write_register_int8(3, 0x80)

def reboot_and_freeze():
    """ Reboot and freeze the processor

    This command comes from coolwatcher and resets the processor and
    immediately halts it.  This is useful for stepping through the
    boot process, but also allows some areas of ROM to be read without
    crashing

    @return: the frame to send

    """
    return write_register_int8(0, 0x03)

def read_uart_to_host():
    """ make a frame containing a knock command

    this function creates a frame that I assume wakes up the device
    for further commands.

    @return: the frame to send

    """
    return read_register_int8(3)

def ate_command(cmd, p_atecps_write):
    """ make a frame containing an ATE command

    @param cmd: the command to send
    @param p_atecps_write: the address of the CPS write register
    @return: the frame to send

    """
    cmd = bytearray(cmd, 'utf-8') + b'\r'
    cmd += bytes(4 - len(cmd) % 4)
    return write_block(p_atecps_write, cmd)

def cps_command(cmd, p_atecps_write):
    """ make a frame containing an CPS command

    @param cmd: the command to send
    @param p_atecps_write: the address of the CPS write register
    @return: the frame to send

    """
    length = (len(cmd) + 4).to_bytes(1, 'big')
    check = compute_check(length + cmd)
    begin = bytes([0xaa])
    end = bytes([0xbb])
    msg = begin + length + cmd + check + end
    padding = 4 - (len(msg) % 4)
    return write_block(p_atecps_write, msg + bytes([0x00]) * padding)

class CPSFrame:
    """ Received CPS class

    This class decodes CPS frames received from the device.
    """
    check_fail = False
    length = 0
    type = 0
    content = bytes([])
    def __init__(self, msg):
        eprint(msg.hex())
        if (msg[-1].to_bytes(1, 'big') != compute_check(msg[1:-2])):
            self.check_fail = True
            eprint('CPS frame check failed')
            return
        self.length = msg[1]
        self.type = int.from_bytes(msg[2:4], 'big')
        self.is_ok = msg[4] == 0x01
        self.content = msg[5:-3]
    def __repr__(self):
        return 'packet length {} type {} is_ok {} content {}'.format(self.length, self.type, self.is_ok, self.content)

class ChanInfoFrame(CPSFrame):
    """ Received ChanInfoFrame class

    This class decodes ChanInfoFrame frames received from the device.

    "\tcpsInst.chanInfo.nChanIndex=%d\n
     \tcpsInst.chanInfo.nChanType=%d\n
     \tcpsInst.chanInfo.nVox=%d\n
     \tcpsInst.chanInfo.nPower=%d\n
     \tcpsInst.chanInfo.nRxFreq=%d\n
     \tcpsInst.chanInfo.nTxFreq=%d\n
     \tcpsInst.chanInfo.nTxContactsIdx=0x%08x\n
     \tcpsInst.chanInfo.nColorCode=%d\n
     \tcpsInst.chanInfo.nTimeSlot=%d\n
     \tcpsInst.chanInfo.bPoliteCall=%d\n"
     \tcpsInst.chanInfo.nEmrSys=%d\n
     \tcpsInst.chanInfo.nEncry=%d\n 
     \tcpsInst.chanInfo.nTypeWideNarrow=%d\n 
     \tcpsInst.chanInfo.nRxCtdcs=%d\n 
     \tcpsInst.chanInfo.bRxCtdcsInvert=%d\n 
     \tcpsInst.chanInfo.bTxCtdcsInvert=%d\n 
     \tcpsInst.chanInfo.nTxCtdcs=%d\n 
     \tcpsInst.chanInfo.nRxGrpListIdx=%d\n"
    """
    def __init__(self, msg):
        super().__init__(msg)
        self.index = int.from_bytes(self.content[0:2], 'little')
        self.chantype = self.content[2]
        self.rxFreq = int.from_bytes(self.content[4:8], 'little')
        self.txFreq = int.from_bytes(self.content[8:12], 'little')
        self.txContactIndex = int.from_bytes(self.content[12:16], 'little')
        self.colorCode = self.content[16]
        self.timeslot = self.content[17]
        self.polite = self.content[18]
        self.emrSys = int.from_bytes(self.content[1:2], 'big')
        self.encryption = int.from_bytes(self.content[1:2], 'big')
        self.widenarrow = int.from_bytes(self.content[1:2], 'big')
        self.rxctdcs = int.from_bytes(self.content[1:2], 'big')
        self.rxctdcsinvert = int.from_bytes(self.content[1:2], 'big')
        self.txctdcsinvert = int.from_bytes(self.content[1:2], 'big')
        self.txctdcs = int.from_bytes(self.content[1:2], 'big')
        self.rxGroupIdx = int.from_bytes(self.content[1:2], 'big')
        self.vox = int.from_bytes(self.content[1:2], 'big')
    def __repr__(self):
        return 'packet length {} type {} is_ok {} index {} chantype {} rxfreq {} txfreq {}'.format(
            self.length, self.type, self.is_ok, self.index, self.chantype, self.rxFreq, self.txFreq)