import threading
import time
import tkinter as tk
from tkinter import messagebox, ttk

import serial
import serial.tools.list_ports


class WaveformGenHMI:
    def __init__(self, root):
        self.root = root
        self.root.title("ESP32-P4 Waveform Generator")
        self.root.geometry("400x300")

        self.ser = None

        # UI Elements
        ttk.Label(root, text="Serial Port:").grid(
            row=0, column=0, padx=5, pady=5, sticky="w"
        )
        self.port_var = tk.StringVar()
        self.port_combo = ttk.Combobox(root, textvariable=self.port_var)
        self.port_combo.grid(row=0, column=1, padx=5, pady=5)
        self.refresh_ports()

        ttk.Button(root, text="Connect", command=self.toggle_connect).grid(
            row=0, column=2, padx=5, pady=5
        )

        ttk.Label(root, text="GPIO Pin:").grid(
            row=1, column=0, padx=5, pady=5, sticky="w"
        )
        self.pin_entry = ttk.Entry(root)
        self.pin_entry.insert(0, "21")
        self.pin_entry.grid(row=1, column=1, padx=5, pady=5)

        ttk.Label(root, text="Frequency (Hz):").grid(
            row=2, column=0, padx=5, pady=5, sticky="w"
        )
        self.freq_slider = tk.Scale(
            root, from_=0, to=5000, orient=tk.HORIZONTAL, length=200
        )
        self.freq_slider.grid(row=2, column=1, columnspan=2, padx=5, pady=5)

        self.freq_entry = ttk.Entry(root, width=10)
        self.freq_entry.insert(0, "1000")
        self.freq_entry.grid(row=3, column=1, padx=5, pady=5, sticky="w")
        ttk.Button(root, text="Apply", command=self.send_config).grid(
            row=3, column=2, padx=5, pady=5
        )

        self.status_label = ttk.Label(root, text="Disconnected", foreground="red")
        self.status_label.grid(row=4, column=0, columnspan=3, pady=10)

        # Sync slider and entry
        self.freq_slider.bind("<Motion>", lambda e: self.sync_entry_to_slider())
        self.freq_entry.bind("<Return>", lambda e: self.sync_slider_to_entry())

    def refresh_ports(self):
        ports = [p.device for p in serial.tools.list_ports.comports()]
        self.port_combo["values"] = ports
        if ports:
            self.port_combo.current(0)

    def sync_entry_to_slider(self):
        self.freq_entry.delete(0, tk.END)
        self.freq_entry.insert(0, str(self.freq_slider.get()))

    def sync_slider_to_entry(self):
        try:
            val = int(self.freq_entry.get())
            if 0 <= val <= 5000:
                self.freq_slider.set(val)
        except ValueError:
            pass

    def toggle_connect(self):
        if self.ser and self.ser.is_open:
            self.ser.close()
            self.status_label.config(text="Disconnected", foreground="red")
        else:
            try:
                self.ser = serial.Serial(self.port_var.get(), 115200, timeout=1)
                self.status_label.config(
                    text=f"Connected to {self.port_var.get()}", foreground="green"
                )
            except Exception as e:
                messagebox.showerror("Error", f"Failed to connect: {e}")

    def send_config(self):
        if not self.ser or not self.ser.is_open:
            messagebox.showwarning("Warning", "Not connected to serial port")
            return

        try:
            pin = int(self.pin_entry.get())
            freq = int(self.freq_entry.get())

            if not (0 <= pin <= 54):
                raise ValueError("Pin must be 0-54")
            if not (0 <= freq <= 5000):
                raise ValueError("Frequency must be 0-5000")

            cmd = f"SET {pin} {freq}"

            self.ser.write(cmd.encode())

            # Optional: read response
            line = self.ser.readline().decode().strip()
            if line:
                print(f"ESP32: {line}")

        except ValueError as e:
            messagebox.showerror("Error", str(e))
        except Exception as e:
            messagebox.showerror("Error", f"Serial communication error: {e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = WaveformGenHMI(root)
    root.mainloop()
