'''
/** Original work Copyright 2024 damiano IU3QGD
 * https://to-be-defined
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.

 * Addition to the licence: NO company with more than 50employees can use this SW and you are NOT entitled to store this software 
 * on ANY storage that in some way attempts to monetize it (eg: github)
 * corporations and banks are too rich and too powerful, they MUST be split and ALL the money given back to the state

should put all chunk id here
ok, so, the following are the chunk id on the SERIAL datachannel
they are UNIQUE to IPP since there is a magic work at the beginning to differentiate from quansheng
also, NOTHING is sent on the serial unless polled 

An issue is to simplify the logic
What I wish is for radio stream to be directly txable to serial port, meaning with as little work as possible
the radio stream should have the usual two bytes len at the beginning and a series of chunks
to be sendable UP to router the chunks MUST be IPP chunks

therefore.... the chunks on radio MUST conform to IPP
therefore the "messanger" in quansheng MUST conform to IPP chunk, under a "special" protocol
meaning chunkid special protocol and subtype whatever....

'''

from __future__ import annotations

import struct

from glob_class import IppChunk
import glob_eeprom
import ipp_parser
import qpystat
from uvk5_lib import RadioReceiveRes, QS_read_EE_Parser, Qswio


IPPU_scan_loudest_reqid=4

IPPU_sendbuf_reqid=6

IPPU_senddtmf16_reqid=11

IPPU_radiocfg_reqid=14
IPPU_radiocfg_resid=16

IPPU_pollpending_reqid=18

IPPU_pollprintf_resid=20

IPPU_polldtmf16_resid=23

IPPU_pollradiostat_resid=24
IPPU_pollqsmsg_resid=26

IPPU_ident_reqid=28
IPPU_ident_resid=30

IPPU_generic_resid=32

IPPU_res_pollipp16=35

IPPU_systemcmd_reqid=36

IPPU_scan_loudest_resid=38

IPPU_res_counters16=41

IPPU_req_spectrum=42
IPPU_res_spectrum16=43

IPPU_req_eeprom=44
IPPU_res_eeprom=46

IPPU_req_rbands_set=45

IPPU_res_screen_bmap=52

RMSG_IPP_GENERIC_ID8=0x20    # CANNOT CHANGE, it is a public interface






# ===================================================================================
# you can "decode" into this one
'''
struct IPPU_radio_status_res
   {
   enum IPPU_cid chunk_id;  // IPPU_radiostat_resid
   uint8_t  chunk_len;
   uint16_t rx_rssi;
   uint32_t rx_freq_1x;   // current RX frequency
   uint32_t tx_freq_1x;   // current TX frequency
   uint16_t rx_noise;
   uint16_t rx_glitch;
   uint16_t batteryV_2dec;   // it is reported as a 2 dec fixed
   uint8_t  tx_power;        // current TX power
   uint8_t  padding;
   union Rstate radio_status;
   uint16_t tc_cpu_2dec;
   };
'''
class Qsk5_res_RadioStatus(IppChunk):

    # -------------------------------------------------------------------------------
    # note that the given buffer INCLUDES the id and len
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        rx_rssi,rxfreq,txfreq,rx_noise,rx_glitch,batteryV_2dec,txpwr,_a,radio_status,tc_cpu_2dec=struct.unpack_from('<HIIHHHBBHH',self.rxbuffer,2)

        self.rx_rssi=rx_rssi
        self.rxfreq=rxfreq
        self.txfreq=txfreq
        self.rx_noise=rx_noise
        self.rx_glitch=rx_glitch
        self.batteryV_2dec=batteryV_2dec
        self.batteryV = float(batteryV_2dec)/100.0
        self.txpwr=txpwr
        self.radio_status=radio_status
        self.tc_cpu_2dec=tc_cpu_2dec;
        self.tc_cpu=float(tc_cpu_2dec)/100.0;
        


