#!/usr/bin/python3.11

import queue
import socket
from threading import Thread

import tkinter as tk
from tkinter import ttk
from tkinter import *
from tkinter.ttk import Combobox

from htc_find_controller import FindController
from htc_get_modules import getModuleList

def valid_ip_address(ip_address):
    if len(ip_address) < 6:
        return False
    try:
        socket.inet_aton(ip_address)
    except socket.error:
        return True
    return True

class StatusBar(tk.Frame):
    def __init__(self, owner):
        tk.Frame.__init__(self, owner)
        self.variable=tk.StringVar()
        self.label=tk.Label(self, bd=1, relief=tk.SUNKEN, anchor=tk.W, textvariable=self.variable,font=('arial',12,'normal'))
        self.variable.set('')
        self.label.pack(fill=tk.X)
        self.label.pack(fill=tk.X)
        self.pack(side = BOTTOM, fill=tk.X)
    def set(self, text):
        self.variable.set(text)

class MainWindow:
    def __init__(self, owner):

        # Save the owner
        self.owner = owner

        # Create some queues for aysncronous work
        self.queueFindController = queue.Queue()
        self.queueFindModule = queue.Queue()
        self.moduleFinderThread = None

        # Create a module list and correspondign target IP address
        self.moduleList = []
        self.target_ip = None

        # Configure the main window
        owner.geometry('600x650')
        owner.configure(background='#FFFACD')
        owner.title('HTC debug tool')

        # Main frame and status bar
        self.myStatusBar=StatusBar(owner)
        self.myStatusBar.set("Hi!")
        mainframe = Frame(owner, bg='#FFFACD', borderwidth=25)
        mainframe.pack(ipadx=15, ipady=15, side="top", fill="both", expand=True)

        # variable associated with the radio button group
        self.debugRadioButtonVar = StringVar(value="DEBUG_NONE")

        # Create label for IP address
        Label(mainframe, text='HTC IP address:', bg='#FFFACD', font=('arial', 12, 'normal')).grid(column=0, row=0, padx=10, pady=10)

        # Create combo box for IP address
        self.HTC_address_var = StringVar()
        self.HTC_address_combo = Combobox(mainframe, width = 27, textvariable = self.HTC_address_var)
        self.HTC_address_combo['values'] = ()
        self.HTC_address_combo.grid(column=1, row=0, padx=10, pady=10)
        self.HTC_address_combo.current()

        # Create label for debug type radio button
        Label(mainframe, text='Debug type:', bg='#FFFACD', font=('arial', 12, 'normal')).grid(column=0, row=1, padx=10, pady=10)

        # Create  a group of radio buttons
        radioButtonFrame=Frame(mainframe, width=0, height=0, bg='#FFFACD')
        radioButtonFrame.grid(column=1, row=1, padx=10, pady=10)
        debug_values=['DEBUG_NONE', 'DEBUG_FATAL','DEBUG_ERROR','DEBUG_WARN', 'DEBUG_NOTICE''DEBUG_INFO', 'DEBUG_TRACE']
        for text in debug_values:
            rbGroupOne=Radiobutton(radioButtonFrame, text=text, variable=self.debugRadioButtonVar, value=text, bg='#FFFACD', font=('arial', 12, 'normal')).pack(side='top', anchor = 'w')

        # Create label for module list
        Label(mainframe, text='Module:', bg='#FFFACD', font=('arial', 12, 'normal')).grid(column=0, row=2, padx=10, pady=10)

        # Create a listbox and associated scrollbar
        listBoxFrame=Frame(mainframe, width=0, height=0, bg='#FFFACD')
        listBoxFrame.grid(column=1, row=2, rowspan=2, padx=10, pady=10)
        self.moduleListBox=Listbox(listBoxFrame, bg='#FFFACD', font=('arial', 12, 'normal'), width=25, height=15, selectmode=SINGLE)
        self.moduleListBox.pack(side="left", fill="y")

        # Creating a Scrollbar
        scrollbar = Scrollbar(listBoxFrame, orient="vertical")
        scrollbar.config(command=self.moduleListBox.yview)
        scrollbar.pack(side="right", fill="y")
        self.moduleListBox.config(yscrollcommand=scrollbar.set)

        # Create  a button for find modules
        self.btnFind = Button(mainframe, text='Find modules', bg='#9ACD32', font=('arial', 12, 'normal'), command=self.btnClickFind)
        self.btnFind.grid(column=2, row=0, padx=10, pady=10)

        # Create  a button for apply debug
        self.btnApply = Button(mainframe, text='Apply', bg='#9ACD32', font=('arial', 12, 'normal'), command=self.btnClickApply)
        self.btnApply.grid(column=2, row=2, padx=10, pady=10)

        # Create  a button
        self.btnExit = Button(mainframe, text='Exit', bg='#FF0000', font=('arial', 12, 'normal'), command=self.btnClickExit)
        self.btnExit.grid(column=2, row=3, padx=10, pady=10)

        # Create and start the controller finder
        self.findController = FindController(self.notify_add_service)
        self.findController.Run()

        # Start a repeating function
        self.repeater()

    # Repeating function
    def repeater(self):
        self.checkQueueFindController()
        self.checkQueueFindModule()
        self.owner.after(1000, self.repeater)

    # Service discovery callback
    def notify_add_service(self, adding, ip_address):
        print(adding, ip_address)
        self.queueFindController.put((adding, ip_address))

    # Module discovery callback
    def notify_modules_found(self, target_ip, moduleList):
        print(target_ip)
        print(moduleList)
        self.queueFindModule.put((target_ip, moduleList))

    # Check the FindController queue
    def checkQueueFindController(self):
        if not self.queueFindController.empty():
            item = self.queueFindController.get()
            if item and item[0]:
                self.addListHTC_addressValue(item[1])

    # Check the FindModule queue
    def checkQueueFindModule(self):
        if not self.queueFindModule.empty():
            target_ip, moduleList = self.queueFindModule.get()
            self.setStatusBarText(f'Found {len(moduleList)} modules.')
            self.moduleList = moduleList
            self.target_ip = target_ip
            names = [tple[0] for tple in moduleList]
            self.setModuleListboxItems(names)
            print("Thread joining")
            print(target_ip)
            self.moduleFinderThread.join()
            print("Thread joined")

    # Set status bar text
    def setStatusBarText(self, text):
        self.myStatusBar.set(text)

    # get the user input from the HTC IP address combo box
    def getHTC_addressValue(self):
        return self.HTC_address_var.get()

    # add a value to the HTC IP address combo box drop down
    def addListHTC_addressValue(self, newValue):
        values = self.HTC_address_combo['values']
        if newValue not in values:
            self.HTC_address_combo['values'] = (*values, newValue)

    # get the selected radio button value
    def getDebugRadioButtonValue(self):
        return self.debugRadioButtonVar.get()

    # get the selected module list box value
    def getModuleListboxValue(self):
        itemSelected = self.moduleListBox.curselection()
        if itemSelected:
            return itemSelected[0]
        else:
            return None

    # Replace all the current module list box items
    def setModuleListboxItems(self, items):
        self.moduleListBox.delete(0, END)
        for index, item in enumerate(items):
            self.moduleListBox.insert(index, item)

    # Start the module finder thread which gets the module list form the target controller
    def startModuleFinder(self, ip_address):
        self.moduleFinderThread = Thread(target = getModuleList, args = (ip_address, self.notify_modules_found))
        self.moduleFinderThread.start()

    def sendDebugMessage(self, ipAddress, port, module, debugLevel):
        msg = f'{{"MessageType":"SYS_DEBUG_STATE", "TARGET_MODULE":"{module}", "DEBUG_LEVEL":"{debugLevel}", "DEBUG_OUTPUT":"console"}}'
        print(msg)        
        send_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        send_socket.sendto(msg.encode('utf8'), (ipAddress, port))        

    # call when the Find button is clicked
    def btnClickFind(self):
        print('Find clicked')
        ip_address = self.getHTC_addressValue()
        if valid_ip_address(ip_address):
            self.setStatusBarText("Looking for modules at " + ip_address)
            self.startModuleFinder(ip_address)
        else:
            self.setStatusBarText("Please enter a valid IP address")

    # call when the apply button is clicked
    def btnClickApply(self):
        print('Apply clicked')
        print("getDebugRadioButtonValue", self.getDebugRadioButtonValue())
        print("getModuleListboxValue", self.getModuleListboxValue())
        if not self.target_ip:
            self.setStatusBarText("Please select an IP address and press Find")
            return
        index = self.getModuleListboxValue()
        if index is None:
            self.setStatusBarText("Please select a module")
            return
        debugLevel = self.getDebugRadioButtonValue()
        module = self.moduleList[index][0]
        port = self.moduleList[index][1]
        self.sendDebugMessage(self.target_ip, port, module, debugLevel)


    # call when the exit button is clicked
    def btnClickExit(self):
        print('Exit clicked')

root = Tk()
app = MainWindow(root)
root.mainloop()

