import struct
import time
import tkinter as tk
from tkinter import ttk
import threading

# Constants
ID_TIMESTAMP = 0x01
ID_EFIELD_CH1 = 0x02
ID_ACCEL_X = 0x03
ID_ACCEL_Y = 0x04
ID_ACCEL_Z = 0x05
ID_BATTERY_VOLT = 0x09
ID_3V3_VOLT = 0x0A
ID_1V5_VOLT = 0x0B
ID_EFIELD_CH2 = 0x1B
ID_EFIELD_CH3 = 0x1C
ID_3V_RTC = 0x12
ID_TEMP = 0x13
ID_STATUS = 0x11

class DataGUI:
    def __init__(self, master):
        self.master = master
        master.title("Instrument Data Display")

        # Variables to hold data
        self.pctime_var = tk.StringVar()
        self.timestamp_var = tk.StringVar()
        self.e1_var = tk.StringVar()
        self.e2_var = tk.StringVar()
        self.e3_var = tk.StringVar()
        self.vbat_var = tk.StringVar()
        self.v3v3_var = tk.StringVar()
        self.v1v5_var = tk.StringVar()
        self.v3vrtc_var = tk.StringVar()
        self.accel_x_var = tk.StringVar()
        self.accel_y_var = tk.StringVar()
        self.accel_z_var = tk.StringVar()
        self.temp_var = tk.StringVar()

        # Status variables
        self.status_var = tk.StringVar()
        self.status_hex_var = tk.StringVar()
        self.status_bits_vars = [tk.StringVar() for _ in range(12)]

        self.make_widgets()

        # Start the reader thread
        self.reader_thread = threading.Thread(target=self.read_data_loop, daemon=True)
        self.reader_thread.start()

    def make_widgets(self):
        ttk.Label(self.master, text="PC Time").grid(row=0, column=0, sticky='e')
        ttk.Label(self.master, textvariable=self.pctime_var, width=20).grid(row=0, column=1, sticky='w')

        labels = [
            ("Timestamp", self.timestamp_var),
            ("E-field Ch1 (uV)", self.e1_var),
            ("E-field Ch2 (uV)", self.e2_var),
            ("E-field Ch3 (uV)", self.e3_var),
            ("Battery Voltage (V)", self.vbat_var),
            ("3.3V RTC (V)", self.v3vrtc_var),
            ("3.3V Rail (V)", self.v3v3_var),
            ("1.5V Rail (V)", self.v1v5_var),
            ("Accel X (g)", self.accel_x_var),
            ("Accel Y (g)", self.accel_y_var),
            ("Accel Z (g)", self.accel_z_var),
            ("Temperature (°C)", self.temp_var),
        ]

        for i, (label, var) in enumerate(labels, start=1):
            ttk.Label(self.master, text=label).grid(row=i, column=0, sticky='e')
            ttk.Label(self.master, textvariable=var, width=20).grid(row=i, column=1, sticky='w')

        # Horizontal separator
        ttk.Separator(self.master, orient="horizontal").grid(row=len(labels) + 1, columnspan=2, sticky="ew", pady=10)

        # Status display
        row = len(labels) + 2
        ttk.Label(self.master, text="Status (Decimal)").grid(row=row, column=0, sticky='e')
        ttk.Label(self.master, textvariable=self.status_var, width=20).grid(row=row, column=1, sticky='w')
        row += 1
        ttk.Label(self.master, text="Status (Hex)").grid(row=row, column=0, sticky='e')
        ttk.Label(self.master, textvariable=self.status_hex_var, width=20).grid(row=row, column=1, sticky='w')

        status_labels = [
            "31:RemoteSupport (0=disconnected, 1=connected)",
            "30–29:EF Gain Ch3",
            "28–27:EF Gain Ch2",
            "26–12:Atomic Clock Alarm",
            "11–8:Atomic Clock Status",
            "7:EF SimData (0=normal, 1=sim)",
            "6:LSB output (0=off, 1=on)",
            "5–4:EF Gain Ch1",
            "3:Watchdog (0=off, 1=on)",
            "2–1:Start-Mode",
            "0:Logging (0=off, 1=on)"
        ]

        for label, var in zip(status_labels, self.status_bits_vars):
            row += 1
            ttk.Label(self.master, text=label).grid(row=row, column=0, sticky='e')
            ttk.Label(self.master, textvariable=var, width=20).grid(row=row, column=1, sticky='w')

    def read_data_loop(self):
        efield_data = {}
        last_voltages = {
            ID_BATTERY_VOLT: 0.0,
            ID_3V3_VOLT: 0.0,
            ID_1V5_VOLT: 0.0,
            ID_3V_RTC: 0.0,
            ID_TEMP: 0.0,
        }
        accel_data = {
            ID_ACCEL_X: b'\x00\x00\x00\x00',
            ID_ACCEL_Y: b'\x00\x00\x00\x00',
            ID_ACCEL_Z: b'\x00\x00\x00\x00',
        }
        current_timestamp = None
        status_val = 0

        try:
            with open("Test.B162", "rb") as f:
                while True:
                    header = f.read(1)
                    if not header:
                        break

                    data_id = header[0]
                    data_bytes = f.read(4)
                    if len(data_bytes) < 4:
                        break

                    if data_id == ID_TIMESTAMP:
                        ts = struct.unpack('<I', data_bytes)[0]
                        current_timestamp = ts

                        pc_time = time.time()
                        self.pctime_var.set(self.format_time(pc_time))
                        self.timestamp_var.set(self.format_time(ts))

                        self.e1_var.set(f"{efield_data.get(ID_EFIELD_CH1, 0.0):.4f}")
                        self.e2_var.set(f"{efield_data.get(ID_EFIELD_CH2, 0.0):.4f}")
                        self.e3_var.set(f"{efield_data.get(ID_EFIELD_CH3, 0.0):.4f}")
                        self.vbat_var.set(f"{last_voltages[ID_BATTERY_VOLT]:.3f}")
                        self.v3vrtc_var.set(f"{last_voltages[ID_3V_RTC]:.3f}")
                        self.v3v3_var.set(f"{last_voltages[ID_3V3_VOLT]:.3f}")
                        self.v1v5_var.set(f"{last_voltages[ID_1V5_VOLT]:.3f}")
                        self.accel_x_var.set(f"{self.bytes_to_accel(accel_data[ID_ACCEL_X]):.4f}")
                        self.accel_y_var.set(f"{self.bytes_to_accel(accel_data[ID_ACCEL_Y]):.4f}")
                        self.accel_z_var.set(f"{self.bytes_to_accel(accel_data[ID_ACCEL_Z]):.4f}")
                        self.temp_var.set(f"{last_voltages[ID_TEMP]:.2f}")

                        # Display status
                        self.status_var.set(str(status_val))
                        self.status_hex_var.set(hex(status_val))

                        # Decode each status field
                        self.status_bits_vars[0].set(str((status_val >> 31) & 0x1))
                        self.status_bits_vars[1].set(str((status_val >> 29) & 0x3))
                        self.status_bits_vars[2].set(str((status_val >> 27) & 0x3))
                        self.status_bits_vars[3].set(str((status_val >> 12) & 0x7FFF))
                        self.status_bits_vars[4].set(str((status_val >> 8) & 0xF))
                        self.status_bits_vars[5].set(str((status_val >> 7) & 0x1))
                        self.status_bits_vars[6].set(str((status_val >> 6) & 0x1))
                        self.status_bits_vars[7].set(str((status_val >> 4) & 0x3))
                        self.status_bits_vars[8].set(str((status_val >> 3) & 0x1))
                        self.status_bits_vars[9].set(str((status_val >> 1) & 0x3))
                        self.status_bits_vars[10].set(str(status_val & 0x1))

                        time.sleep(1)

                    elif data_id in (ID_EFIELD_CH1, ID_EFIELD_CH2, ID_EFIELD_CH3):
                        val = struct.unpack('<f', data_bytes)[0]
                        efield_data[data_id] = val

                    elif data_id in (ID_BATTERY_VOLT, ID_3V3_VOLT, ID_1V5_VOLT, ID_3V_RTC, ID_TEMP):
                        val = struct.unpack('<f', data_bytes)[0]
                        last_voltages[data_id] = val

                    elif data_id in (ID_ACCEL_X, ID_ACCEL_Y, ID_ACCEL_Z):
                        accel_data[data_id] = data_bytes

                    elif data_id == ID_STATUS:
                        status_val = struct.unpack('<I', data_bytes)[0]

        except FileNotFoundError:
            print("File Test.B162 not found.")

    def format_time(self, unix_time):
        return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(unix_time))

    def bytes_to_accel(self, data_bytes):
        raw_value = struct.unpack('<I', data_bytes)[0]
        return raw_value * 0.0001  # Adjust scaling as needed

if __name__ == "__main__":
    root = tk.Tk()
    gui = DataGUI(root)
    root.mainloop()