# ===================================================================================
# you can "decode" into this one
'''
enum IPPU_cid chunk_id;   // IPPU_ident_resid
uint8_t chunk_len;        // sizeof(struct IPPU_ident_res)
char hw_model[12];        // UV-K5
char hw_version[12];      // possibly empty
char sw_model[12];        // damiano
char sw_version[14];      // 2024071711
uint32_t hw_cpuid[4];     // as taken from CPU
'''
class Qsk5_res_RadioIdentity(IppChunk):

    # -------------------------------------------------------------------------------
    # note that the given buffer INCLUDES the id and len
    # will return an array of elements as indicated in the decode
    # NOTE that it all begins at 2
    # NOTE that 4L result in four longs
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        # AHHHHHHHH, can whoever invented python go to HELL
        # you NEED the < at the beginning, otherwise it will just get the NUMBER of bytes to convert wrong
        # yes, the NUMBER OF BYTES, I know, the value is fair game, but the NUMBER of bytes ? idiots
        hw_model,hw_version,sw_model,sw_version,cpu_id=struct.unpack_from('<12s12s12s14s16s',self.rxbuffer,2)
        
        self.hw_model=hw_model.decode('ascii','ignore')
        self.hw_version=hw_version.decode('ascii','ignore')
        self.sw_model=sw_model.decode('ascii','ignore')
        a_version=sw_version.decode('ascii','ignore')

        # this actually works to remove trailing 0, split the string at first zero         
        self.sw_version=a_version.split('\x00', 1)[0]
        
        # the magic of stripping trailing zeros is totally unreliable
        # actually, it fails a_v = sw_version.rstrip(b'\x00')
        
        # 01020302 0c534a33 4d43ff14 28007f00

        self.cpu_id=cpu_id

    # --------------------------------------------------
    def getCpuid_hex(self):
        return self.cpu_id.hex()
        
    # --------------------------------------------------
    def __str__(self):
        return self.hw_model+" "+self.sw_version


# ===================================================================================
'''
struct IPPU_radiocfg_res
    {
    enum IPPU_cid chunk_id;      // IPPU_radiocfg_resid
    uint8_t  chunk_len;
    uint16_t pad_even;
    uint32_t rx_freq_1x;       // receive on this f, zero means leave it alone _1x means that you need to mult by 10
    uint32_t tx_freq_1x;       // send on this f, zero means leave it alone _1x means that you need to mult by 10
    uint8_t  tx_power;           // 0 means do not touch config, FF max power, intermediate numbers as appropriate
    uint8_t  pad2[3];
    };
'''
class Qsk5_res_RadioConfiguration(IppChunk):

    # -------------------------------------------------------------------------------
    # note that the given buffer INCLUDES the id and len
    # will return an array of elements as indicated in the decode
    # NOTE that it all begins at 2
    # NOTE that 4L result in four longs
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        rxfreq_1x,txfreq_1x,txpower,_,_,_=struct.unpack_from('<xxIIBBBB',self.rxbuffer,2)

        self.rxfreq_1x=rxfreq_1x
        self.txfreq_1x=txfreq_1x
        self.txpower=txpower
        
    # --------------------------------------------------
    def __str__(self):
        return str(self.rxfreq_1x)


# ==================================================================================
'''
struct IPPU_generic_res
    {
    enum IPPU_cid chunk_id;       // IPPU_error_resid
    uint8_t chunk_len;            // sizeof(myself)
    uint16_t from_reqid;          // the original request this response is bound to
    uint16_t reason;              // some sort of reason for the message
    uint16_t rescode;             // some sort of error code
    char     message[40];         // a short message
    };
'''
class Qsk5_res_Generic(IppChunk):

    # --------------------------------------------------
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        from_reqid,reason,rescode,message=struct.unpack_from('<HHH40s',self.rxbuffer,2)

        self.from_reqid=from_reqid
        self.reason=reason
        self.rescode=rescode
        self.message=message.decode('ascii','replace')
        
    # --------------------------------------------------
    def __str__(self):
        return str(self.from_reqid)+" "+str(self.reason)+" "+str(self.rescode)+" "+self.message



