'''
/** 
 * 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


I need a global holder of the eeprom, so I can ask for blocks
Eeprom is divided in blocks of 16 bytes, it is best to keep it like this

'''
# -------------------------- NOTES
# See https://realpython.com/iterate-through-dictionary-python/
# they are global to make it ieasy to write....

from __future__ import annotations

import struct
from tkinter import ttk
import typing

from app_csv import Csv_Writer, CsvTable
from glob_eeprom import EEblock
from glob_gui import JtkTableColdef, EE_events_listener, JtkTable
import qpystat


fmtcol_idx="fmcol_idx"
fmtcol_band="fmcol_band"
fmtcol_chipchan="fmcol_chipchan"
fmtcol_freq="fmcol_freq"

    
# -------------------------------------------------------
# Generic way to define a map from a column name to some properties        
fmtcols_map = { 
    fmtcol_idx      : JtkTableColdef(fmtcol_idx,'Nr.', True, 50), 
    fmtcol_band     : JtkTableColdef(fmtcol_band,"Band", True, 50),
    fmtcol_chipchan : JtkTableColdef(fmtcol_chipchan,"C Channel", True, 80),
    fmtcol_freq     : JtkTableColdef(fmtcol_freq,"Frequency", False, 100),
    }

'''
struct FM_EE_chan_t
    {
    uint16_t fmchip_chan_po :10;    // reg03 there are 10 bits from 0 to 1023 included, but 0 is NULL !
    uint16_t fmchip_space   :2;     // reg05 bits 4:5
    uint16_t fmchip_band    :2;     // reg05 bits 6:7
    uint16_t free           :2;     // MSB on LE CPU
    }    
'''


# ===================================================================
# It is safer to deal with objects instead of a generic map
class FmradioRow:

    csv_heading=['ee_index'   ,'chip_band',    'chip_channel',    'FM_frequency']
    
    def getBandFreqBase(self):
        if self.fmr_band == 0:
            return 875
        elif self.fmr_band == 1:
            return 760
        elif self.fmr_band == 2:
            return 760
        elif self.fmr_band == 3:
            return 640
        else:
            return 0
    
    
    # ----------------------------------------------------------------
    # I just need to create the iid
    # the row is always shown, with zero values
    def __init__(self, index : int ):
        self.fmr_idx=index
        
        self.fmr_band = 0;
        self.fmr_chipchan=0
        self.fmr_freq=''
            
    # ------------------------------------------------------------------            
    def updateFromEeprom(self, raw_val : int ):
        
        self.fmr_band = (raw_val >> 12) & 0b11
        
        chipchan_po : int =(raw_val & 0b1111111111)
        
        if chipchan_po == 0 or chipchan_po == 0b1111111111:
            self.fmr_chipchan=0
            self.fmr_freq=''
        else:
            self.fmr_chipchan=chipchan_po -1
            fmbase=self.getBandFreqBase()
            
            fmr_freq=fmbase+self.fmr_chipchan
            
            self.fmr_freq = "%d.%d Mhz" % (fmr_freq/10, fmr_freq%10)
            
    # -----------------------------------------------------------------
    # must return the values as a list of strings in the same order as the header
    def csv_get_values(self) -> typing.List[str]:
        
        risul : typing.List[str] = []
        
        risul.append(str(self.fmr_idx))
        risul.append(str(self.fmr_band))
        risul.append(str(self.fmr_chipchan))
        risul.append(str(self.fmr_freq))
        
        return risul
        
            
    # -----------------------------------------------------------------
    # return the list of values to show into the table, the GUI table
    # NOTE that the order has to be consistent with the GUI definition
    def getValuesForTreeview(self):
        risul = (
            self.fmr_idx+1,
            self.fmr_band,
            self.fmr_chipchan,
            self.fmr_freq
            )
            
        return risul         
    
    
