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

'''
# =================================================================================
# the idea is that this produces a Frame with all the possible commands in it
# Since python need the parent when building the gui, this must be provided
# Remember that the caller is responsible to layout this one

from __future__ import annotations

from queue import Queue
from threading import Thread
import threading
from tkinter import  IntVar, StringVar
import typing

from glob_fun import string_to_int_hex
from glob_gui import LogPanel, JtkPanelPackLeft, JtkLabel, TV_Entry, JtkCombo
from ipp_parser import Qsk5_req_identity, Qsk5_req_set_radio_params, \
    Qsk5_req_syscmd, IPPU_SYSCMD_reset
import qpystat
import tkinter.ttk as ttk


class AppMainGui(ttk.Frame):

    # -----------------------------------------------------------------------------
    # Constructor, note that this component layout is done by the caller
    def __init__(self, stat : qpystat.Qpystat, parent_panel ):
        ttk.Frame.__init__(self, parent_panel, padding=3) # borderwidth=3, relief='ridge')     
        
        self.stat=stat
    
        self._newSubButtonsPanel(self).pack(fill='x')
        self._newCommandButtonsPanel(self).pack(fill='x')
        self._newSpecialReqPanel(self).pack(fill='x')
#        self._newConfigRadioPanel(self).pack(fill='x')
        
        self._appmain_log = LogPanel(self,"Commands Log")
        self._appmain_log.pack(fill='both',expand=True)

        
#        sys.stderr = StderrRedirector(self._appmain_log) 
        
        self._println("AppMainGui init complete")

    # -------------------------------------------------------------------------------
    # make a new frame and put input line in it
    def _newConfigRadioPanel(self, parent):

        panel=JtkPanelPackLeft(parent,borderwidth=3, relief='groove')
        
        panel.addItem(JtkLabel(panel,"rx tx Frequency (x10): "))
        
        # this variable will store whatever the user has typed
        self.rxtxFrequency = IntVar(panel,value=44611000)
        
        panel.addItem(ttk.Entry(panel, textvariable=self.rxtxFrequency))

        panel.addItem(JtkLabel(panel,"  Power bias: "))
        
        # this variable will store whatever the user has typed
        self.txPowerBias = IntVar(panel,value=10)
        
        panel.addItem(ttk.Entry(panel, textvariable=self.txPowerBias, width=4))

        panel.addItem(ttk.Button(panel,text="Set Parameters" , command=self._reqSetRadioParams ))
        
        return panel

    # -------------------------------------------------------------------------------
    # make a new frame and put input line in it
    def _newSpecialReqPanel(self, parent):

        cmd_codes = (' ','Reset','reload cfg','halt cpu','BK4 read','BK4 write')

        #panel=JtkPanelPackLeft(parent,borderwidth=3, relief='groove')
        panel=JtkPanelPackLeft(parent)
        
        panel.addItem(JtkLabel(panel,"cmd_code "))

        self._syscmd_code = JtkCombo(panel,values=cmd_codes)
        panel.addItem(self._syscmd_code)
        
        panel.addItem(JtkLabel(panel," p0:"))
        self._syscmd_p0_uint8 = StringVar(panel,value='0')
        panel.addItem(TV_Entry(panel, self._syscmd_p0_uint8, width=4))

        panel.addItem(JtkLabel(panel," p1:"))
        self._syscmd_p1_uint16 = StringVar(panel,value='0')
        panel.addItem(TV_Entry(panel, self._syscmd_p1_uint16, width=6))

        panel.addItem(JtkLabel(panel," p2:"))
        self._syscmd_p2_uint16 = StringVar(panel,value='0')
        panel.addItem(TV_Entry(panel, self._syscmd_p2_uint16, width=6))

        panel.addItem(ttk.Button(panel,text="Special Request" , command=self._reqSpecial ))
        
        return panel

    # --------------------------------------------------------------------------------------
    def _reqSpecial(self):
        code=self._syscmd_code.getSelectedIndex()
        p0_uint8=string_to_int_hex(self._syscmd_p0_uint8.get(),0)
        p1_uint16=string_to_int_hex(self._syscmd_p1_uint16.get(),0)
        p2_uint16=string_to_int_hex(self._syscmd_p2_uint16.get(),0)
        cmd = Qsk5_req_syscmd(code,p0_uint8,p1_uint16,p2_uint16)
        
        self._println(str(cmd))
        
        self.stat.qconnect.queuePut(cmd)
    
    
    
    # --------------------------------------------------------------------------------------
    def _reqSetRadioParams(self):
        try:
            rxfreq1x = int(self.rxtxFrequency.get())
        except Exception as _exc:
            rxfreq1x=0
            
        try:
            powerb = int(self.txPowerBias.get())
        except Exception as _exc:
            powerb=0

        cmd = Qsk5_req_set_radio_params(rxfreq1x,powerb,0)
        self.stat.qconnect.queuePut(cmd)
        
        self.stat.app_println("qant set f="+str(cmd))




    # -------------------------------------------------------------------------------
    # make a new frame and put buttons into it, but stay in this class
    def _newSubButtonsPanel(self, container):
        cframe = JtkPanelPackLeft(container, padding=3) # borderwidth=3, relief='flat') 

        cframe.addItem(ttk.Button(cframe,text="Q Connect" , command=self._raiseQconnect ))
        cframe.addItem(ttk.Button(cframe,text="EEprom" , command=self._raiseEeprom ))
        cframe.addItem(ttk.Button(cframe,text="Messagy" , command=self._raiseMessagy ))
        cframe.addItem(ttk.Button(cframe,text="DTMF" , command=self._raiseDtmf ))
        cframe.addItem(ttk.Button(cframe,text="Spectrum" , command=self._raiseSpectrum ))

        return cframe

    # --------------------------------------------------------------------------------------
    def _raiseQconnect(self):
        self.stat.qconnect.GUI_show_window()

    # --------------------------------------------------------------------------------------
    def _raiseMessagy(self):
        self.stat.qsmsg_gui.GUI_show_window() 

    # --------------------------------------------------------------------------------------
    def _raiseDtmf(self):
        self.stat.qsdtmf_gui.GUI_show_window()

    # --------------------------------------------------------------------------------------
    def _raiseEeprom(self):
        self.stat.eeprom_gui.GUI_show_window()

    # --------------------------------------------------------------------------------------
    def _raiseSpectrum(self):
        self.stat.spectrum_gui.GUI_show_window() 

    # -------------------------------------------------------------------------------
    def _newCommandButtonsPanel(self, container):
        cframe = JtkPanelPackLeft(container, padding=3) 

        cframe.addItem(ttk.Button(cframe,text="Request Identity" , command=self._reqRadioIdentity ))
#        cframe.addItem(ttk.Button(cframe,text="Tasto M" , command=self._sendKey_Menu ))
        cframe.addItem(ttk.Button(cframe,text="Reboot" , command=self._reqRadioReboot ))
#        cframe.addItem(ttk.Button(cframe,text="Read Quansheng EEprom" , command=self._reqReadQuanshengEEprom ))

        return cframe
    
    # --------------------------------------------------------------------------------------
    def _reqReadQuanshengEEprom(self):
        self.stat.qconnect.SHIT_python_circular_import()
    
    # --------------------------------------------------------------------------------------
    def _reqRadioReboot(self):
        cmd = Qsk5_req_syscmd(IPPU_SYSCMD_reset)
        self.stat.qconnect.queuePut(cmd)
    
    # --------------------------------------------------------------------------------------
    def _sendKey_Menu(self):
        key_event_menu = 0b00100000 | 4;
        cmd = Qsk5_req_set_radio_params(0,0,key_event_menu)
        self.stat.qconnect.queuePut(cmd)
    
    # --------------------------------------------------------------------------------------
    def _reqRadioIdentity(self):
        cmd = Qsk5_req_identity()
        self.stat.qconnect.queuePut(cmd)

    # ---------------------------------------------------
    def _println(self, msg):
        self._appmain_log.println(msg)

# -------------------------------------------------------------------------
# I can both type commands from console or use an event or a button
# so, there has to be a class that manage the queue of incoming commands
class InputParserThread (Thread):

    # ----------------------------------------------------------------
    def __init__(self, stat : qpystat.Qpystat):
        Thread.__init__(self,name="Input Parser",daemon=True)
        
        self.stat = stat
        
        self.oqueue : Queue = Queue()
    
    # ------------------------------------------------------
    # use this method to queue a command to be executed
    def queuePut (self,line):
        self.oqueue.put(line)
    
    # --------------------------------------------------------------
    # Called by the Thread, do NOT call it, use start()
    def run(self):
        print ("START "+self.name)
        
        while self._parseOneLine_A():
            pass
            
        # this has to be the last one
        self.stat.qsk5_gui.endGuiConsole()
        
        print ("END ", self.name)

    # ---------------------------------------------------------------
    # _parseOneLine_B one input line and return False when need to terminate
    def _parseOneLine_A(self)-> bool:

        inline = self.oqueue.get()

        self.log_println("Request: "+inline)

        if inline == "quit":
            return False

        try:
            # need to protect from exceptions
            self._parseOneLine_B(inline)
        except Exception as _exc:
            self.log_println("parseOneCommand: "+str(_exc))
            
        return True



            
    # ---------------------------------------------------------------------
    # This is for when a command is called by the command line
    def _parseOneLine_B(self, line : str ):

        if line.startswith('show stat'):
            self.log_println(self.stat)
            
        elif line == 'threads':
            self.log_println("Threads registered")
            self.log_println("  isAlive\t isDaemon\t thread_name")

            for thread in threading.enumerate(): 
                self.log_println("  "+str(thread.is_alive())+"\t"+str(thread.daemon)+"  \t"+thread.name)

        elif line == 'clients':
            self.log_println("Clients registered")

        else:
            self.log_println("unsupported command")
            
            
    # ---------------------------------------------------------------------
    # To make code cleaner and shorter
    def log_println(self, msg):
        self.stat.appmain_gui._println(msg)




class StderrRedirector (typing.TextIO):

    # ---------------------------------------------------------------------
    def __init__(self, log_here : LogPanel ):
        self._log_panel = log_here
        
    # ---------------------------------------------------------------------
    def write(self, data):
        self._log_panel.write(str(data))    

        