# ==================================================================================
class Qsk5_res_pollipp16(IppChunk):
    '''
    struct IPPU_res_pollipp
       {
       struct IPPU_header16 header;  // IPPU_pollipp16_resid
       uint8_t  isIpp;               // if true then the payload is an IPP
       uint8_t  barker_errcounter;   // Could be a barker error counter ?
       uint8_t  freeA;               // possible future use
       uint8_t  freeB;               // possible future use
       uint8_t  payload[0];          // I should map this into the send buffer
       };
    '''

    # --------------------------------------------------
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        isIpp,barker_errcounter=struct.unpack_from('<?B',self.rxbuffer,4)

        self.isIpp=isIpp
        self.barker_errcounter=barker_errcounter
        self.ipp_packet=self.rxbuffer[8:]
        
    # --------------------------------------------------
    def __str__(self) -> str:
        ipp_pklen=len(self.ipp_packet)
        return 'Qsk5_res_pollipp16: '+str(self.isIpp)+" "+str(ipp_pklen)




# ==================================================================================
'''
struct IPPU_scan_loudest_res
    {
    enum IPPU_cid chunk_id;      // IPPU_scan_loudest_resid
    uint8_t  chunk_len;
    uint16_t pad_even;
    uint32_t rx_freq_1x;         // the found frequency
    // in future time I could return further info on CxCSS
    };
'''
class Qsk5_res_Loudest(IppChunk):

    # --------------------------------------------------
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        # the xx is to skip some padding bytes
        found_F_1x=struct.unpack_from('<xxI',self.rxbuffer,2)

        # because, when you have a single result, what is returned is an array of elements, in any case
        self.found_F_1x=found_F_1x[0]
        
    # --------------------------------------------------
    def __str__(self):
        return str(self.found_F_1x)

# ==================================================================================
# need to look at 'c'code to know the structure
class Qsk5_res_Counters(IppChunk):

    # --------------------------------------------------
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        a_touple=struct.unpack_from('<12s',self.rxbuffer,4)
        
        self.sw_version=a_touple[0].decode('ascii','ignore')

        # the actual c structure is here
        self.c_struct = self.rxbuffer[16:]

        
    # --------------------------------------------------
    def __str__(self):
        return str(self.sw_version)

# =====================================================================
class Qsk5_res_Spectrum(IppChunk):
    '''
    struct IPPU_res_spectrum
       {
        struct IPPU_header16 header;  // IPPU_res_spectrum
       uint32_t start_freq_1x;       // the starting frequency
       uint32_t step_freq_1x;        // the step can be HUGE, but should really not be
       uint16_t samples_count;       // this is the number of steps (samples) to do
       uint16_t sample_time_cs;      // the time to take for a sample in cs, so you can do 100 samples per second, max
       uint8_t  samples[IPPU_SSAMPLES_MAX]; // from here onward there are the samples
       };
    '''

    # --------------------------------------------------
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        start_freq_1x, step_freq_1x, samples_count, sample_time_cs, samples=struct.unpack_from('<IIHH256s',self.rxbuffer,4)

        self.start_freq_1x=start_freq_1x
        self.step_freq_1x=step_freq_1x
        self.samples_count=samples_count
        self.sample_time_cs=sample_time_cs
        self.samples=samples

        
    # --------------------------------------------------
    def __str__(self):
        return str(self.start_freq_1x)



# =====================================================================

class Qsk5_res_Eeprom(IppChunk):
    '''
    struct IPPU_res_eeprom
       {
       struct IPPU_header8 header;  // IPPU_res_eeprom
       enum IPPU_EE_req_e ee_req;   // what request is it ?
       uint8_t  ee_data_len;        // the actual number of bytes, can be zero
       uint16_t ee_rw_address;      // read or write here
       uint16_t ee_rescode;         // response code to the request
       uint16_t padding;            // free for possible uses
       uint8_t  ee_data[128];       // max buffer of 128 bytes
       };
    
    '''
    # --------------------------------------------------
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        ee_req,ee_data_len,ee_rw_address,ee_rescode,_=struct.unpack_from('<BBHHH',self.rxbuffer,2)
        
        self.ee_req=ee_req
        self.ee_data_len=ee_data_len
        self.ee_rw_address=ee_rw_address
        self.ee_rescode_=ee_rescode

        self.ablock : glob_eeprom.EEblock

        if self.ee_data_len > 0:
            b_index = int(self.ee_rw_address / glob_eeprom.EEblock.block_bytes_len)
            
            self.ablock = glob_eeprom.EEblock(b_index, self.rxbuffer[10:])
            
    
    # --------------------------------------------------
    # @return True if this chunk is a read response
    def isReadResponse(self) -> bool:
        return self.ee_req == 1
        
    # --------------------------------------------------
    # @return True if this chunk is a write response
    def isWriteResponse(self) -> bool:
        return self.ee_req == 2

    # --------------------------------------------------
    def __str__(self):
        return 'Qsk5_res_Eeprom s_address='+str(self.ee_rw_address)
























