import os
import json
import subprocess
import time
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, messagebox
import serial.tools.list_ports
import sys
from mpremote.main import main as mpremote_main



class RP2040UploaderApp:
    def __init__(self, master):
        self.master = master

        self.folder_path = os.path.join(os.getcwd(), "Resources/RP2040")
        self.config_path = os.path.join(self.folder_path, "config.json")

        self.label = tk.Label(master, text="Searching for board...")
        self.label.pack(pady=10)

        self.port, self.board_name = self.find_supported_port()
        if self.port:
            self.label.config(text=f"{self.board_name} detected on {self.port}")
        else:
            self.label.config(text="No supported board found.")
        
        # Config entry fields
        self.ip_entry = self.create_labeled_entry("IP Address:")
        self.subnet_entry = self.create_labeled_entry("Subnet Mask:")
        self.gateway_entry = self.create_labeled_entry("Gateway:")

        self.load_config()

        self.upload_button = ttk.Button(
            master, 
            text="Upload Files", 
            style="Accent.TButton", 
            command=self.upload_files, 
            state=tk.NORMAL if self.port else tk.DISABLED
        )
        self.upload_button.pack(pady=10)

    def create_labeled_entry(self, label_text):
        frame = tk.Frame(self.master)
        frame.pack(pady=2)
        label = tk.Label(frame, text=label_text)
        label.pack(side=tk.LEFT)
        entry = tk.Entry(frame, width=20)
        entry.pack(side=tk.RIGHT)
        return entry

    def find_supported_port(self):
        # List of supported boards (VID, PID, Name)
        supported_boards = [
            (0x239A, 0x80F2, "RP2040"),
            (0x2E8A, 0x0005, "RP2350"),
        ]

        ports = serial.tools.list_ports.comports()
        for port in ports:
            for vid, pid, name in supported_boards:
                if port.vid == vid and port.pid == pid:
                    return port.device, name

        return None, None

    def load_config(self):
        if os.path.isfile(self.config_path):
            try:
                with open(self.config_path, "r") as f:
                    config = json.load(f)
                    self.ip_entry.insert(0, config.get("ip", ""))
                    self.subnet_entry.insert(0, config.get("subnet", ""))
                    self.gateway_entry.insert(0, config.get("gateway", ""))
            except Exception as e:
                messagebox.showerror("Error", f"Failed to read config.json:\n{e}")
        else:
            messagebox.showwarning("Missing File", f"No config.json found in {self.folder_path}")

    def save_config(self):
        config = {
            "ip": self.ip_entry.get(),
            "subnet": self.subnet_entry.get(),
            "gateway": self.gateway_entry.get()
        }
        try:
            with open(self.config_path, "w") as f:
                json.dump(config, f, indent=4)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to save config.json:\n{e}")

    def upload_files(self):
        if not os.path.isdir(self.folder_path):
            messagebox.showerror("Error", f"Folder '{self.folder_path}' not found.")
            return

        self.save_config()

        success = True
        for filename in os.listdir(self.folder_path):
            full_path = os.path.join(self.folder_path, filename)
            if os.path.isfile(full_path):
                self.label.config(text=f"Uploading {filename}...")
                self.master.update()

                # Save original sys.argv and replace it with mpremote-style args
                original_argv = sys.argv
                sys.argv = ["mpremote", "connect", self.port, "fs", "cp", full_path, f":{filename}"]

                try:
                    mpremote_main()
                except SystemExit as e:
                    success = False
                    messagebox.showerror("Upload Failed", f"Failed to upload {filename} (mpremote exited with code {e.code}).")
                    break
                finally:
                    sys.argv = original_argv  # Always restore original argv

        if success:

            # --- Soft reset the board ---
            self.label.config(text="Resetting board...")
            print("Resetting board...")
            self.master.update()

            original_argv = sys.argv
            sys.argv = ["mpremote", "connect", self.port, "reset"]
            try:
                mpremote_main()
            except SystemExit:
                pass
            finally:
                sys.argv = original_argv
            time.sleep(0.4)
            self.label.config(text="DONE")
            self.master.update()
            messagebox.showinfo("Success", "All files uploaded successfully!")



if __name__ == "__main__":
    root = tk.Tk()
    app = RP2040UploaderApp(root)
    root.title("RP Loader V1.2.0")

    root.iconbitmap(r'Resources/icon1.ico')

    # Set the theme
    root.tk.call("source", "Resources/azure.tcl")
    root.tk.call("set_theme", "dark")

    # Set a minsize for the window, and place it in the middle
    root.update()
    root.minsize(400, 200)

    # Footer label in bottom right
    footer_frame = tk.Frame(root)
    footer_frame.pack(side=tk.BOTTOM, fill=tk.X)
    footer_label = tk.Label(footer_frame, text="Made by Hendo 08/10/2025", anchor="e", justify="right", font=("TkDefaultFont", 7, "bold"))
    footer_label.pack(side=tk.RIGHT, padx=10, pady=5)

    root.mainloop()