# ========================================================================
# Let me try to have some decent OO wrapping in this shitty language
class Fmradio_list(list[FmradioRow]):

    # -----------------------------------------------------------------
    # I need to preallocate list and never deallocate them
    # NOTE that squelch goes from zero to 9, inclusive
    def __init__(self):
        
        for r_idx in range(24):
            self.append(FmradioRow(r_idx))


    # -----------------------------------------------------------------
    # this will receive more than one set of data
    def setListFromEeprom (self, s_idx : int, byte_array : bytearray):
        
        if not byte_array:
            return 
        
        short_array = struct.unpack_from('<8H',byte_array)
        
        for index,raw_val in enumerate(short_array):
            row : FmradioRow = self[s_idx+int(index)]
            row.updateFromEeprom(raw_val)
    



# =====================================================================
# the table with the list of FM channels
# It should take the data from the global EEPROM repository
# It will be signalled when new EEPROM data is received
class QFMtable_gui (EE_events_listener,CsvTable):


    # ----------------------------------------------------------------
    def __init__(self, stat : qpystat.Qpystat, parent_frame : ttk.Frame ):
        self._stat = stat
        
        # THIS panel will be regulated by a grid layout, from parent
        self.work_panel=ttk.Frame(parent_frame)

        self._fm_table = JtkTable(self.work_panel,fmtcols_map )
        self._fm_table.getComponent().pack(fill='both', expand=True)
        
        self._newClearTablePanel(self.work_panel).pack(fill='x')
        
        self._fmradiolist : Fmradio_list = Fmradio_list()
        
        self._stat.appcsv.add_to_tablesList(self)

    # ----------------------------------------------------------
    def _newClearTablePanel(self, parent_panel : ttk.Frame ) -> ttk.Frame:
        
        a_panel=ttk.Frame(parent_panel,padding=3)
        
        ttk.Button(a_panel, text='Clear Channels', command=self._clearFmChannels).pack()
        
        return a_panel
        
        
    # ----------------------------------------------------------
    def _clearFmChannels(self):

        a_block : EEblock = self._stat.globeeprom.get_block_from_address(0xE40)
        a_block.clearBytearray(0)
        
        a_block = self._stat.globeeprom.get_block_from_address(0xE50)
        a_block.clearBytearray(0)
        
        a_block = self._stat.globeeprom.get_block_from_address(0xE60)
        a_block.clearBytearray(0)
        
        self.updateGuiFromEeprom()
        
    # ----------------------------------------------------------
    def tkraise(self):
        self.work_panel.tkraise()

    # ----------------------------------------------------------
    # override CSV
    def csv_tableName(self) -> str:
        return 'radio_FM_channels'
    
    # ----------------------------------------------------------
    # override CSV
    def csv_write_header(self, a_writer : Csv_Writer):
        a_writer.writerow(FmradioRow.csv_heading)

    # ---------------------------------------------------------
    # in a different thread than swing, write the table to this csv writer
    def csv_write_table(self, a_writer : Csv_Writer):
        
        for row in self._fmradiolist:
            v_list = row.csv_get_values()
            a_writer.writerow(v_list)
        
    # ------------------------------------------------------------------------
    # there are three blocks to parse
    def updateGuiFromEeprom(self):
        
        a_block : EEblock = self._stat.globeeprom.get_block_from_address(0xE40)
        self._fmradiolist.setListFromEeprom(0,a_block.ee_bytearray)
        
        a_block = self._stat.globeeprom.get_block_from_address(0xE50)
        self._fmradiolist.setListFromEeprom(8,a_block.ee_bytearray)
        
        a_block = self._stat.globeeprom.get_block_from_address(0xE60)
        self._fmradiolist.setListFromEeprom(16,a_block.ee_bytearray)
        
        self.GUI_refresh()


    # -------------------------------------------------------------------
    # At any time it should be possible to filter the entries and show them
    # Note that this may be called at the beginning , when there is nothing in the list...
    def GUI_refresh(self ):    
        
        for e_row in self._fmradiolist:
            if not self._fm_table.row_exist(e_row.fmr_idx):
                self._fm_table.row_append(e_row.fmr_idx,e_row.getValuesForTreeview())
            else:
                self._fm_table.set_row(e_row.fmr_idx,e_row.getValuesForTreeview(),'')

    # -----------------------------------------------------------------
    # There is no write, at the moment
    def update_EEblock_from_GUI(self):
        pass
        