# ===================================================================================
# this is given the chunk, starting from the chunk id
# note that the parsing unpack the from uid and the to_uid
# NOTE the "ACK" message is on the SAME packet being sent, it has just the ack pit set
# See RMSG_RX_parse_ack in the c source
class Qsk5_res_PollMessages(IppChunk):

    # -------------------------------------------------------------------------------
    # note that the given buffer INCLUDES the id and len
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        # TODO have the radio actually send the msg_id to storage
        _flags,from_uid,to_uid,msg_id = struct.unpack_from('<BBBB',self.rxbuffer,2)
        
        self.is_rx  = _flags & 0x01
        self.is_tx  = _flags & 0x02
        self.is_ack = _flags & 0x04
        self.is_log = _flags & 0x10
        
        self.from_uid=from_uid
        self.to_uid=to_uid
        self.msg_id=msg_id
        
        # starts on third byte
        self.messagestring = self.rxbuffer[6:].decode('ascii','replace') 

    # -----------------------------------------------------------------------
    # NOTE that the order of IF is relevant
    def _get_StatusString(self):
        if self.is_ack:
            return 'ACK \u25C4'
        
        if self.is_tx:
            return 'TX <'

        if self.is_rx:
            return 'RX >'
        
        if self.is_log:
            return 'LOG'

        return "?"

    # --------------------------------------------------
    def __str__(self):
        return self._get_StatusString()+" "+str(self.from_uid)+" "+str(self.to_uid)+" "+self.messagestring

# ===================================================================================
# this is given the chunk, starting from the chunk id
class Qsk5_res_RadioPrintf(IppChunk):

    # -------------------------------------------------------------------------------
    # standard constructor
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        astring = self.rxbuffer[3:].decode('ascii','replace')
        
        self.print_this=astring

# ===================================================================================
'''
struct IPPU_dtmf_poll_res
    {
    enum IPPU_cid chunk_id;      // IPPU_polldtmf_resid
    uint8_t  chunk_len;          // whole chunk length
    uint8_t  isIpp;              // if true then the payload is an IPP
    uint8_t  align_even;         // Align even
    uint8_t  payload[0];         // I should map this into the send buffer
    };
'''
class Qsk5_res_PollDtmf(IppChunk):

    # -------------------------------------------------------------------------------
    # note that the given buffer INCLUDES the id and len
    # will return an array of elements as indicated in the decode
    # NOTE that it all begins at 2
    # NOTE that 4L result in four longs
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        # I know it is a chunk16
        is_ipp,_=struct.unpack_from('<BB',self.rxbuffer,4)
        
        self.is_ipp=is_ipp
        
        # use this if it is an ipp packet
        self.dtmf_bytes : bytearray = bytearray(self.rxbuffer[6:])

        if self.is_ipp:
            self.dtmf_string = self.dtmf_bytes.hex()
        else:
            self.dtmf_string = self.dtmf_bytes.decode('ascii','replace')
        
    # --------------------------------------------------
    def __str__(self):
        return str(self.chunk_id)+' '+str(self.is_ipp)




