import lora
import time

class JoinError(lora.LoRaException):
    '''Parent device (PC or pyb) failing to join LoRaWAN'''
    pass

class LoRaWAN(lora.LoRa):

    def config_otaa(self, appkey=None, appeui=None, deveui=None):
        ''' Configure Over The Air Authentication '''
        self._ser_write_read_verify("mac set adr on", "ok")

        self._ser_write("sys get hweui")
        hweui = self._ser_read()
        self._ser_write_read_verify("mac set deveui {0}".format(deveui), "ok")
        self._ser_write_read_verify("mac set appkey {0}".format(appkey), "ok")
        self._ser_write_read_verify("mac set appeui {0}".format(appeui), "ok")
        self._ser_write_read_verify("mac save", "ok")
        self.wan_mode = "otaa"
        return(hweui)

    def config_abp(self, nwskey=None, appskey=None, devaddr=None):
        ''' Configure for ABP join '''
        self._ser_write_read_verify("mac set adr on", "ok")

        self._ser_write_read_verify("mac set nwkskey {0}".format(nwskey), "ok")
        self._ser_write_read_verify("mac set appskey {0}".format(appskey), "ok")
        self._ser_write_read_verify("mac set devaddr {0}".format(devaddr), "ok")
        self._ser_write_read_verify("mac save", "ok")
        self.wan_mode = "abp"

    def join(self):
        self._ser_write_read_verify("mac resume", "ok")
        self._ser_write_read_verify("mac join {}".format(self.wan_mode), "ok")
        res = self._parent_ser.readline()
        while not res:
            res = self._parent_ser.readline()
        res = res.strip().decode()
        if res != 'accepted':
            raise JoinError("unable to join LoRaWAN: {}".format(res))
        if self._debug:
            print("Joined LoRa WAN")

    def send_uplink(self, data, type="uncnf", portno=4):
        '''Send LoRaWan uplink'''
        if self._debug:
            print("LoRaWan:send : Sending...")
    
        self._ser_write_read_verify("mac resume", "ok")
        try:
            cmd = "mac tx {} {} {}".format(type, portno, data)
            self._ser_write_read_verify(cmd, "ok", max_retries=5, retry_delay=1)
    
            # read out transmission verification
            while True:
                ret = self._ser_read()
                ret_stripped = ret.strip()
                if ret_stripped == "mac_tx_ok":
                    if self._debug:
                        print("LoRaWan:send : mac_tx_ok received.")
                    break
                elif ret_stripped == 'busy':
                    # Wait and retry
                    if self._debug:
                        print("Module busy, waiting for completion...")
                    time.sleep(1)
                    continue
                else:
                    raise lora.TransmissionError(f"Unexpected response after mac tx: {ret_stripped}")
        finally:
            self._ser_write_read_verify("mac pause")
    
    def mac_pause(self):
        '''Pauses the MAC to allow radio operations'''
        self._ser_write_read_verify("mac pause")
        self.mode = 'radio'

    def mac_resume(self):
        '''Resumes the MAC to allow LoRaWAN operations'''
        # Stop any ongoing radio receive operation
        self._ser_write_read_verify("radio rxstop", "ok")
        time.sleep(0.1)
        self._ser_write_read_verify("mac resume", "ok")
        self.mode = 'mac'

    def radio_set_mode(self, modulation):
        '''Sets the radio mode (e.g., 'lora')'''
        self._ser_write_read_verify(f"radio set mod {modulation}", "ok")

    def radio_receive(self):
        '''Starts receiving in radio mode'''
        self._ser_write_read_verify("radio rx 0", "ok")

