import asyncio
import tkinter as tk
from tkinter import ttk, messagebox  # Import messagebox for confirmations
import json
from async_tkinter_loop import async_handler, async_mainloop
from networking import WebSocketClient
import color_utils
from settings import Settings
import time

# Dark theme colors (unchanged)
bg_color = "#2e2e2e"
fg_color = "white"
trough_color_red = "#4a0000"
trough_color_green = "#004a00"
trough_color_blue = "#00004a"
trough_color_brightness = "#4a4a4a"
trough_color_delay = "#4a4a4a"
active_bg_color = "#4a4a4a"
highlight_pattern_color = "#6a5acd"
# New color for active color in palette
active_palette_color_border = "#FFD700"  # Gold color


class App:
    def __init__(self) -> None:
        self.settings = Settings()
        self.root = tk.Tk()
        self.root.attributes("-fullscreen", True)
        self.root.configure(bg=bg_color)

        # Debouncing variables (remain the same)
        self.last_rgb_update_time = 0
        self.rgb_update_interval_ms = 100
        self.pending_rgb_update_id = None

        self.last_brightness_update_time = 0
        self.brightness_update_interval_ms = 100
        self.pending_brightness_update_id = None

        self.last_delay_update_time = 0
        self.delay_update_interval_ms = 100
        self.pending_delay_update_id = None

        # --- WebSocketClient ---
        self.websocket_client = WebSocketClient("ws://192.168.4.1:80/ws")
        self.root.after(100, async_handler(self.websocket_client.connect))

        # Configure ttk style (unchanged)
        style = ttk.Style()
        style.theme_use("alt")
        style.configure(".", background=bg_color, foreground=fg_color, font=("Arial", 14))
        style.configure("TNotebook", background=bg_color, borderwidth=0)
        style.configure(
            "TNotebook.Tab", background=bg_color, foreground=fg_color, font=("Arial", 30), padding=[10, 5]
        )
        style.map("TNotebook.Tab", background=[("selected", active_bg_color)], foreground=[("selected", fg_color)])
        style.configure("TFrame", background=bg_color)

        # Create Notebook for tabs (unchanged)
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(expand=1, fill="both")

        self.tabs = {}
        self.create_tabs()

        self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_change)

        # Add Reload Config Button (unchanged)
        reload_button = tk.Button(
            self.root,
            text="Reload Config",
            command=self.reload_config,
            bg=active_bg_color,
            fg=fg_color,
            font=("Arial", 20),
            padx=20,
            pady=10,
            relief=tk.FLAT,
        )
        reload_button.pack(side=tk.BOTTOM, pady=20)

        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

        async_mainloop(self.root)

    def on_closing(self):
        print("Closing application...")
        asyncio.create_task(self.websocket_client.close())
        self.root.destroy()

    def create_tabs(self):
        for tab_name in list(self.tabs.keys()):
            self.notebook.forget(self.tabs[tab_name])
            del self.tabs[tab_name]

        for key, value in self.settings["lights"].items():
            tab = ttk.Frame(self.notebook)
            self.notebook.add(tab, text=key)
            self.create_light_control_widgets(tab, key, value["names"], value["settings"])
            self.tabs[key] = tab

    def create_light_control_widgets(self, tab, tab_name, ids, initial_settings):
        slider_length = 800
        slider_width = 50

        # Extract initial color, brightness, and delay
        initial_colors = initial_settings.get("colors", ["#000000"])
        initial_hex_color = initial_colors[0] if initial_colors else "#000000"
        initial_brightness = initial_settings.get("brightness", 127)
        initial_delay = initial_settings.get("delay", 0)
        initial_pattern = initial_settings.get("pattern", "on")

        initial_r, initial_g, initial_b = color_utils.hex_to_rgb(initial_hex_color)

        # Main frame to hold everything within the tab
        main_tab_frame = tk.Frame(tab, bg=bg_color)
        main_tab_frame.pack(expand=True, fill="both", padx=10, pady=10)

        # Left panel for sliders
        slider_panel_frame = tk.Frame(main_tab_frame, bg=bg_color)
        slider_panel_frame.pack(side=tk.LEFT, padx=10, pady=10)

        # Common slider configuration
        slider_config = {
            "from_": 255,
            "to": 0,
            "orient": tk.VERTICAL,
            "length": slider_length,
            "width": slider_width,
            "bg": bg_color,
            "fg": fg_color,
            "highlightbackground": bg_color,
            "activebackground": active_bg_color,
            "resolution": 1,
            "sliderlength": 70,
        }

        # Red Slider
        red_slider = tk.Scale(slider_panel_frame, label="Red", troughcolor=trough_color_red, **slider_config)
        red_slider.set(initial_r)
        red_slider.pack(side=tk.LEFT, padx=10)
        red_slider.bind("<B1-Motion>", lambda _: self.schedule_update_rgb(tab))
        red_slider.bind("<ButtonRelease-1>", lambda _: self.schedule_update_rgb(tab, force_send=True))

        # Green Slider
        green_slider = tk.Scale(slider_panel_frame, label="Green", troughcolor=trough_color_green, **slider_config)
        green_slider.set(initial_g)
        green_slider.pack(side=tk.LEFT, padx=10)
        green_slider.bind("<B1-Motion>", lambda _: self.schedule_update_rgb(tab))
        green_slider.bind("<ButtonRelease-1>", lambda _: self.schedule_update_rgb(tab, force_send=True))

        # Blue Slider
        blue_slider = tk.Scale(slider_panel_frame, label="Blue", troughcolor=trough_color_blue, **slider_config)
        blue_slider.set(initial_b)
        blue_slider.pack(side=tk.LEFT, padx=10)
        blue_slider.bind("<B1-Motion>", lambda _: self.schedule_update_rgb(tab))
        blue_slider.bind("<ButtonRelease-1>", lambda _: self.schedule_update_rgb(tab, force_send=True))

        # Brightness Slider
        brightness_slider = tk.Scale(
            slider_panel_frame, label="Brightness", troughcolor=trough_color_brightness, **slider_config
        )
        brightness_slider.set(initial_brightness)
        brightness_slider.pack(side=tk.LEFT, padx=10)
        brightness_slider.bind("<B1-Motion>", lambda _: self.schedule_update_brightness(tab))
        brightness_slider.bind("<ButtonRelease-1>", lambda _: self.schedule_update_brightness(tab, force_send=True))

        # Delay Slider
        delay_slider_config = slider_config.copy()
        delay_slider_config.update(
            {
                "from_": 1000,
                "to": 0,
                "resolution": 10,
                "label": "Delay (ms)",
                "troughcolor": trough_color_delay,
            }
        )
        delay_slider = tk.Scale(slider_panel_frame, **delay_slider_config)
        delay_slider.set(initial_delay)
        delay_slider.pack(side=tk.LEFT, padx=10)
        delay_slider.bind("<B1-Motion>", lambda _: self.schedule_update_delay(tab))
        delay_slider.bind("<ButtonRelease-1>", lambda _: self.schedule_update_delay(tab, force_send=True))

        # Store references to widgets for this tab
        tab.widgets = {
            "red_slider": red_slider,
            "green_slider": green_slider,
            "blue_slider": blue_slider,
            "brightness_slider": brightness_slider,
            "delay_slider": delay_slider,
            "selected_color_index": 0,  # Default to the first color
        }
        tab.colors_in_palette = initial_colors.copy()  # Store the list of hex colors for this tab
        tab.color_swatch_frames = []  # To hold references to the color swatches

        # Right panel for IDs, Patterns, and NEW Color Palette
        right_panel_frame = tk.Frame(main_tab_frame, bg=bg_color)
        right_panel_frame.pack(side=tk.LEFT, padx=20, pady=10, anchor="n", expand=True, fill="both")

        # IDs section - MODIFIED TO BE SIDE-BY-SIDE
        ids_frame = tk.Frame(right_panel_frame, bg=bg_color)
        ids_frame.pack(pady=10, fill=tk.X)
        tk.Label(ids_frame, text="Associated Names:", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(pady=10)

        # New inner frame for the IDs to be displayed horizontally
        ids_inner_frame = tk.Frame(ids_frame, bg=bg_color)
        ids_inner_frame.pack(fill=tk.X, expand=True) # Pack this frame to fill available width

        for light_id in ids:
            tk.Label(ids_inner_frame, text=str(light_id), font=("Arial", 18), bg=bg_color, fg=fg_color).pack(
                side=tk.LEFT, padx=5, pady=2
            ) # Pack labels horizontally

        # --- New Frame to hold Patterns and Color Palette side-by-side ---
        patterns_and_palette_frame = tk.Frame(right_panel_frame, bg=bg_color)
        patterns_and_palette_frame.pack(pady=20, fill=tk.BOTH, expand=True)

        # Patterns section
        patterns_frame = tk.Frame(patterns_and_palette_frame, bg=bg_color, bd=2, relief=tk.GROOVE)
        patterns_frame.pack(side=tk.LEFT, padx=10, pady=5, fill=tk.BOTH, expand=True)  # Pack to the left
        tk.Label(patterns_frame, text="Patterns:", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(pady=10)

        tab.pattern_buttons = {}
        patterns = self.settings.get("patterns", [])
        for pattern_name in patterns:
            button = tk.Button(
                patterns_frame,
                text=pattern_name,
                command=lambda p=pattern_name: self.send_pattern(tab_name, p),
                bg=active_bg_color,
                fg=fg_color,
                font=("Arial", 18),
                padx=15,
                pady=5,
                relief=tk.FLAT,
            )
            button.pack(pady=5, fill=tk.X)
            tab.pattern_buttons[pattern_name] = button

        self.highlight_pattern_button(tab, initial_pattern)

        # --- Color Palette Editor Section ---
        color_palette_editor_frame = tk.Frame(patterns_and_palette_frame, bg=bg_color, bd=2, relief=tk.GROOVE)
        color_palette_editor_frame.pack(side=tk.LEFT, padx=10, pady=5, fill=tk.BOTH, expand=True)  # Pack to the left
        tab.color_palette_editor_frame = color_palette_editor_frame  # Store reference for update_ui_for_pattern

        tk.Label(color_palette_editor_frame, text="Color Palette:", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(
            pady=10
        )

        # Frame to hold color swatches (will be dynamic)
        tab.color_swatches_container = tk.Frame(color_palette_editor_frame, bg=bg_color)
        tab.color_swatches_container.pack(pady=5, fill=tk.BOTH, expand=True)

        # Buttons for Add/Remove Color
        palette_buttons_frame = tk.Frame(color_palette_editor_frame, bg=bg_color)
        palette_buttons_frame.pack(pady=10, fill=tk.X)

        add_color_button = tk.Button(
            palette_buttons_frame,
            text="Add Color",
            command=lambda t=tab: self.add_color_to_palette(t),
            bg=active_bg_color,
            fg=fg_color,
            font=("Arial", 16),
            padx=10,
            pady=5,
            relief=tk.FLAT,
        )
        add_color_button.pack(side=tk.LEFT, expand=True, padx=5)

        remove_color_button = tk.Button(
            palette_buttons_frame,
            text="Remove Selected",
            command=lambda t=tab: self.remove_selected_color_from_palette(t),
            bg=active_bg_color,
            fg=fg_color,
            font=("Arial", 16),
            padx=10,
            pady=5,
            relief=tk.FLAT,
        )
        remove_color_button.pack(side=tk.RIGHT, expand=True, padx=5)

        # Initial population of the color palette
        self.refresh_color_palette_display(tab)

        # The initial call to update_ui_for_pattern now only sets slider values and highlights
        self.update_ui_for_pattern(tab, initial_pattern)

    def refresh_color_palette_display(self, tab):
        """Clears and repopulates the color swatches in the palette display."""
        # Clear existing swatches
        for frame in tab.color_swatch_frames:
            frame.destroy()
        tab.color_swatch_frames.clear()

        for i, hex_color in enumerate(tab.colors_in_palette):
            swatch_frame = tk.Frame(
                tab.color_swatches_container, bg=hex_color, width=100, height=50, bd=2, relief=tk.SOLID
            )
            swatch_frame.pack(pady=3, padx=5, fill=tk.X)
            # Bind click to select this color for editing
            swatch_frame.bind("<Button-1>", lambda event, idx=i, t=tab: self.select_color_in_palette(t, idx))

            # Add a label inside to make it clickable too
            swatch_label = tk.Label(
                swatch_frame,
                text=f"Color {i+1}",
                bg=hex_color,
                fg=color_utils.get_contrast_text_color(hex_color),
                font=("Arial", 14),
                width=5,
                height=3,
            )
            swatch_label.pack(expand=True, fill=tk.BOTH)
            swatch_label.bind("<Button-1>", lambda event, idx=i, t=tab: self.select_color_in_palette(t, idx))

            tab.color_swatch_frames.append(swatch_frame)

        # Re-highlight the currently selected color
        self._highlight_selected_color_swatch(tab)

    def _highlight_selected_color_swatch(self, tab):
        """Applies/removes highlight border to the selected color swatch."""
        current_index = tab.widgets["selected_color_index"]
        for i, swatch_frame in enumerate(tab.color_swatch_frames):
            if i == current_index:
                swatch_frame.config(highlightbackground=active_palette_color_border, highlightthickness=3)
            else:
                swatch_frame.config(highlightbackground=swatch_frame.cget("bg"), highlightthickness=0)  # Reset to no highlight

    def select_color_in_palette(self, tab, index: int):
        """Selects a color in the palette, updates sliders, and highlights swatch.
        This now also triggers an RGB update to the device."""
        if not (0 <= index < len(tab.colors_in_palette)):
            return

        tab.widgets["selected_color_index"] = index
        self._highlight_selected_color_swatch(tab)

        # Update RGB sliders with the selected color
        hex_color = tab.colors_in_palette[index]
        r, g, b = color_utils.hex_to_rgb(hex_color)
        tab.widgets["red_slider"].set(r)
        tab.widgets["green_slider"].set(g)
        tab.widgets["blue_slider"].set(b)

        print(f"Selected color index {index}: {hex_color}")

        # Immediately send the update, as changing the selected color implies
        # a desire to change the light's current color, regardless of pattern.
        # This will also save the settings.
        self.schedule_update_rgb(tab, force_send=True)

    def add_color_to_palette(self, tab):
        """Adds a new black color to the palette and selects it, with a limit of 10 colors."""
        MAX_COLORS = 8  # Define the maximum number of colors allowed

        if len(tab.colors_in_palette) >= MAX_COLORS:
            messagebox.showwarning("Color Limit Reached", f"You can add a maximum of {MAX_COLORS} colors to the palette.")
            return

        # Simplified: just add black. If unique colors were required globally,
        # more complex logic would be needed here.
        tab.colors_in_palette.append("#000000")  # Add black as default
        self.refresh_color_palette_display(tab)
        # Select the newly added color
        self.select_color_in_palette(tab, len(tab.colors_in_palette) - 1)
        self.save_current_tab_settings()  # Save changes to settings.json

    def remove_selected_color_from_palette(self, tab):
        """Removes the currently selected color from the palette."""
        current_index = tab.widgets["selected_color_index"]
        if len(tab.colors_in_palette) <= 1:
            messagebox.showwarning("Cannot Remove", "There must be at least one color in the palette.")
            return

        if messagebox.askyesno("Confirm Delete", f"Are you sure you want to remove Color {current_index + 1}?"):
            del tab.colors_in_palette[current_index]
            # Adjust selected index if the removed color was the last one
            if current_index >= len(tab.colors_in_palette):
                tab.widgets["selected_color_index"] = len(tab.colors_in_palette) - 1
                if tab.widgets["selected_color_index"] < 0:  # Should not happen with 1-color check
                    tab.widgets["selected_color_index"] = 0

            self.refresh_color_palette_display(tab)
            # Update sliders with the new selected color (if any)
            if tab.colors_in_palette:
                self.select_color_in_palette(tab, tab.widgets["selected_color_index"])
            else:  # If palette became empty (shouldn't happen with 1-color check)
                tab.widgets["red_slider"].set(0)
                tab.widgets["green_slider"].set(0)
                tab.widgets["blue_slider"].set(0)

            self.save_current_tab_settings()  # Save changes to settings.json

    def update_ui_for_pattern(self, tab, current_pattern: str):
        """
        Manages the state of the UI elements based on the selected pattern.
        The Color Palette Editor is always visible. RGB sliders update
        based on the currently selected color in the palette, or the first
        color if the palette is empty or not in transition mode and a new tab/pattern is selected.
        """
        # The color_palette_editor_frame is always packed, so no visibility control needed here.

        # When the pattern changes, we need to ensure the RGB sliders reflect
        # the appropriate color based on the context.

        if tab.colors_in_palette:
            # If in 'transition' mode, set sliders to the currently selected color in the palette.
            if current_pattern == "transition":
                self.select_color_in_palette(tab, tab.widgets["selected_color_index"])
            else:
                # If not in 'transition' mode, but a color is selected, update sliders to that.
                # Or, if this is a fresh load/tab change, ensure it's the first color.
                # This ensures the sliders consistently show the color that will be sent
                # for 'on'/'blink' based on the palette's first entry.
                hex_color = tab.colors_in_palette[tab.widgets["selected_color_index"]]
                r, g, b = color_utils.hex_to_rgb(hex_color)
                tab.widgets["red_slider"].set(r)
                tab.widgets["green_slider"].set(g)
                tab.widgets["blue_slider"].set(b)
                self._highlight_selected_color_swatch(tab)  # Re-highlight even if index didn't change
        else:
            # Handle empty palette scenario (shouldn't happen with default ["#000000"])
            tab.widgets["red_slider"].set(0)
            tab.widgets["green_slider"].set(0)
            tab.widgets["blue_slider"].set(0)
            tab.widgets["selected_color_index"] = 0  # Ensure index is valid
            self._highlight_selected_color_swatch(tab)

        # Brightness and Delay sliders are always visible.

    def highlight_pattern_button(self, tab_widget, active_pattern_name):
        if hasattr(tab_widget, "pattern_buttons"):
            for pattern_name, button in tab_widget.pattern_buttons.items():
                if pattern_name == active_pattern_name:
                    button.config(bg=highlight_pattern_color)
                else:
                    button.config(bg=active_bg_color)

    def on_tab_change(self, event):
        selected_tab_name = self.notebook.tab(self.notebook.select(), "text")
        current_tab_widget = self.notebook.nametowidget(self.notebook.select())

        initial_settings = self.settings["lights"][selected_tab_name]["settings"]

        # Ensure current_tab_widget has the necessary attributes
        if not hasattr(current_tab_widget, "colors_in_palette"):
            # This tab might not have been fully initialized yet, or recreated
            # In a full reload, create_tabs ensures it is.
            return

        # Update the local colors_in_palette list for the tab
        current_tab_widget.colors_in_palette = initial_settings.get("colors", ["#000000"]).copy()
        current_tab_widget.widgets["selected_color_index"] = 0  # Default to first color

        # Refresh the color palette display and select the first color
        self.refresh_color_palette_display(current_tab_widget)
        if current_tab_widget.colors_in_palette:
            self.select_color_in_palette(current_tab_widget, 0)
        else:  # If palette became empty (shouldn't happen with default ["#000000"])
            current_tab_widget.widgets["red_slider"].set(0)
            current_tab_widget.widgets["green_slider"].set(0)
            current_tab_widget.widgets["blue_slider"].set(0)

        # Update brightness and delay sliders
        current_tab_widget.widgets["brightness_slider"].set(initial_settings.get("brightness", 127))
        current_tab_widget.widgets["delay_slider"].set(initial_settings.get("delay", 0))

        # Highlight the active pattern button
        initial_pattern = initial_settings.get("pattern", "on")
        self.highlight_pattern_button(current_tab_widget, initial_pattern)

        # Update UI visibility based on the current pattern
        self.update_ui_for_pattern(current_tab_widget, initial_pattern)

    def reload_config(self):
        print("Reloading configuration...")
        self.settings = Settings()
        self.create_tabs()
        # After recreating, ensure the currently selected tab's sliders are updated
        # Trigger on_tab_change manually for the currently selected tab
        self.on_tab_change(None)

    # --- Debouncing functions (no change to core logic, just how they call update_rgb) ---
    def schedule_update_rgb(self, tab, force_send=False):
        current_time = time.time() * 1000
        if force_send:
            if self.pending_rgb_update_id:
                self.root.after_cancel(self.pending_rgb_update_id)
            self.pending_rgb_update_id = None
            self.update_rgb(tab)
            self.last_rgb_update_time = current_time
        elif current_time - self.last_rgb_update_time >= self.rgb_update_interval_ms:
            if self.pending_rgb_update_id:
                self.root.after_cancel(self.pending_rgb_update_id)
            self.pending_rgb_update_id = None
            self.update_rgb(tab)
            self.last_rgb_update_time = current_time
        else:
            if self.pending_rgb_update_id:
                self.root.after_cancel(self.pending_rgb_update_id)
            time_to_wait = int(self.rgb_update_interval_ms - (current_time - self.last_rgb_update_time))
            self.pending_rgb_update_id = self.root.after(time_to_wait, lambda: self.update_rgb(tab))

    def schedule_update_brightness(self, tab, force_send=False):
        current_time = time.time() * 1000
        if force_send:
            if self.pending_brightness_update_id:
                self.root.after_cancel(self.pending_brightness_update_id)
            self.pending_brightness_update_id = None
            self.update_brightness(tab)
            self.last_brightness_update_time = current_time
        elif current_time - self.last_brightness_update_time >= self.brightness_update_interval_ms:
            if self.pending_brightness_update_id:
                self.root.after_cancel(self.pending_brightness_update_id)
            self.pending_brightness_update_id = None
            self.update_brightness(tab)
            self.last_brightness_update_time = current_time
        else:
            if self.pending_brightness_update_id:
                self.root.after_cancel(self.pending_brightness_update_id)
            time_to_wait = int(self.brightness_update_interval_ms - (current_time - self.last_brightness_update_time))
            self.pending_brightness_update_id = self.root.after(time_to_wait, lambda: self.update_brightness(tab))

    def schedule_update_delay(self, tab, force_send=False):
        current_time = time.time() * 1000
        if force_send:
            if self.pending_delay_update_id:
                self.root.after_cancel(self.pending_delay_update_id)
            self.pending_delay_update_id = None
            self.update_delay(tab)
            self.last_delay_update_time = current_time
        elif current_time - self.last_delay_update_time >= self.delay_update_interval_ms:
            if self.pending_delay_update_id:
                self.root.after_cancel(self.pending_delay_update_id)
            self.pending_delay_update_id = None
            self.update_delay(tab)
            self.last_delay_update_time = current_time
        else:
            if self.pending_delay_update_id:
                self.root.after_cancel(self.pending_delay_update_id)
            time_to_wait = int(self.delay_update_interval_ms - (current_time - self.last_delay_update_time))
            self.pending_delay_update_id = self.root.after(time_to_wait, lambda: self.update_delay(tab))

    # --- Asynchronous Update Functions ---
    @async_handler
    async def update_rgb(self, tab):
        """Update the currently selected color in the palette and send to the server."""
        try:
            red_slider = tab.widgets["red_slider"]
            green_slider = tab.widgets["green_slider"]
            blue_slider = tab.widgets["blue_slider"]

            r = red_slider.get()
            g = green_slider.get()
            b = blue_slider.get()

            hex_color = f"#{r:02x}{g:02x}{b:02x}"
            print(f"Updating selected color to: {hex_color}")

            selected_color_index = tab.widgets["selected_color_index"]
            if 0 <= selected_color_index < len(tab.colors_in_palette):
                tab.colors_in_palette[selected_color_index] = hex_color
                self.refresh_color_palette_display(tab)  # Update swatch immediately

            selected_server = self.notebook.tab(self.notebook.select(), "text")
            names = self.settings["lights"][selected_server]["names"]

            # Determine which colors to send based on the current pattern.
            current_pattern = self.settings["lights"][selected_server]["settings"].get("pattern", "on")
            colors_to_send = []

            if current_pattern == "transition":
                colors_to_send = tab.colors_in_palette.copy()
            elif current_pattern in ["on", "blink", "theater_chase", "flicker"]:  # Add other patterns that use a single color
                if tab.colors_in_palette:
                    # For non-transition patterns, the device typically uses only the first color.
                    # However, if a user picks a color from the palette, we want THAT color to be the one
                    # sent and active. So, the selected color from the palette *becomes* the first color
                    # in the list we send to the device for these modes.
                    # This ensures the light matches the selected palette color.
                    colors_to_send = [hex_color]  # Send the color currently set by the sliders
                else:
                    colors_to_send = ["#000000"]  # Default if palette is empty
            else:  # For other patterns like "off", "rainbow" where colors might not be primary
                # We still want to send the *current* palette state for saving,
                # but the device firmware might ignore it for these patterns.
                colors_to_send = tab.colors_in_palette.copy()

            payload = {
                "save": True,  # Always save this change to config
                "names": names,
                "settings": {
                    "colors": colors_to_send,  # This now dynamically changes based on pattern
                    "brightness": tab.widgets["brightness_slider"].get(),
                    "delay": tab.widgets["delay_slider"].get(),
                    "pattern": current_pattern,  # Always send the current pattern
                },
            }

            # Update the settings object with the new color list (and potentially other synced values)
            self.settings["lights"][selected_server]["settings"]["colors"] = tab.colors_in_palette.copy()
            self.settings["lights"][selected_server]["settings"]["brightness"] = tab.widgets["brightness_slider"].get()
            self.settings["lights"][selected_server]["settings"]["delay"] = tab.widgets["delay_slider"].get()
            self.settings.save()

            await self.websocket_client.send_data(payload)
            print(f"Sent RGB payload: {payload}")

        except Exception as e:
            print(f"Error updating RGB: {e}")

    @async_handler
    async def update_brightness(self, tab):
        try:
            brightness_slider = tab.widgets["brightness_slider"]
            brightness = brightness_slider.get()
            print(f"Brightness: {brightness}")

            selected_server = self.notebook.tab(self.notebook.select(), "text")
            names = self.settings["lights"][selected_server]["names"]

            payload = {
                "save": True,
                "names": names,
                "settings": {
                    "brightness": brightness,
                },
            }
            # Update the settings object with the new brightness
            self.settings["lights"][selected_server]["settings"]["brightness"] = brightness
            self.settings.save()
            await self.websocket_client.send_data(payload)
            print(f"Sent brightness payload: {payload}")
        except Exception as e:
            print(f"Error updating brightness: {e}")

    @async_handler
    async def update_delay(self, tab):
        try:
            delay_slider = tab.widgets["delay_slider"]
            delay = delay_slider.get()
            print(f"Delay: {delay}")

            selected_server = self.notebook.tab(self.notebook.select(), "text")
            names = self.settings["lights"][selected_server]["names"]
            payload = {
                "save": True,
                "names": names,
                "settings": {
                    "delay": delay,
                },
            }
            # Update the settings object with the new delay
            self.settings["lights"][selected_server]["settings"]["delay"] = delay
            self.settings.save()
            await self.websocket_client.send_data(payload)
            print(f"Sent delay payload: {payload}")
        except Exception as e:
            print(f"Error updating delay: {e}")

    @async_handler
    async def send_pattern(self, tab_name: str, pattern_name: str):
        try:
            names = self.settings["lights"][tab_name]["names"]
            # Get the actual tab widget to access its `colors_in_palette` and other attributes
            current_tab_widget = None
            for key, tab_widget in self.tabs.items():
                if key == tab_name:
                    current_tab_widget = tab_widget
                    break

            if not current_tab_widget:
                print(f"Error: Could not find tab widget for {tab_name}")
                return

            current_settings_for_tab = self.settings["lights"][tab_name]["settings"]

            payload_settings = {
                "pattern": pattern_name,
                "brightness": current_settings_for_tab.get("brightness", 127),
                "delay": current_settings_for_tab.get("delay", 0),
            }

            # Determine colors to send based on the *newly selected* pattern
            if pattern_name == "transition":
                payload_settings["colors"] = current_tab_widget.colors_in_palette.copy()
            elif pattern_name in ["on", "blink"]:  # Add other patterns that use a single color here
                # When switching TO 'on' or 'blink', ensure the color sent is the one
                # currently displayed on the sliders (which reflects the selected palette color).
                r = current_tab_widget.widgets["red_slider"].get()
                g = current_tab_widget.widgets["green_slider"].get()
                b = current_tab_widget.widgets["blue_slider"].get()
                hex_color_from_sliders = f"#{r:02x}{g:02x}{b:02x}"
                payload_settings["colors"] = [hex_color_from_sliders]
            else:
                # For other patterns, send the full palette, device might ignore or use default
                payload_settings["colors"] = current_tab_widget.colors_in_palette.copy()

            payload = {
                "save": True,
                "names": names,
                "settings": payload_settings,
            }

            # Update the settings object with the new pattern and current colors/brightness/delay
            self.settings["lights"][tab_name]["settings"]["pattern"] = pattern_name
            # Always save the full current palette state in settings.
            self.settings["lights"][tab_name]["settings"]["colors"] = current_tab_widget.colors_in_palette.copy()
            self.settings.save()

            self.highlight_pattern_button(current_tab_widget, pattern_name)
            self.update_ui_for_pattern(current_tab_widget, pattern_name)  # Update UI based on new pattern

            await self.websocket_client.send_data(payload)
            print(f"Sent pattern payload: {payload}")
        except Exception as e:
            print(f"Error sending pattern: {e}")

    def save_current_tab_settings(self):
        """Saves the current state of the active tab's settings (colors, brightness, delay, pattern) to config."""
        selected_server = self.notebook.tab(self.notebook.select(), "text")
        current_tab_widget = self.notebook.nametowidget(self.notebook.select())

        if not hasattr(current_tab_widget, "colors_in_palette"):
            return  # Tab not fully initialized yet

        # Update settings for the current tab in the self.settings object
        self.settings["lights"][selected_server]["settings"]["colors"] = current_tab_widget.colors_in_palette
        self.settings["lights"][selected_server]["settings"]["brightness"] = current_tab_widget.widgets["brightness_slider"].get()
        self.settings["lights"][selected_server]["settings"]["delay"] = current_tab_widget.widgets["delay_slider"].get()
        # The pattern is updated in send_pattern already, but ensure consistency
        # For simplicity, we assume send_pattern is the primary way to change pattern.

        self.settings.save()
        print(f"Saved settings for {selected_server}")


if __name__ == "__main__":
    app = App()