# ==================================================================================
# Only ONE line is given, the one that has changed
class Qsk5_res_screen_bmap(IppChunk):
    '''
    #define LCD_X_BLEN         128   // the bytes length in X, each char is made of about 7 bytes
    #define LCD_Y_BLEN         8     // the bytes length in Y
    
    struct IPPU_res_screen
       {
       struct IPPU_header8 header;    // IPPU_res_screen_bmap
       uint8_t line_idx;              // the line index being transferred, 0 to 7
       uint8_t pad_even;
       uint8_t frameBuffer[LCD_X_BLEN];  // hold a line in the native format
       };
    '''

    bits_rows_count = 8         # 64
    bits_columns_count = 8      # 128

    # --------------------------------------------------
    # IPP header for 16 bits chunks is 4 bytes
    # plus two bytes extra status
    def __init__(self,chunk : IppChunk ):
        IppChunk.__init__(self,chunk.rxbuffer)

        line_idx,_pad=struct.unpack_from('<BB',self.rxbuffer,2)
        
        self.line_idx=line_idx 

        # this is the actual frame buffer        
        self.frame_buffer=self.rxbuffer[4:]
                
    # --------------------------------------------------
    def __str__(self) -> str:
        return 'Qsk5_res_screen_bmap: clen '+str(self.chunk_len)+' line_idx '+str(self.line_idx)





# ===================================================================================
# commands to be executed will be sent to the poller using this as ancestor
# I need this since the serial channel is NON sharable
# well, not really, I could have a lock on an IPP request ....
# so, the whole "queue" issue disappear BUT you still need a separate thread, otherwise you freeze the GUI....
# worth it ? not really, since you would need a runner thread
# unless you create a runner thread each "request"..... I suppose a gen Z would do this.....
# Also, I am tempted to merge the GUI in here, but it will become a blob of code that becomes confusing 
# since this is just a data transfer...

# COMMANDS CANNOT share the quansheng protocol, so, you detect the firmware upload ONLY on normal polling !

class Qsk5_command:
    
    # -------------------------------------------------------------------------------
    # standard constructor requires the command identifier that is later used, if needed
    def __init__(self,command):
        self.command=command


    # -------------------------------------------------------------------------------
    # This is the origianl methond
    # Subclasses must implement this to send the request 
    # what it should do is to send a request using the given library 
    # NOTE: this method does NOTHING, meaning, it does not send anything !
    def send_request(self, qswio : Qswio ):
        pass

        
    # -------------------------------------------------------------------------------
    # This is the default methond to send a request, BUT a subclass MAY implement something different
    # this is, if the default behaviour is not approprite, Eg when dealing with EEPROM
    # note that I make use of the standard dispatcher of qconnect
    def send_request_new(self, stat : qpystat.Qpystat, qswio : Qswio ):
        
        self.send_request(qswio)
        
        r_res : RadioReceiveRes = RadioReceiveRes(qswio)
        
        if r_res.isIppRes():
            parser = ipp_parser.Ippparser(r_res.pk_data)
            stat.qconnect.parseReceivedChunks(parser)


    # --------------------------------------------------
    # this is the equivalent of toString()
    # if you wish more details, override it into subclasses
    def __str__(self):
        return 'Qsk5_command: '+str(self.command)
        
        














# ===================================================================================
# A command to read write eeprom
# his is used even to write or read big amount of data
class Qsk5_req_write_eeprom(Qsk5_command):
    '''
    struct IPPU_req_eeprom
       {
       struct IPPU_header8 header;  // IPPU_req_eeprom
       enum IPPU_EE_req_e ee_req;   // what request is it ?
       uint8_t  ee_data_len;        // the actual number of bytes to read or write
       uint16_t ee_rw_address;      // read or write here
       uint16_t ee_read_pw;         // if required this is a possible read password
       uint16_t ee_write_pw;        // if required this is a possible write password
       uint8_t  ee_data[IPPU_EPPROM_DATALEN];   // max buffer of 128 bytes
       };
    '''
    # -------------------------------------------------------------------------------
    # If we have EE data then this is a write, otherwise it is a read
    def __init__(self, ee_address : int, ee_len, ee_data : bytearray ):
        Qsk5_command.__init__(self, 'EEPROM write')
        
        self.s_address = ee_address
        self.ee_len  = ee_len;
        self.ee_data = ee_data

    # --------------------------------------------------
    # this is the equivalent of toString()
    def __str__(self):
        return self.command+' '+str(self.s_address)

    # ---------------------------------------------------------------------------------------
    # See c header, this sends the request, the reply will be parsed
    def send_request(self, qswio : Qswio ):

        if  self.ee_data and len(self.ee_data) > 0:
            s_chunk = struct.pack('<BBHHH',2,self.ee_len,self.s_address,0,0)+self.ee_data
            #print("EE write len ",self.ee_len)
            qswio.IPP_send_chunk(IPPU_req_eeprom,s_chunk)
    
    
    

