import tkinter as tk
import tkinter.ttk as ttk
import hid
import struct
import time
import threading
from typing import Callable

DEFAULT_HID_TIMEOUT = 10 # milliseconds to wait for a HID message

BIGSCREEN_VID = 0x35BD
BEYOND_PID = 0x0101

HMD_GET_SW_VER = ord('*')
HMD_GET_BOARD_SERIAL = ord('%')
HMD_GET_VXR_FIRMWARE = ord('N')
HMD_GET_OLED_ID = ord('^')
HMD_GET_USAGE_TIMER = ord('Z')
HMD_GET_STACK_LEVELS = ord('s')
HMD_DATA = ord('#')
HMD_ERROR = ord('E')

# "wait_for_response" is a helper function that waits for a specific message type
# the HID response packet (aka the HID report) has the message type as the first byte
# send the desired message types in a list of ints, any message that 
# matches one of the types in the list will be returned
# if timeout, returns empty bytes
#
# params:
# hmd_device -      A hid.Device object that is already connected to the HMD
# message_types -   List of ints, each int is a message type. If any message
#                   is received that matches one of the types, this function
#                   will return with that response packet
# timeout_ms -      An int with the desired timeout in milliseconds. Default 1000 
#
# Returns:  A bytes object. If timed out, will be empty bytes (b''). Otherwise
#           it will have the first message that matched one of the types given.

def wait_for_response(hmd_device: hid.Device, message_types:list, timeout_ms: int = 1000) -> bytes:
    start_time = time.monotonic_ns()
    
    while( (start_time + (timeout_ms*1000000)) > time.monotonic_ns()):
        bytesout = hmd_device.read(65, timeout=DEFAULT_HID_TIMEOUT)
        if(len(bytesout) > 0):
            if(bytesout[0] in message_types):
                return bytesout
    
    return b''

class readthread():
    def __init__(self, hid_dev:hid.Device, callback: Callable[[float, float, float], None] = None):
        self.dev = hid_dev
        self.cb = callback
        self.quitting_now = False

    def quit_now(self):
        self.quitting_now = True

    def start(self):
        if(hasattr(self, "mthread")):
            if(self.mthread.is_alive()):
                self.quitting_now = True
                self.mthread.join()
        self.mthread = threading.Thread(target=self.threadworker)
        self.quitting_now = False
        self.mthread.start()

    def threadworker(self):
        while(not self.quitting_now):
            bytesout = self.dev.read(65, timeout=DEFAULT_HID_TIMEOUT)
            if(len(bytesout) > 0):
                if(bytesout[0] == HMD_DATA):
                    # fan_speed, prox_dist, cc1val, cc2val = struct.unpack('>HHHH', bytesout[2:10])
                    btemp, ltemp, rtemp = struct.unpack('<fff', bytesout[10:22])
                    self.cb(ltemp, rtemp, btemp)

class mainui():
    def __init__(self):
        self.root = tk.Tk()
        self.ltemp = tk.StringVar(self.root, "n/a")
        self.rtemp = tk.StringVar(self.root, "n/a")
        self.btemp = tk.StringVar(self.root, "n/a")

        lbl1 = ttk.Label(self.root, text="Left Panel Temp:")
        lbl2 = ttk.Label(self.root, text="Right Panel Temp:")
        lbl3 = ttk.Label(self.root, text="Board Temp:")

        lbl4 = ttk.Label(self.root, textvariable=self.ltemp)
        lbl5 = ttk.Label(self.root, textvariable=self.rtemp)
        lbl6 = ttk.Label(self.root, textvariable=self.btemp)
        
        lbl1.grid(row=0, column=0)
        lbl2.grid(row=1, column=0)
        lbl3.grid(row=2, column=0)
        lbl4.grid(row=0, column=1)
        lbl5.grid(row=1, column=1)
        lbl6.grid(row=2, column=1)

        self.bigs = hid.Device(vid=0x35bd, pid=0x0101)

        self.readthread = readthread(self.bigs, self.newdata)
        self.readthread.start()

        self.root.mainloop()


    def newdata(self, ltemp, rtemp, btemp):
        self.ltemp.set(f"{ltemp:.2f}")
        self.rtemp.set(f"{rtemp:.2f}")
        self.btemp.set(f"{btemp-272.15:.2f}")

if __name__ == '__main__':
    mywin = mainui()