# ===============================================================================
# I have split the read from the write since it behaves differently
# a read can span easily multiple EEchunk and be odd
# a write is fussy, and cannot be more than 16 bytes (to be safe)
# Also, since the EEPROM is divided into blocks of 16 bytes, I have to read THE FULL block
# so, I always read 16 bytes and the parameter is the number of blocks to read
class Qsk5_req_read_eeprom(Qsk5_command):
    '''
    struct IPPU_req_eeprom
       {
       struct IPPU_header8 header;  // IPPU_req_eeprom
       enum IPPU_EE_req_e ee_req;   // what request is it ?
       uint8_t  ee_data_len;        // the actual number of bytes to read or write
       uint16_t ee_rw_address;      // read or write here
       uint16_t ee_read_pw;         // if required this is a possible read password
       uint16_t ee_write_pw;        // if required this is a possible write password
       uint8_t  ee_data[IPPU_EPPROM_DATALEN];   // max buffer of 128 bytes
       };
    '''
    # -------------------------------------------------------------------------------
    # If we have EE data then this is a write, otherwise it is a read
    def __init__(self, s_address : int, w_block_count ):
        Qsk5_command.__init__(self, 'EEPROM read')
        
        self.s_address = s_address
        self.c_address = s_address  # by default the current address is the same as the start
        
        self.w_block_count = w_block_count;   # the number of blocks to received

    # --------------------------------------------------
    # this is the equivalent of toString()
    def __str__(self):
        return self.command+' '+str(self.s_address)

    # ---------------------------------------------------------------------------------------
    # See c header, this is a read of a EEblock
    def send_request(self, qswio : Qswio ):
        s_chunk = struct.pack('<BBHHH',1,glob_eeprom.GlobEeprom.ee_block_len,self.c_address,0,0)
        qswio.IPP_send_chunk(IPPU_req_eeprom,s_chunk)

    # ----------------------------------------------------------------------------------------
    # Override the original to read multiple blocks
    def send_request_new(self, stat : qpystat.Qpystat, qswio : Qswio ):
        
        have_received_data = False
        
        for block_index in range(self.w_block_count):
            self.send_request(qswio)
            
            r_res : RadioReceiveRes = RadioReceiveRes(qswio)

            if not r_res.isIppRes():
                stat.app_println("NOT an ipp packet !!!")
                return 
            
            parser=ipp_parser.Ippparser(r_res.pk_data)
            
            if stat.qconnect.parseReceivedChunks(parser):
                self.c_address += glob_eeprom.GlobEeprom.ee_block_len
                stat.eeprom_gui.setLoadingProgress(block_index)
                have_received_data=True
            
            
        if have_received_data:    
            stat.eeprom_gui.signalEEpromLoaded()    
            











# --------------------------------------------------------------------------------------
# So, you queued wuite a few commands to the radio and then you wish something to happen to the local GUI
# when the commands have been processed
# Insert this command and do something in the send_request_new

class Qsk5_req_GUI_command(Qsk5_command):
    
    # -------------------------------------------------------------------------------
    # standard constructor requires the command identifier that is later used, if needed
    def __init__(self, want_command : str  ):
        Qsk5_command.__init__(self, 'GUI command')

        self.want_command=want_command
        
    # -------------------------------------------------------------------------------
    # This is the default methond to send a request, BUT a subclass MAY implement something different
    # this is, if the default behaviour is not approprite, Eg when dealing with EEPROM
    # note that I make use of the standard dispatcher of qconnect
    def send_request_new(self, stat : qpystat.Qpystat, qswio : Qswio ):

        stat.eeprom_gui.setLoadingProgress(0)        









# ===============================================================================
# It may happen that you wish to send a poll request outside the normal poll periodic
class Qsk5_req_radio_poll(Qsk5_command):

    '''
       uint16_t w_printf:1;        // bit0 poll for printf
       uint16_t w_ipp:1;           // bit1 poll for IPP packets
       uint16_t w_dtmfipp:1;    // bit2 poll for DTMF IPP packets
       uint16_t w_counters:1;  // bit3 poll for counters
       uint16_t w_dtmfrxtx:1;  // bit4 poll for DTMF
       uint16_t w_qsmsg:1;     // bit5 poll for Quansheng messages
       uint16_t w_scanloud:1;  // bit6 poll for scanloud result
       uint16_t padding:9;     // MSB on LE CPU
    '''

    # -------------------------------------------------------------------------------
    # There is just one uint16 that has a bitmask with the request
    def __init__(self, w_mask : int ):
        Qsk5_command.__init__(self, 'Generic radio poll')
        
        self.w_mask = w_mask

    # --------------------------------------------------
    # this is the equivalent of toString()
    def __str__(self):
        return self.command+' '+str(self.w_mask)

    # ---------------------------------------------------------------------------------------
    # The reply will be on the generic incoming flow 
    def send_request(self, qswio : Qswio ):
        s_chunk = struct.pack('<H',self.w_mask)
        qswio.IPP_send_chunk(IPPU_pollpending_reqid,s_chunk)





# ===============================================================================
# this is like the standard one BUT read using the quansheng protocol
# this will always read 16 bytes, like the other one
class Qsk5_req_read_eeprom_quansheng(Qsk5_command):

    # -------------------------------------------------------------------------------
    # @param s_address start address
    # @param w_block_count number of 16 bytes blocks to read
    def __init__(self, s_address : int, w_block_count ):
        Qsk5_command.__init__(self, 'EEPROM read Quansheng')
        
        self.s_address = s_address
        self.c_address = s_address  # by default the current address is the same as the start
        
        self.w_block_count = w_block_count;   # the number of blocks to received

    # --------------------------------------------------
    # this is the equivalent of toString()
    def __str__(self):
        return self.command+' '+str(self.s_address)

    # ---------------------------------------------------------------------------------------
    # Need to wrap a read into quansheng protocol
    def send_request(self, qswio : Qswio ):
        qswio.qua_read_EEPROM(self.c_address, 16) 

    # ----------------------------------------------------------------------------------------
    # can be used ONLY after calling send_Request_new
    def _println(self, msg : str ):
        self._stat.qconnect._println(msg)
        
    # ----------------------------------------------------------------------------------------
    # Override the original to read multiple blocks
    def send_request_new(self, stat : qpystat.Qpystat, qswio : Qswio ):
        
        self._stat = stat
        
        have_received_data = False
        
        # quansheng wants this, first
        qswio.qua_get_fw_version()

        r_res : RadioReceiveRes = RadioReceiveRes(qswio)

        if not r_res.isQuanshengRes():
            self._println("NOT A quansheng res")
            return 
        
        for block_index in range(self.w_block_count):
            self.send_request(qswio)
            
            r_res = RadioReceiveRes(qswio)

            if not r_res.isQuanshengRes():
                self._println("NOT B quansheng res")
                return 
            
            if r_res.error_message:
                self._println('read EE '+r_res.error_message)
                return
            
            parsed = QS_read_EE_Parser(r_res.pk_data)
            
            if stat.globeeprom.eepromUpdateFromRadioQuansheng(parsed):
                self.c_address += glob_eeprom.GlobEeprom.ee_block_len
                stat.eeprom_gui.setLoadingProgress(block_index)
                have_received_data=True
            
            
        if have_received_data:    
            stat.eeprom_gui.signalEEpromLoaded()    
            



