#!/usr/bin/python3

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import subprocess
import os
import sys
import re
import getpass


class FirstBootApp:
    def __init__(self, root):
        self.root = root
        self.root.title("RX30 Configuration")
        self.root.geometry("800x750")
        self.password = None
        self.network_interfaces = []

        # Create notebook for tabs
        self.notebook = ttk.Notebook(root)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Create tabs
        self.auth_frame = ttk.Frame(self.notebook)
        self.network_frame = ttk.Frame(self.notebook)
        self.ssh_frame = ttk.Frame(self.notebook)
        self.scanners_frame = ttk.Frame(self.notebook)  # New Scanners tab
        self.printers_frame = ttk.Frame(self.notebook)  # Renamed from scanner_frame to printers_frame

        self.notebook.add(self.auth_frame, text="1. Authentication")
        self.notebook.add(self.network_frame, text="2. Network Config")
        self.notebook.add(self.ssh_frame, text="3. SSH Config")
        self.notebook.add(self.scanners_frame, text="4. Scanners")  # New tab
        self.notebook.add(self.printers_frame, text="5. Printers")  # Renamed tab

        self.setup_auth_tab()
        self.setup_network_tab()
        self.setup_ssh_tab()
        self.setup_scanners_tab()  # New method for scanners
        self.setup_printers_tab()  # Renamed method for printers

        # Disable tabs until authenticated
        self.notebook.tab(1, state="disabled")
        self.notebook.tab(2, state="disabled")
        self.notebook.tab(3, state="disabled")
        self.notebook.tab(4, state="disabled")

        # Track changes for auto-generation
        self.setup_auto_generation()

        # Bind tab change event
        self.notebook.bind('<<NotebookTabChanged>>', self.on_tab_changed)

    def on_tab_changed(self, event):
        """Handle tab change events"""
        current_tab = self.notebook.index(self.notebook.select())

        # If Scanners or Printers tab is selected, auto-refresh status
        if current_tab == 3:  # Scanners tab
            self.refresh_scanner_services()
            self.refresh_scanner_devices()  # Refresh scanner devices when tab is selected
        elif current_tab == 4:  # Printers tab
            self.refresh_printer_services()
            # Only check CUPS status if authenticated
            if self.password is not None:
                self.check_cups_status()
            else:
                self.cups_status_label.config(text="Please authenticate to check CUPS status")

    def detect_network_interfaces(self):
        """Detect available network interfaces with detailed information"""
        interfaces = []

        try:
            # Method 1: Using ip command (most reliable)
            result = subprocess.run(['ip', 'addr', 'show'], capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                current_interface = None
                for line in result.stdout.split('\n'):
                    # Look for interface lines (e.g., "2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP>")
                    if re.match(r'^\d+:', line):
                        parts = line.split(':')
                        if len(parts) >= 2:
                            current_interface = parts[1].strip()
                            if current_interface and current_interface != 'lo':  # Skip loopback
                                interfaces.append(current_interface)

            # If ip command failed, try alternative methods
            if not interfaces:
                # Method 2: Using /sys/class/net
                if os.path.exists('/sys/class/net'):
                    for item in os.listdir('/sys/class/net'):
                        if item != 'lo':  # Skip loopback
                            interfaces.append(item)

                # Method 3: Using ifconfig
                if not interfaces:
                    result = subprocess.run(['ifconfig', '-a'], capture_output=True, text=True, timeout=5)
                    if result.returncode == 0:
                        for line in result.stdout.split('\n'):
                            if line and not line.startswith(' ') and ':' in line:
                                interface = line.split(':')[0]
                                if interface and interface != 'lo':
                                    interfaces.append(interface)

            # Remove duplicates and sort
            interfaces = sorted(list(set(interfaces)))

            # Get interface details for display
            detailed_interfaces = []
            for interface in interfaces:
                details = self.get_interface_details(interface)
                detailed_interfaces.append(details)

            return detailed_interfaces

        except Exception as e:
            print(f"Error detecting interfaces: {e}")
            # Fallback to common interface names
            return [
                {"name": "eth0", "status": "Unknown", "mac": "N/A", "ip": "N/A"},
                {"name": "wlan0", "status": "Unknown", "mac": "N/A", "ip": "N/A"},
                {"name": "enp0s3", "status": "Unknown", "mac": "N/A", "ip": "N/A"},
                {"name": "wlp2s0", "status": "Unknown", "mac": "N/A", "ip": "N/A"}
            ]

    def get_interface_details(self, interface):
        """Get detailed information about a network interface"""
        details = {
            "name": interface,
            "status": "Down",
            "mac": "N/A",
            "ip": "N/A",
            "subnet": "N/A",
            "gateway": "N/A",
            "dns": "N/A"
        }

        try:
            # Get interface status
            operstate_path = f"/sys/class/net/{interface}/operstate"
            if os.path.exists(operstate_path):
                with open(operstate_path, 'r') as f:
                    details["status"] = f.read().strip().capitalize()

            # Get MAC address
            address_path = f"/sys/class/net/{interface}/address"
            if os.path.exists(address_path):
                with open(address_path, 'r') as f:
                    details["mac"] = f.read().strip()

            # Get IP address and subnet using ip command
            result = subprocess.run(['ip', 'addr', 'show', interface], capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                for line in result.stdout.split('\n'):
                    if 'inet ' in line:
                        ip_match = re.search(r'inet (\d+\.\d+\.\d+\.\d+)/(\d+)', line)
                        if ip_match:
                            details["ip"] = ip_match.group(1)
                            # Convert CIDR to subnet mask
                            cidr = int(ip_match.group(2))
                            details["subnet"] = self.cidr_to_subnet(cidr)
                            break

            # Get gateway
            result = subprocess.run(['ip', 'route', 'show', 'default'], capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                for line in result.stdout.split('\n'):
                    if 'default via' in line and f'dev {interface}' in line:
                        gateway_match = re.search(r'default via (\d+\.\d+\.\d+\.\d+)', line)
                        if gateway_match:
                            details["gateway"] = gateway_match.group(1)
                            break

            # Get DNS servers from resolv.conf
            if os.path.exists('/etc/resolv.conf'):
                with open('/etc/resolv.conf', 'r') as f:
                    dns_servers = []
                    for line in f:
                        if line.startswith('nameserver'):
                            dns_match = re.search(r'nameserver\s+(\S+)', line)
                            if dns_match:
                                dns_servers.append(dns_match.group(1))
                    if dns_servers:
                        details["dns"] = ', '.join(dns_servers)

        except Exception as e:
            print(f"Error getting details for {interface}: {e}")

        return details

    def cidr_to_subnet(self, cidr):
        """Convert CIDR notation to subnet mask"""
        if 0 <= cidr <= 32:
            mask = (0xffffffff >> (32 - cidr)) << (32 - cidr)
            return '.'.join([str((mask >> 24) & 0xff), str((mask >> 16) & 0xff),
                             str((mask >> 8) & 0xff), str(mask & 0xff)])
        return "255.255.255.0"  # Default

    def get_current_ssh_config(self):
        """Read current SSH configuration from /usr/local/bin/myscript.sh"""
        config = {
            "ssh_server": "",
            "ssh_port": "22",
            "ssh_user": "",
            "ssh_pass": "",
            "remote_program": "/usr/bin/xterm"
        }

        try:
            if os.path.exists('/usr/local/bin/myscript.sh'):
                with open('/usr/local/bin/myscript.sh', 'r') as f:
                    content = f.read()

                # Extract SSH_SERVER - match both quoted and unquoted values
                server_match = re.search(r'SSH_SERVER=([^\s\'"]+|"[^"]*"|\'[^\']*\')', content)
                if server_match:
                    server_value = server_match.group(1)
                    # Remove quotes if present
                    if (server_value.startswith('"') and server_value.endswith('"')) or \
                            (server_value.startswith("'") and server_value.endswith("'")):
                        server_value = server_value[1:-1]
                    config["ssh_server"] = server_value

                # Extract SSH_PORT
                port_match = re.search(r'SSH_PORT=([^\s\'"]+|"[^"]*"|\'[^\']*\')', content)
                if port_match:
                    port_value = port_match.group(1)
                    if (port_value.startswith('"') and port_value.endswith('"')) or \
                            (port_value.startswith("'") and port_value.endswith("'")):
                        port_value = port_value[1:-1]
                    config["ssh_port"] = port_value

                # Extract SSH_USER
                user_match = re.search(r'SSH_USER=([^\s\'"]+|"[^"]*"|\'[^\']*\')', content)
                if user_match:
                    user_value = user_match.group(1)
                    if (user_value.startswith('"') and user_value.endswith('"')) or \
                            (user_value.startswith("'") and user_value.endswith("'")):
                        user_value = user_value[1:-1]
                    config["ssh_user"] = user_value

                # Extract SSH_PASS
                pass_match = re.search(r'SSH_PASS=([^\s\'"]+|"[^"]*"|\'[^\']*\')', content)
                if pass_match:
                    pass_value = pass_match.group(1)
                    if (pass_value.startswith('"') and pass_value.endswith('"')) or \
                            (pass_value.startswith("'") and pass_value.endswith("'")):
                        pass_value = pass_value[1:-1]
                    config["ssh_pass"] = pass_value

                # Extract remote program from ssh command - improved regex
                # Look for the pattern: sshpass -p "$SSH_PASS" ssh ... "program"
                program_match = re.search(r'sshpass\s+-p\s+"\$\w+"\s+ssh[^"]*"\s*([^"]+)"', content)
                if not program_match:
                    # Alternative pattern: ssh ... "program"
                    program_match = re.search(r'ssh[^"]*"\s*([^"]+)"', content)
                if program_match:
                    config["remote_program"] = program_match.group(1).strip()

        except Exception as e:
            print(f"Error reading SSH config: {e}")

        return config

    def get_current_scanner_subnets(self):
        """Read current scanner subnets from /etc/sane.d/saned.conf"""
        subnets = []
        try:
            if os.path.exists('/etc/sane.d/saned.conf'):
                with open('/etc/sane.d/saned.conf', 'r') as f:
                    for line in f:
                        line = line.strip()
                        # Skip empty lines and comments
                        if line and not line.startswith('#'):
                            # Check if it looks like an IP/subnet
                            if re.match(r'^\d+\.\d+\.\d+\.\d+(\/\d+)?$', line):
                                subnets.append(line)
        except Exception as e:
            print(f"Error reading scanner subnets: {e}")
        return subnets

    def refresh_scanner_aliases(self):
        """Refresh and display current scanner aliases in listbox"""
        aliases = self.get_current_scanner_aliases()

        # Clear listbox
        self.aliases_listbox.delete(0, tk.END)

        if aliases:
            for alias in aliases:
                self.aliases_listbox.insert(tk.END, alias)
        else:
            self.aliases_listbox.insert(tk.END, "No aliases configured")

    def get_current_scanner_aliases(self):
        """Read current scanner aliases from /etc/sane.d/dll.aliases"""
        aliases = []
        try:
            if os.path.exists('/etc/sane.d/dll.aliases'):
                with open('/etc/sane.d/dll.aliases', 'r') as f:
                    for line in f:
                        line = line.strip()
                        # Skip empty lines and comments
                        if line and not line.startswith('#'):
                            aliases.append(line)
        except Exception as e:
            print(f"Error reading scanner aliases: {e}")
        return aliases

    def refresh_interfaces(self):
        """Refresh the network interfaces list"""
        self.network_interfaces = self.detect_network_interfaces()

        # Update the combobox
        interface_names = [iface["name"] for iface in self.network_interfaces]
        self.interface_combo['values'] = interface_names

        # Update the details display
        self.update_interface_details()

        # Select the first interface by default if none selected
        if interface_names and not self.interface_var.get():
            self.interface_combo.set(interface_names[0])
            self.update_interface_details()

    def update_interface_details(self):
        """Update the interface details display"""
        selected_interface = self.interface_var.get()
        if selected_interface:
            for iface in self.network_interfaces:
                if iface["name"] == selected_interface:
                    # Update details label
                    details_text = f"Status: {iface['status']}"
                    self.interface_details_label.config(text=details_text)

                    # Update current configuration display
                    current_config_text = f"Current Configuration:\n"
                    current_config_text += f"IP: {iface['ip']}\n"
                    current_config_text += f"Subnet: {iface['subnet']}\n"
                    current_config_text += f"Gateway: {iface['gateway']}\n"
                    current_config_text += f"DNS: {iface['dns']}"
                    self.current_config_label.config(text=current_config_text)

                    # Auto-fill IP if available and not manually set
                    current_ip = self.ip_entry.get().strip()
                    if iface["ip"] != "N/A" and (not current_ip or current_ip == "192.168.1.100"):
                        self.ip_entry.delete(0, tk.END)
                        # Use the same network but different host part
                        ip_parts = iface["ip"].split('.')
                        if len(ip_parts) == 4:
                            new_ip = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.100"
                            self.ip_entry.insert(0, new_ip)

                    # Auto-fill other fields if they are empty
                    if iface["subnet"] != "N/A" and not self.subnet_entry.get().strip():
                        self.subnet_entry.delete(0, tk.END)
                        self.subnet_entry.insert(0, iface["subnet"])

                    if iface["gateway"] != "N/A" and not self.gateway_entry.get().strip():
                        self.gateway_entry.delete(0, tk.END)
                        self.gateway_entry.insert(0, iface["gateway"])

                    if iface["dns"] != "N/A" and not self.dns_entry.get().strip():
                        self.dns_entry.delete(0, tk.END)
                        self.dns_entry.insert(0, iface["dns"])

                    break
        else:
            self.interface_details_label.config(text="Select an interface to view details")
            self.current_config_label.config(text="Select an interface to view current configuration")

    def setup_auto_generation(self):
        # Bind all SSH entry fields to auto-update
        ssh_fields = [
            self.ssh_server_entry,
            self.ssh_port_entry,
            self.ssh_user_entry,
            self.ssh_pass_entry,
            self.remote_program_entry
        ]

        for field in ssh_fields:
            field.bind('<KeyRelease>', self.update_ssh_status)
            field.bind('<FocusOut>', self.update_ssh_status)

    def setup_auth_tab(self):
        """Authentication tab"""
        frame = ttk.LabelFrame(self.auth_frame, text="Root Authentication", padding="15")
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        ttk.Label(frame, text="Enter root password to configure system:").pack(pady=10)

        ttk.Label(frame, text="Root Password:").pack(pady=5)
        self.password_entry = ttk.Entry(frame, show="•", width=30)
        self.password_entry.pack(pady=5)
        self.password_entry.bind('<Return>', self.authenticate)
        self.password_entry.focus_set()

        ttk.Button(frame, text="Authenticate", command=self.authenticate).pack(pady=10)

        self.auth_status = ttk.Label(frame, text="Not authenticated", foreground="red")
        self.auth_status.pack(pady=5)

        help_text = """Note: You need root privileges to configure system settings.
        Enter the root password to continue."""
        ttk.Label(frame, text=help_text, foreground="gray", font=('Arial', 9)).pack(pady=10)

    def toggle_static_fields(self):
        """Enable or disable static IP configuration fields based on configuration type"""
        if self.config_type.get() == "static":
            # Enable all static IP fields
            self.ip_entry.config(state="normal")
            self.subnet_entry.config(state="normal")
            self.gateway_entry.config(state="normal")
            self.dns_entry.config(state="normal")
        else:
            # Disable all static IP fields for DHCP
            self.ip_entry.config(state="disabled")
            self.subnet_entry.config(state="disabled")
            self.gateway_entry.config(state="disabled")
            self.dns_entry.config(state="disabled")

    def setup_network_tab(self):
        # Network configuration tab
        frame = ttk.LabelFrame(self.network_frame, text="Network Configuration", padding="15")
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Interface selection with refresh button
        interface_frame = ttk.Frame(frame)
        interface_frame.grid(row=0, column=0, columnspan=3, sticky=tk.W + tk.E, pady=10)

        ttk.Label(interface_frame, text="Network Interface:", font=('Arial', 9, 'bold')).pack(side=tk.LEFT,
                                                                                              padx=(0, 10))

        self.interface_var = tk.StringVar()
        self.interface_combo = ttk.Combobox(interface_frame, textvariable=self.interface_var, width=15,
                                            state="readonly")
        self.interface_combo.pack(side=tk.LEFT, padx=(0, 10))
        self.interface_combo.bind('<<ComboboxSelected>>', lambda e: self.update_interface_details())

        ttk.Button(interface_frame, text="Refresh", command=self.refresh_interfaces, width=8).pack(side=tk.LEFT,
                                                                                                   padx=(0, 10))

        # Interface details
        self.interface_details_label = ttk.Label(interface_frame, text="Click Refresh to detect interfaces",
                                                 foreground="green")
        self.interface_details_label.pack(side=tk.LEFT)

        # Current configuration display
        self.current_config_label = ttk.Label(frame, text="Select an interface to view current configuration",
                                              justify=tk.LEFT, background='#f5f5f5', relief=tk.SUNKEN, padding=5)
        self.current_config_label.grid(row=1, column=0, columnspan=3, sticky=tk.W + tk.E, pady=10, padx=5)

        # IP Address
        ttk.Label(frame, text="IP Address:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.ip_entry = ttk.Entry(frame, width=20)
        self.ip_entry.grid(row=2, column=1, sticky=tk.W, pady=5, padx=5)
        self.ip_entry.insert(0, "192.168.1.100")

        # Subnet Mask
        ttk.Label(frame, text="Subnet Mask:").grid(row=3, column=0, sticky=tk.W, pady=5)
        self.subnet_entry = ttk.Entry(frame, width=20)
        self.subnet_entry.grid(row=3, column=1, sticky=tk.W, pady=5, padx=5)
        self.subnet_entry.insert(0, "255.255.255.0")

        # Gateway
        ttk.Label(frame, text="Gateway:").grid(row=4, column=0, sticky=tk.W, pady=5)
        self.gateway_entry = ttk.Entry(frame, width=20)
        self.gateway_entry.grid(row=4, column=1, sticky=tk.W, pady=5, padx=5)
        self.gateway_entry.insert(0, "192.168.1.1")

        # DNS
        ttk.Label(frame, text="DNS Servers:").grid(row=5, column=0, sticky=tk.W, pady=5)
        self.dns_entry = ttk.Entry(frame, width=20)
        self.dns_entry.grid(row=5, column=1, sticky=tk.W, pady=5, padx=5)
        self.dns_entry.insert(0, "8.8.8.8,8.8.4.4")

        # Configuration type
        ttk.Label(frame, text="Configuration Type:").grid(row=6, column=0, sticky=tk.W, pady=5)
        self.config_type = tk.StringVar(value="static")

        # Create radio buttons and bind the toggle function
        static_rb = ttk.Radiobutton(frame, text="Static IP", variable=self.config_type, value="static",
                                    command=self.toggle_static_fields)
        static_rb.grid(row=6, column=1, sticky=tk.W, padx=5)

        dhcp_rb = ttk.Radiobutton(frame, text="DHCP", variable=self.config_type, value="dhcp",
                                  command=self.toggle_static_fields)
        dhcp_rb.grid(row=6, column=1, sticky=tk.W, padx=80)

        # Buttons
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=7, column=0, columnspan=3, pady=15)

        ttk.Button(button_frame, text="Apply Changes", command=self.configure_network).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Detect Interfaces", command=self.refresh_interfaces).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Test Connection", command=self.test_network).pack(side=tk.LEFT, padx=5)

        self.network_status = ttk.Label(frame, text="Click 'Detect Interfaces' to find available network interfaces")
        self.network_status.grid(row=8, column=0, columnspan=3)

        # Configure grid weights
        frame.columnconfigure(1, weight=1)

        # Initial interface detection
        self.refresh_interfaces()

    def test_network(self):
        """Test network connectivity"""
        try:
            # Test internet connectivity
            result = subprocess.run(['ping', '-c', '3', '8.8.8.8'], capture_output=True, text=True, timeout=10)
            if result.returncode == 0:
                messagebox.showinfo("Network Test", "Network connectivity test successful!")
            else:
                messagebox.showwarning("Network Test", "Network connectivity test failed. Check your configuration.")
        except Exception as e:
            messagebox.showerror("Network Test", f"Network test error: {str(e)}")

    def setup_ssh_tab(self):
        # SSH configuration tab
        frame = ttk.LabelFrame(self.ssh_frame, text="SSH Connection Settings", padding="15")
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Current SSH configuration display
        current_ssh_frame = ttk.LabelFrame(frame, text="Current SSH Configuration", padding="10")
        current_ssh_frame.grid(row=0, column=0, columnspan=3, sticky=tk.W + tk.E, pady=10)

        self.current_ssh_label = ttk.Label(current_ssh_frame, text="Loading current configuration...",
                                           justify=tk.LEFT, background='#f5f5f5', relief=tk.SUNKEN, padding=5)
        self.current_ssh_label.pack(fill=tk.X, padx=5, pady=5)

        # Refresh current config button
        ttk.Button(current_ssh_frame, text="Refresh Current Config", command=self.refresh_ssh_config).pack(pady=5)

        # Instructions
        ttk.Label(frame, text="Enter SSH connection details:",
                  font=('Arial', 10, 'bold')).grid(row=1, column=0, columnspan=3, sticky=tk.W, pady=(0, 15))

        # SSH Server
        ttk.Label(frame, text="SSH Server:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.ssh_server_entry = ttk.Entry(frame, width=25)
        self.ssh_server_entry.grid(row=2, column=1, sticky=tk.W, pady=5, padx=5)

        # SSH Port
        ttk.Label(frame, text="SSH Port:").grid(row=3, column=0, sticky=tk.W, pady=5)
        self.ssh_port_entry = ttk.Entry(frame, width=25)
        self.ssh_port_entry.grid(row=3, column=1, sticky=tk.W, pady=5, padx=5)

        # SSH Username
        ttk.Label(frame, text="SSH Username:").grid(row=4, column=0, sticky=tk.W, pady=5)
        self.ssh_user_entry = ttk.Entry(frame, width=25)
        self.ssh_user_entry.grid(row=4, column=1, sticky=tk.W, pady=5, padx=5)

        # SSH Password
        ttk.Label(frame, text="SSH Password:").grid(row=5, column=0, sticky=tk.W, pady=5)
        self.ssh_pass_entry = ttk.Entry(frame, show="•", width=25)
        self.ssh_pass_entry.grid(row=5, column=1, sticky=tk.W, pady=5, padx=5)

        # Program to execute
        ttk.Label(frame, text="Remote Program:").grid(row=6, column=0, sticky=tk.W, pady=5)
        self.remote_program_entry = ttk.Entry(frame, width=25)
        self.remote_program_entry.grid(row=6, column=1, sticky=tk.W, pady=5, padx=5)

        # RX30 Desktop Icon Option
        rx30_frame = ttk.LabelFrame(frame, text="RX30 Desktop Integration", padding="10")
        rx30_frame.grid(row=7, column=0, columnspan=3, sticky=tk.W + tk.E, pady=10)

        self.rx30_icon_var = tk.BooleanVar()
        self.rx30_icon_manually_changed = False  # Track if user manually changed the checkbox

        def on_rx30_checkbox_change(*args):
            self.rx30_icon_manually_changed = True
            self.update_rx30_status_display()

        self.rx30_icon_var.trace('w', on_rx30_checkbox_change)

        self.rx30_checkbutton = ttk.Checkbutton(rx30_frame, text="Enable RX30 Desktop Icon",
                                                variable=self.rx30_icon_var)
        self.rx30_checkbutton.pack(anchor=tk.W, pady=5)

        # RX30 status display
        self.rx30_status_label = ttk.Label(rx30_frame, text="Checking desktop icon status...")
        self.rx30_status_label.pack(anchor=tk.W, pady=5)

        # Buttons
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=8, column=0, columnspan=3, pady=15)

        ttk.Button(button_frame, text="Test SSH Connection", command=self.test_ssh).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Apply Changes", command=self.apply_ssh_changes).pack(side=tk.LEFT, padx=5)

        self.ssh_status = ttk.Label(frame, text="")
        self.ssh_status.grid(row=9, column=0, columnspan=3)

        # Load current SSH configuration and check desktop icon status
        self.refresh_ssh_config()
        self.check_rx30_icon_status()

    def update_rx30_status_display(self):
        """Update the status display based on checkbox state and actual icon existence"""
        try:
            current_user = getpass.getuser()
            desktop_path = os.path.expanduser(f"~{current_user}/Desktop/rx30.desktop")
            applications_path = '/usr/share/applications/rx30.desktop'

            # Check if either desktop file exists
            desktop_exists = os.path.exists(desktop_path)
            applications_exists = os.path.exists(applications_path)
            icon_exists = desktop_exists or applications_exists

            if self.rx30_icon_var.get():
                if icon_exists:
                    self.rx30_status_label.config(text="RX30 desktop icon exists", foreground="green")
                else:
                    self.rx30_status_label.config(text="RX30 desktop icon will be created when changes are applied",
                                                  foreground="blue")
            else:
                if icon_exists:
                    self.rx30_status_label.config(
                        text="RX30 desktop icon exists - will be removed when changes are applied", foreground="orange")
                else:
                    self.rx30_status_label.config(text="No RX30 desktop icon", foreground="black")

        except Exception as e:
            print(f"Error updating RX30 status display: {e}")
            self.rx30_status_label.config(text="Error checking icon status", foreground="red")

    def check_rx30_icon_status(self):
        """Check if RX30 desktop icon already exists and update status label (but don't force checkbox state)"""
        try:
            current_user = getpass.getuser()
            desktop_path = os.path.expanduser(f"~{current_user}/Desktop/rx30.desktop")
            applications_path = '/usr/share/applications/rx30.desktop'

            # Check if either desktop file exists
            desktop_exists = os.path.exists(desktop_path)
            applications_exists = os.path.exists(applications_path)
            icon_exists = desktop_exists or applications_exists

            # Only update the checkbox if we're initializing or if it's currently in a neutral state
            # Don't override user's manual selection
            current_state = self.rx30_icon_var.get()
            if current_state == icon_exists:
                # States match, no need to change
                pass
            elif not self.rx30_icon_manually_changed:  # Only auto-set on first load
                self.rx30_icon_var.set(icon_exists)

            if icon_exists:
                status_text = "RX30 desktop icon exists"
                if not self.rx30_icon_var.get():
                    status_text += " - will be removed when changes are applied"
                self.rx30_status_label.config(text=status_text, foreground="green")
            else:
                status_text = "No RX30 desktop icon found"
                if self.rx30_icon_var.get():
                    status_text += " - will be created when changes are applied"
                self.rx30_status_label.config(text=status_text, foreground="black")

        except Exception as e:
            print(f"Error checking RX30 icon status: {e}")
            self.rx30_status_label.config(text="Error checking icon status", foreground="red")

    def remove_rx30_desktop_icon(self):
        """Remove RX30 desktop icon from both locations"""
        try:
            current_user = getpass.getuser()
            desktop_path = os.path.expanduser(f"~{current_user}/Desktop/rx30.desktop")
            applications_path = '/usr/share/applications/rx30.desktop'

            removed_locations = []

            # Remove user desktop file
            if os.path.exists(desktop_path):
                os.remove(desktop_path)
                removed_locations.append("user desktop")

            # Remove system application file if we have root access
            if self.password and os.path.exists(applications_path):
                cmd = ['sudo', '-S', 'rm', '-f', applications_path]
                result = subprocess.run(cmd, input=f"{self.password}\n",
                                        capture_output=True, text=True, timeout=10)
                if result.returncode == 0:
                    removed_locations.append("system applications")

            if removed_locations:
                self.rx30_status_label.config(
                    text=f"RX30 desktop icon removed from: {', '.join(removed_locations)}",
                    foreground="green"
                )
                return True
            else:
                self.rx30_status_label.config(text="No RX30 desktop icon found to remove", foreground="orange")
                return True

        except Exception as e:
            self.rx30_status_label.config(text=f"Error removing RX30 icon: {str(e)}", foreground="red")
            return False

    def refresh_ssh_config(self):
        """Refresh and display current SSH configuration"""
        config = self.get_current_ssh_config()

        # Update display
        display_text = f"SSH Server: {config['ssh_server']}\n"
        display_text += f"SSH Port: {config['ssh_port']}\n"
        display_text += f"SSH Username: {config['ssh_user']}\n"
        display_text += f"SSH Password: {'*' * len(config['ssh_pass']) if config['ssh_pass'] else 'Not set'}\n"
        display_text += f"Remote Program: {config['remote_program']}"

        self.current_ssh_label.config(text=display_text)

        # Update entry fields
        self.ssh_server_entry.delete(0, tk.END)
        self.ssh_server_entry.insert(0, config['ssh_server'])

        self.ssh_port_entry.delete(0, tk.END)
        self.ssh_port_entry.insert(0, config['ssh_port'])

        self.ssh_user_entry.delete(0, tk.END)
        self.ssh_user_entry.insert(0, config['ssh_user'])

        self.ssh_pass_entry.delete(0, tk.END)
        self.ssh_pass_entry.insert(0, config['ssh_pass'])

        self.remote_program_entry.delete(0, tk.END)
        self.remote_program_entry.insert(0, config['remote_program'])

    def setup_scanners_tab(self):
        """New Scanners tab for scanner subnet configuration"""
        frame = ttk.LabelFrame(self.scanners_frame, text="Scanner Network Configuration", padding="10")
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Use a grid layout for better control
        row = 0

        # Current scanner subnets display - COMPACT
        current_subnets_frame = ttk.LabelFrame(frame, text="Current Scanner Subnets", padding="5")
        current_subnets_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E, pady=5)
        row += 1

        self.current_subnets_label = ttk.Label(current_subnets_frame, text="Loading current subnets...",
                                               justify=tk.LEFT, background='#f5f5f5', relief=tk.SUNKEN, padding=3,
                                               wraplength=650)
        self.current_subnets_label.pack(fill=tk.X, padx=5, pady=2)

        ttk.Button(current_subnets_frame, text="Refresh Current Subnets",
                   command=self.refresh_scanner_subnets).pack(pady=2)

        # Scanner Subnets - COMPACT
        subnets_frame = ttk.LabelFrame(frame, text="Scanner Subnets/IPs", padding="5")
        subnets_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E, pady=5)
        row += 1

        ttk.Label(subnets_frame, text="Enter one subnet or IP per line:",
                  font=('Arial', 8), foreground='gray').pack(anchor=tk.W)

        text_frame = ttk.Frame(subnets_frame)
        text_frame.pack(fill=tk.X, pady=2)

        self.scanner_subnets_text = tk.Text(text_frame, height=2, width=30)
        self.scanner_subnets_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        scrollbar_subnets = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.scanner_subnets_text.yview)
        scrollbar_subnets.pack(side=tk.RIGHT, fill=tk.Y)
        self.scanner_subnets_text.config(yscrollcommand=scrollbar_subnets.set)

        # Scanner Device Detection - COMPACT
        scanner_alias_frame = ttk.LabelFrame(frame, text="Scanner Device Alias", padding="5")
        scanner_alias_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E, pady=5)
        row += 1

        # Device detection in one row
        detect_frame = ttk.Frame(scanner_alias_frame)
        detect_frame.pack(fill=tk.X, pady=2)

        ttk.Button(detect_frame, text="Detect Scanners",
                   command=self.detect_scanner_devices).pack(side=tk.LEFT, padx=(0, 10))

        self.scanner_devices_label = ttk.Label(detect_frame, text="Click 'Detect Scanners' to find devices",
                                               justify=tk.LEFT, background='#f5f5f5', relief=tk.SUNKEN, padding=3,
                                               wraplength=500)
        self.scanner_devices_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))

        # Alias creation in one row
        alias_frame = ttk.Frame(scanner_alias_frame)
        alias_frame.pack(fill=tk.X, pady=2)

        ttk.Label(alias_frame, text="Alias Name:").pack(side=tk.LEFT, padx=(0, 5))
        self.scanner_alias_entry = ttk.Entry(alias_frame, width=15)
        self.scanner_alias_entry.pack(side=tk.LEFT, padx=(0, 10))

        ttk.Button(alias_frame, text="Create Alias",
                   command=self.create_scanner_alias).pack(side=tk.LEFT, padx=(0, 10))

        ttk.Button(alias_frame, text="Remove Selected",
                   command=self.remove_selected_alias).pack(side=tk.LEFT)

        # Aliases listbox - COMPACT
        ttk.Label(scanner_alias_frame, text="Current Aliases:",
                  font=('Arial', 9, 'bold')).pack(anchor=tk.W, pady=(5, 2))

        aliases_frame = ttk.Frame(scanner_alias_frame)
        aliases_frame.pack(fill=tk.X, pady=2)

        self.aliases_listbox = tk.Listbox(aliases_frame, height=2, background='#f5f5f5', relief=tk.SUNKEN)
        self.aliases_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        listbox_scrollbar = ttk.Scrollbar(aliases_frame, orient=tk.VERTICAL, command=self.aliases_listbox.yview)
        listbox_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.aliases_listbox.config(yscrollcommand=listbox_scrollbar.set)

        # SANED Service Management - COMPACT
        saned_frame = ttk.LabelFrame(frame, text="SANED Service Management", padding="5")
        saned_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E, pady=5)
        row += 1

        saned_service_frame = ttk.Frame(saned_frame)
        saned_service_frame.pack(fill=tk.X, pady=2)

        ttk.Label(saned_service_frame, text="SANED Service:").pack(side=tk.LEFT, padx=(0, 10))

        ttk.Button(saned_service_frame, text="Start",
                   command=lambda: self.manage_service('xinetd', 'start')).pack(side=tk.LEFT, padx=2)
        ttk.Button(saned_service_frame, text="Restart",
                   command=lambda: self.manage_service('xinetd', 'restart')).pack(side=tk.LEFT, padx=2)
        ttk.Button(saned_service_frame, text="Enable",
                   command=lambda: self.manage_service('xinetd', 'enable')).pack(side=tk.LEFT, padx=2)

        self.saned_service_status = ttk.Label(saned_service_frame, text="Checking...")
        self.saned_service_status.pack(side=tk.LEFT, padx=(10, 0))

        # Bottom buttons - COMPACT
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E, pady=10)
        row += 1

        ttk.Button(button_frame, text="Apply Scanner Settings",
                   command=self.apply_scanner_settings).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Refresh Services",
                   command=self.refresh_scanner_services).pack(side=tk.LEFT, padx=5)

        self.scanner_status = ttk.Label(frame, text="", wraplength=650)
        self.scanner_status.grid(row=row, column=0, columnspan=2, sticky=tk.W + tk.E, pady=5)

        # Configure grid weights
        frame.columnconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)

        # Load current scanner subnets and auto-check status
        self.refresh_scanner_subnets()
        self.refresh_scanner_services()
        self.refresh_scanner_aliases()

    def setup_printers_tab(self):
        """Printers tab for CUPS configuration and printer management"""
        frame = ttk.LabelFrame(self.printers_frame, text="Printer Configuration", padding="15")
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # CUPS Remote Administration
        cups_frame = ttk.LabelFrame(frame, text="CUPS Remote Administration", padding="10")
        cups_frame.grid(row=0, column=0, columnspan=2, sticky=tk.W + tk.E, pady=10)

        self.cups_remote_var = tk.BooleanVar()
        self.cups_checkbutton = ttk.Checkbutton(cups_frame, text="Enable CUPS Remote Administration",
                                                variable=self.cups_remote_var)
        self.cups_checkbutton.pack(anchor=tk.W, pady=5)

        # CUPS status display
        self.cups_status_label = ttk.Label(cups_frame, text="Checking CUPS status...")
        self.cups_status_label.pack(anchor=tk.W, pady=5)

        # CUPS Printer Configuration Frame
        cups_config_frame = ttk.LabelFrame(frame, text="CUPS Printer Configuration", padding="10")
        cups_config_frame.grid(row=1, column=0, columnspan=2, sticky=tk.W + tk.E, pady=10)

        # Configure Printers Button
        ttk.Button(cups_config_frame, text="Configure Printers (Web Interface)",
                   command=self.open_cups_web_interface).pack(anchor=tk.W, pady=5)

        ttk.Label(cups_config_frame, text="Open CUPS web interface to add, remove, and manage printers",
                  font=('Arial', 8), foreground='gray').pack(anchor=tk.W, pady=(0, 5))

        # OpenRC Services Management
        services_frame = ttk.LabelFrame(frame, text="CUPS Services Management", padding="10")
        services_frame.grid(row=2, column=0, columnspan=2, sticky=tk.W + tk.E, pady=10)

        # CUPS Service controls
        cups_service_frame = ttk.Frame(services_frame)
        cups_service_frame.pack(fill=tk.X, pady=5)

        ttk.Label(cups_service_frame, text="CUPS Service:", font=('Arial', 9, 'bold')).pack(side=tk.LEFT, padx=(0, 10))

        ttk.Button(cups_service_frame, text="Start", command=lambda: self.manage_service('cupsd', 'start')).pack(
            side=tk.LEFT, padx=2)
        ttk.Button(cups_service_frame, text="Restart", command=lambda: self.manage_service('cupsd', 'restart')).pack(
            side=tk.LEFT, padx=2)
        ttk.Button(cups_service_frame, text="Enable", command=lambda: self.manage_service('cupsd', 'enable')).pack(
            side=tk.LEFT, padx=2)

        self.cups_service_status_label = ttk.Label(cups_service_frame, text="Checking...")
        self.cups_service_status_label.pack(side=tk.LEFT, padx=(10, 0))

        # Buttons
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=3, column=0, columnspan=2, pady=15)

        ttk.Button(button_frame, text="Apply Printer Settings", command=self.apply_printer_settings).pack(side=tk.LEFT,
                                                                                                          padx=5)
        ttk.Button(button_frame, text="Refresh Services", command=self.refresh_printer_services).pack(side=tk.LEFT,
                                                                                                      padx=5)

        self.printer_status = ttk.Label(frame, text="")
        self.printer_status.grid(row=4, column=0, columnspan=2)

        # Load current status
        self.refresh_printer_services()
        # Only check CUPS status if authenticated
        if self.password is not None:
            self.check_cups_status()
        else:
            self.cups_status_label.config(text="Please authenticate to check CUPS status")

    def open_cups_web_interface(self):
        """Open CUPS web interface in the default browser"""
        try:
            url = "http://127.0.0.1:631/admin"

            # Try different methods to open the browser
            methods = [
                ['xdg-open', url],  # Linux
                ['firefox', url],  # Firefox specifically
                ['chromium-browser', url],  # Chromium
                ['google-chrome', url],  # Google Chrome
                ['sensible-browser', url],  # Fallback on Linux
            ]

            success = False
            for method in methods:
                try:
                    result = subprocess.run(method, capture_output=True, timeout=10)
                    if result.returncode == 0:
                        success = True
                        self.printer_status.config(text="CUPS web interface opened successfully", foreground="green")
                        break
                except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
                    continue

            if not success:
                # Last resort: use webbrowser module
                import webbrowser
                webbrowser.open(url)
                self.printer_status.config(text="Opening CUPS web interface...", foreground="green")

        except Exception as e:
            self.printer_status.config(text=f"Error opening browser: {str(e)}", foreground="red")
            messagebox.showerror("Error", f"Could not open CUPS web interface:\n{str(e)}")

    def detect_scanner_devices(self):
        """Detect available scanner devices using scanimage -L and extract device paths"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            # Run scanimage -L and process output to extract only device paths
            cmd = ['bash', '-c', r"""
                scanimage -L | grep -q "device" && \
                scanimage -L | awk '{print $2}' | sed -e 's/^`//g' -e "s/'$//g" || \
                echo "No scanners detected"
                """.strip()]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

            if result.returncode == 0:
                devices = []
                for line in result.stdout.split('\n'):
                    line = line.strip()
                    if line and not line.startswith('device'):
                        devices.append(line)

                if devices:
                    devices_text = "Detected scanner devices:\n" + "\n".join([f"• {device}" for device in devices])
                    self.scanner_devices_label.config(text=devices_text)

                    # Auto-fill the first device path in the alias entry if empty
                    if devices and not self.scanner_alias_entry.get().strip():
                        first_device = devices[0]
                        # Extract just the first part of device path for suggestion
                        if ':' in first_device:
                            suggested_alias = first_device.split(':')[0]
                            self.scanner_alias_entry.insert(0, suggested_alias)
                else:
                    self.scanner_devices_label.config(text="No scanners detected")
            else:
                self.scanner_devices_label.config(text=f"Error detecting scanners: {result.stderr}")

        except Exception as e:
            self.scanner_devices_label.config(text=f"Error detecting scanners: {str(e)}")

    def create_scanner_alias(self):
        """Create scanner alias in /etc/sane.d/dll.aliases"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            alias_name = self.scanner_alias_entry.get().strip()
            if not alias_name:
                messagebox.showerror("Error", "Please enter a scanner alias name")
                return

            # Get the detected scanner devices from the label text
            current_text = self.scanner_devices_label.cget("text")
            if not current_text.startswith("Detected scanner devices:"):
                messagebox.showerror("Error", "Please detect scanners first")
                return

            # Extract the first device path from the displayed text
            lines = current_text.split('\n')
            if len(lines) < 2:
                messagebox.showerror("Error", "No scanners detected")
                return

            # Find the first device line (starts with •)
            device_path = None
            for line in lines[1:]:  # Skip the first line (header)
                if line.strip().startswith('•'):
                    device_path = line.strip()[2:].strip()  # Remove the "• " prefix
                    break

            if device_path == "No scanners detected":
                messagebox.showerror("Error", "No scanner devices found")
                return

            if not device_path:
                messagebox.showerror("Error", "No scanner devices found")
                return

            # Create the alias entry
            alias_entry = f"alias {alias_name} {device_path}"

            # Read current aliases
            aliases = self.get_current_scanner_aliases()

            # Check if alias already exists
            for existing_alias in aliases:
                if existing_alias.startswith(f"alias {alias_name} "):
                    messagebox.showerror("Error", f"Alias '{alias_name}' already exists")
                    return

            # Add new alias
            aliases.append(alias_entry)

            # Save aliases back to file
            if self.save_scanner_aliases(aliases):
                messagebox.showinfo("Success", f"Scanner alias '{alias_name}' created successfully")
                self.refresh_scanner_aliases()
                self.scanner_alias_entry.delete(0, tk.END)
            else:
                messagebox.showerror("Error", "Failed to create scanner alias")

        except Exception as e:
            messagebox.showerror("Error", f"Error creating scanner alias: {str(e)}")

    def save_scanner_aliases(self, aliases):
        """Save scanner aliases to /etc/sane.d/dll.aliases using sudo"""
        try:
            aliases_path = '/etc/sane.d/dll.aliases'

            # Write the aliases using sudo
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
                for alias in aliases:
                    tmp.write(f"{alias}\n")
                tmp_path = tmp.name

            # Move with sudo - use the stored password
            if self.password is None:
                raise PermissionError("Need root access to save scanner aliases")

            # Use the stored password for sudo
            cmd = ['sudo', '-S', 'mv', tmp_path, aliases_path]
            result = subprocess.run(cmd, input=f"{self.password}\n",
                                    capture_output=True, text=True, timeout=10)

            if result.returncode != 0:
                # Check if it's a password error
                if "sorry, try again" in result.stderr or "incorrect password" in result.stderr:
                    raise PermissionError("Authentication failed - wrong password")
                else:
                    raise PermissionError(f"Failed to save aliases: {result.stderr}")

            # Set proper permissions
            cmd = ['sudo', '-S', 'chmod', '644', aliases_path]
            subprocess.run(cmd, input=f"{self.password}\n",
                           capture_output=True, text=True, timeout=10)

            return True

        except Exception as e:
            messagebox.showerror("Error", f"Error saving scanner aliases: {str(e)}")
            return False

    def remove_selected_alias(self):
        """Remove the selected alias from listbox"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            # Get selected alias from LISTBOX, not entry field
            selection = self.aliases_listbox.curselection()
            if not selection:
                messagebox.showwarning("Warning", "Please select an alias from the list to remove")
                return

            selected_alias = self.aliases_listbox.get(selection[0])

            # Don't remove the "No aliases configured" message
            if selected_alias == "No aliases configured":
                messagebox.showwarning("Warning", "No aliases to remove")
                return

            # Confirm removal
            if not messagebox.askyesno("Confirm Removal", f"Are you sure you want to remove alias:\n{selected_alias}?"):
                return

            # Get current aliases
            aliases = self.get_current_scanner_aliases()

            # Remove the selected alias
            new_aliases = [a for a in aliases if a != selected_alias]

            # Save updated aliases
            if self.save_scanner_aliases(new_aliases):
                messagebox.showinfo("Success", "Scanner alias removed successfully")
                self.refresh_scanner_aliases()
                # AUTO-DETECT: Refresh scanner devices after removing alias
                self.detect_scanner_devices()
            else:
                # If save failed due to authentication, offer to re-authenticate
                if self.password is None:
                    messagebox.showerror("Authentication Failed",
                                         "Authentication failed. Please re-authenticate in the Authentication tab.")
                else:
                    messagebox.showerror("Error", "Failed to remove scanner alias")

        except Exception as e:
            messagebox.showerror("Error", f"Error removing scanner alias: {str(e)}")

    def show_alias_selection_dialog(self, aliases, alias_name):
        """Show dialog to select which alias to remove when multiple matches found"""
        selection_dialog = tk.Toplevel(self.root)
        selection_dialog.title(f"Remove Alias: {alias_name}")
        selection_dialog.geometry("500x300")
        selection_dialog.transient(self.root)
        selection_dialog.grab_set()

        ttk.Label(selection_dialog, text=f"Multiple aliases found for '{alias_name}'. Select one to remove:",
                  font=('Arial', 10, 'bold')).pack(pady=10)

        # Create frame for listbox and scrollbar
        list_frame = ttk.Frame(selection_dialog)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # Create listbox with aliases
        listbox = tk.Listbox(list_frame, selectmode=tk.SINGLE)
        listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # Add scrollbar
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=listbox.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        listbox.configure(yscrollcommand=scrollbar.set)

        for alias in aliases:
            listbox.insert(tk.END, alias)

        # Select first item by default
        if aliases:
            listbox.selection_set(0)

        # Buttons
        button_frame = ttk.Frame(selection_dialog)
        button_frame.pack(pady=10)

        def remove_selected():
            selection = listbox.curselection()
            if selection:
                selected_alias = aliases[selection[0]]
                selection_dialog.destroy()
                self.remove_scanner_alias(selected_alias)
            else:
                messagebox.showwarning("Warning", "Please select an alias to remove")

        def on_double_click(event):
            remove_selected()

        # Bind double-click to remove
        listbox.bind('<Double-Button-1>', on_double_click)

        ttk.Button(button_frame, text="Remove Selected", command=remove_selected).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Cancel", command=selection_dialog.destroy).pack(side=tk.LEFT, padx=5)

        # Set focus to listbox
        listbox.focus_set()

    def refresh_scanner_subnets(self):
        """Refresh and display current scanner subnets"""
        subnets = self.get_current_scanner_subnets()

        # Update display
        if subnets:
            display_text = "Current subnets/IPs:\n" + "\n".join([f"• {subnet}" for subnet in subnets])
        else:
            display_text = "No subnets or IPs configured"

        self.current_subnets_label.config(text=display_text)

        # Update text area
        self.scanner_subnets_text.delete(1.0, tk.END)
        self.scanner_subnets_text.insert(1.0, "\n".join(subnets))

    def refresh_scanner_devices(self):
        """Refresh scanner devices display"""
        # This will be called when the scanners tab is selected
        current_text = self.scanner_devices_label.cget("text")
        if current_text == "Click 'Detect Scanners' to find devices":
            # Auto-detect scanners when tab is first opened
            self.detect_scanner_devices()

    def check_service_status(self, service_name):
        """Check if an OpenRC service is running"""
        try:
            result = subprocess.run(['rc-service', service_name, 'status'],
                                    capture_output=True, text=True, timeout=10)
            return result.returncode == 0
        except Exception as e:
            print(f"Error checking service {service_name}: {e}")
            return False

    def check_service_enabled(self, service_name):
        """Check if an OpenRC service is enabled"""
        try:
            result = subprocess.run(['rc-status'],
                                    capture_output=True, text=True, timeout=10)
            # Check if service is in the default runlevel
            if result.returncode == 0:
                lines = result.stdout.split('\n')
                in_default_section = False
                for line in lines:
                    if 'default' in line:
                        in_default_section = True
                        continue
                    if in_default_section and line.strip().startswith('['):
                        break  # Moved to next section
                    if in_default_section and service_name in line:
                        return True
            return False
        except Exception as e:
            print(f"Error checking service enabled {service_name}: {e}")
            return False

    def check_cups_service(self):
        """Check and display CUPS service status"""
        is_active = self.check_service_status('cupsd')
        is_enabled = self.check_service_enabled('cupsd')

        status_text = f"Status: {'Active' if is_active else 'Inactive'} | "
        status_text += f"Enabled: {'Yes' if is_enabled else 'No'}"

        self.cups_service_status_label.config(
            text=status_text,
            foreground="green" if is_active else "red"
        )

    def check_saned_service(self):
        """Check and display SANED service status"""
        is_active = self.check_service_status('xinetd')
        is_enabled = self.check_service_enabled('xinetd')

        status_text = f"Status: {'Active' if is_active else 'Inactive'} | "
        status_text += f"Enabled: {'Yes' if is_enabled else 'No'}"

        self.saned_service_status.config(
            text=status_text,
            foreground="green" if is_active else "red"
        )

    def refresh_scanner_services(self):
        """Refresh only scanner-related services"""
        self.check_saned_service()

    def refresh_printer_services(self):
        """Refresh only printer-related services"""
        self.check_cups_service()

    def refresh_services(self):
        """Refresh all services (for backward compatibility)"""
        self.refresh_scanner_services()
        self.refresh_printer_services()

    def manage_service(self, service_name, action):
        """Manage OpenRC service (start, stop, restart, add, delete)"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            # Map actions to OpenRC commands
            if action == 'enable':
                # In OpenRC, 'add' adds service to default runlevel
                cmd = ['sudo', '-S', 'rc-update', 'add', service_name]
            elif action == 'disable':
                cmd = ['sudo', '-S', 'rc-update', 'del', service_name]
            else:
                # start, stop, restart
                cmd = ['sudo', '-S', 'rc-service', service_name, action]

            result = subprocess.run(cmd, input=f"{self.password}\n",
                                    capture_output=True, text=True, timeout=30)

            if result.returncode == 0:
                messagebox.showinfo("Success", f"Service {service_name} {action}ed successfully")
                # Refresh appropriate services based on which service was managed
                if service_name == 'xinetd':
                    self.refresh_scanner_services()
                elif service_name == 'cupsd':
                    self.refresh_printer_services()
            else:
                messagebox.showerror("Error", f"Failed to {action} {service_name}: {result.stderr}")

        except Exception as e:
            messagebox.showerror("Error", f"Error managing service: {str(e)}")

    def check_cups_status(self):
        """Check CUPS daemon status and configuration"""
        try:
            # Check if CUPS is running
            result = subprocess.run(['rc-service', 'cupsd', 'status'],
                                    capture_output=True, text=True, timeout=10)
            cups_active = result.returncode == 0

            # Check CUPS configuration for remote admin
            cups_conf_path = '/etc/cups/cupsd.conf'
            remote_admin_enabled = False

            if os.path.exists(cups_conf_path):
                try:
                    # Try to read directly first
                    with open(cups_conf_path, 'r') as f:
                        content = f.read()
                except PermissionError:
                    # If permission denied, use sudo to read
                    if self.password is None:
                        self.cups_status_label.config(text="Authentication needed to check CUPS config")
                        self.cups_remote_var.set(False)
                        return

                    # Read with sudo
                    cmd = ['sudo', '-S', 'cat', cups_conf_path]
                    result = subprocess.run(cmd, input=f"{self.password}\n",
                                            capture_output=True, text=True, timeout=10)
                    if result.returncode == 0:
                        content = result.stdout
                    else:
                        # If we still can't read, show limited status
                        status_text = f"CUPS Service: {'Running' if cups_active else 'Stopped'} | "
                        status_text += "Remote Admin: Unknown (Permission denied)"
                        self.cups_status_label.config(text=status_text)
                        self.cups_remote_var.set(False)
                        return

                # Look for Listen directives that allow remote access
                # Check for Port 631 (enables remote access) vs Listen localhost:631 (local only)
                if 'Port 631' in content or 'Listen *:631' in content or re.search(r'Listen\s+\d+\.\d+\.\d+\.\d+:\d+',
                                                                                   content):
                    remote_admin_enabled = True
                elif 'Listen localhost:631' in content:
                    remote_admin_enabled = False
            else:
                # CUPS config file doesn't exist
                status_text = f"CUPS Service: {'Running' if cups_active else 'Stopped'} | "
                status_text += "Config file not found"
                self.cups_status_label.config(text=status_text)
                self.cups_remote_var.set(False)
                return

            status_text = f"CUPS Service: {'Running' if cups_active else 'Stopped'} | "
            status_text += f"Remote Admin: {'Enabled' if remote_admin_enabled else 'Disabled'}"

            self.cups_status_label.config(text=status_text)

            # Update checkbox state
            self.cups_remote_var.set(remote_admin_enabled)

        except Exception as e:
            # Show a more user-friendly error message
            error_msg = f"Error checking CUPS status: {str(e)}"
            if "Permission denied" in str(e):
                error_msg = "Permission denied to read CUPS config. Please authenticate first."
            self.cups_status_label.config(text=error_msg)
            self.cups_remote_var.set(False)

    def modify_cups_config(self, enable_remote):
        """Modify CUPS configuration to enable/disable remote administration"""
        try:
            cups_conf_path = '/etc/cups/cupsd.conf'

            # Check if file exists and we can read it
            if not os.path.exists(cups_conf_path):
                messagebox.showerror("Error", "CUPS configuration file not found")
                return False

            # Read the current configuration
            try:
                with open(cups_conf_path, 'r') as f:
                    content = f.read()
            except PermissionError:
                # Read with sudo if permission denied
                if self.password is None:
                    messagebox.showerror("Permission Denied",
                                         "Need root access to read CUPS configuration")
                    return False

                cmd = ['sudo', '-S', 'cat', cups_conf_path]
                result = subprocess.run(cmd, input=f"{self.password}\n",
                                        capture_output=True, text=True, timeout=10)
                if result.returncode != 0:
                    messagebox.showerror("Error", f"Failed to read CUPS config: {result.stderr}")
                    return False
                content = result.stdout

            if enable_remote:
                # Enable remote administration - use the provided configuration
                new_content = """LogLevel warn
MaxLogSize 0
ErrorPolicy stop-printer
# Allow remote access
Port 631
Listen /run/cups/cups.sock
Browsing Yes
BrowseLocalProtocols dnssd
DefaultAuthType Basic
WebInterface Yes
IdleExitTimeout 60
<Location />
  # Allow remote administration...
  Order allow,deny
  Allow all
</Location>
<Location /admin>
  AuthType Default
  Require user @SYSTEM
  # Allow remote administration...
  Order allow,deny
  Allow all
</Location>
<Location /admin/conf>
  AuthType Default
  Require user @SYSTEM
  # Allow remote access to the configuration files...
  Order allow,deny
  Allow all
</Location>
<Location /admin/log>
  AuthType Default
  Require user @SYSTEM
  # Allow remote access to the log files...
  Order allow,deny
  Allow all
</Location>
<Policy default>
  JobPrivateAccess default
  JobPrivateValues default
  SubscriptionPrivateAccess default
  SubscriptionPrivateValues default
  <Limit Create-Job Print-Job Print-URI Validate-Job>
    Order deny,allow
  </Limit>
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Get-Document>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default CUPS-Get-Devices>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Cancel-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Authenticate-Job>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit All>
    Order deny,allow
  </Limit>
</Policy>
<Policy authenticated>
  JobPrivateAccess default
  JobPrivateValues default
  SubscriptionPrivateAccess default
  SubscriptionPrivateValues default
  <Limit Create-Job Print-Job Print-URI Validate-Job>
    AuthType Default
    Order deny,allow
  </Limit>
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Cancel-Job CUPS-Authenticate-Job>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit All>
    Order deny,allow
  </Limit>
</Policy>
<Policy kerberos>
  JobPrivateAccess default
  JobPrivateValues default
  SubscriptionPrivateAccess default
  SubscriptionPrivateValues default
  <Limit Create-Job Print-Job Print-URI Validate-Job>
    AuthType Negotiate
    Order deny,allow
  </Limit>
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
    AuthType Negotiate
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Cancel-Job CUPS-Authenticate-Job>
    AuthType Negotiate
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit All>
    Order deny,allow
  </Limit>
</Policy>"""
            else:
                # Disable remote administration - use the provided configuration
                new_content = """LogLevel warn
MaxLogSize 0
ErrorPolicy stop-printer
# Only listen for connections from the local machine.
Listen localhost:631
Listen /run/cups/cups.sock
# Disable printer sharing.
Browsing Off
DefaultAuthType Basic
WebInterface Yes
IdleExitTimeout 60
<Location />
  # Restrict access to the server...
  Order allow,deny
</Location>
<Location /admin>
  AuthType Default
  Require user @SYSTEM
  # Restrict access to the admin pages...
  Order allow,deny
</Location>
<Location /admin/conf>
  AuthType Default
  Require user @SYSTEM
  # Restrict access to the configuration files...
  Order allow,deny
</Location>
<Location /admin/log>
  AuthType Default
  Require user @SYSTEM
  # Restrict access to the log files...
  Order allow,deny
</Location>
<Policy default>
  JobPrivateAccess default
  JobPrivateValues default
  SubscriptionPrivateAccess default
  SubscriptionPrivateValues default
  <Limit Create-Job Print-Job Print-URI Validate-Job>
    Order deny,allow
  </Limit>
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Get-Document>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default CUPS-Get-Devices>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Cancel-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Authenticate-Job>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit All>
    Order deny,allow
  </Limit>
</Policy>
<Policy authenticated>
  JobPrivateAccess default
  JobPrivateValues default
  SubscriptionPrivateAccess default
  SubscriptionPrivateValues default
  <Limit Create-Job Print-Job Print-URI Validate-Job>
    AuthType Default
    Order deny,allow
  </Limit>
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Cancel-Job CUPS-Authenticate-Job>
    AuthType Default
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit All>
    Order deny,allow
  </Limit>
</Policy>
<Policy kerberos>
  JobPrivateAccess default
  JobPrivateValues default
  SubscriptionPrivateAccess default
  SubscriptionPrivateValues default
  <Limit Create-Job Print-Job Print-URI Validate-Job>
    AuthType Negotiate
    Order deny,allow
  </Limit>
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
    AuthType Negotiate
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>
  <Limit Cancel-Job CUPS-Authenticate-Job>
    AuthType Negotiate
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>
  <Limit All>
    Order deny,allow
  </Limit>
</Policy>"""

            # Write back the configuration using sudo
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
                tmp.write(new_content)
                tmp_path = tmp.name

            # Move with sudo
            if self.password is None:
                messagebox.showerror("Permission Denied",
                                     "Need root access to modify CUPS configuration")
                return False

            cmd = ['sudo', '-S', 'mv', tmp_path, cups_conf_path]
            result = subprocess.run(cmd, input=f"{self.password}\n",
                                    capture_output=True, text=True, timeout=10)

            if result.returncode != 0:
                messagebox.showerror("Error", f"Failed to save CUPS config: {result.stderr}")
                return False

            # Set proper permissions
            cmd = ['sudo', '-S', 'chmod', '644', cups_conf_path]
            subprocess.run(cmd, input=f"{self.password}\n",
                           capture_output=True, text=True, timeout=10)

            return True

        except Exception as e:
            messagebox.showerror("Error", f"Error modifying CUPS configuration: {str(e)}")
            return False

    def save_scanner_subnet(self, subnets):
        """Save scanner subnets to saned.conf using sudo"""
        try:
            saned_conf_path = '/etc/sane.d/saned.conf'

            # Read existing content using sudo if needed
            existing_content = []
            if os.path.exists(saned_conf_path):
                try:
                    with open(saned_conf_path, 'r') as f:
                        existing_content = f.readlines()
                except PermissionError:
                    # Read with sudo
                    if self.password is None:
                        raise PermissionError("Need root access to read saned.conf")

                    cmd = ['sudo', '-S', 'cat', saned_conf_path]
                    result = subprocess.run(cmd, input=f"{self.password}\n",
                                            capture_output=True, text=True, timeout=10)
                    if result.returncode == 0:
                        existing_content = result.stdout.splitlines(True)
                    else:
                        raise PermissionError(f"Failed to read saned.conf: {result.stderr}")

            # Filter out existing subnet lines and comments we'll replace
            new_content = []
            for line in existing_content:
                line_stripped = line.strip()
                # Keep lines that don't look like subnets and aren't empty
                if (not line_stripped or
                        line_stripped.startswith('#') or
                        not re.match(r'^\d+\.\d+\.\d+\.\d+(\/\d+)?$', line_stripped)):
                    new_content.append(line)

            # Add the new subnets
            for subnet in subnets:
                if subnet.strip():  # Only add non-empty subnets
                    new_content.append(f"{subnet.strip()}\n")

            # Write the new configuration using sudo
            import tempfile
            with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
                tmp.writelines(new_content)
                tmp_path = tmp.name

            # Move with sudo
            if self.password is None:
                raise PermissionError("Need root access to save saned.conf")

            cmd = ['sudo', '-S', 'mv', tmp_path, saned_conf_path]
            result = subprocess.run(cmd, input=f"{self.password}\n",
                                    capture_output=True, text=True, timeout=10)

            if result.returncode != 0:
                raise PermissionError(f"Failed to save saned.conf: {result.stderr}")

            # Set proper permissions
            cmd = ['sudo', '-S', 'chmod', '644', saned_conf_path]
            subprocess.run(cmd, input=f"{self.password}\n",
                           capture_output=True, text=True, timeout=10)

            return True

        except Exception as e:
            messagebox.showerror("Error", f"Error saving scanner subnets: {str(e)}")
            return False

    def apply_scanner_settings(self):
        """Apply scanner subnet settings"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            # Get subnets from text area
            subnets_text = self.scanner_subnets_text.get(1.0, tk.END).strip()
            subnets = [s.strip() for s in subnets_text.split('\n') if s.strip()]

            # Validate subnets
            for subnet in subnets:
                if not re.match(r'^\d+\.\d+\.\d+\.\d+(\/\d+)?$', subnet):
                    messagebox.showerror("Error", f"Invalid subnet format: {subnet}")
                    return

            # Save subnets
            if self.save_scanner_subnet(subnets):
                # Restart saned service if we have subnets
                if subnets:
                    self.manage_service('xinetd', 'restart')

                messagebox.showinfo("Success", "Scanner settings applied successfully")
                self.refresh_scanner_subnets()
            else:
                messagebox.showerror("Error", "Failed to save scanner subnets")

        except Exception as e:
            messagebox.showerror("Error", f"Error applying scanner settings: {str(e)}")

    def apply_printer_settings(self):
        """Apply printer settings (CUPS configuration)"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            # Modify CUPS configuration
            if self.modify_cups_config(self.cups_remote_var.get()):
                # Restart CUPS service
                self.manage_service('cupsd', 'restart')

                messagebox.showinfo("Success", "Printer settings applied successfully")
                self.check_cups_status()
            else:
                messagebox.showerror("Error", "Failed to apply CUPS configuration")

        except Exception as e:
            messagebox.showerror("Error", f"Error applying printer settings: {str(e)}")

    def update_ssh_status(self, event=None):
        """Update SSH configuration status display"""
        server = self.ssh_server_entry.get().strip()
        port = self.ssh_port_entry.get().strip()
        user = self.ssh_user_entry.get().strip()
        password = self.ssh_pass_entry.get().strip()
        program = self.remote_program_entry.get().strip()

        if server and port and user and password and program:
            self.ssh_status.config(text="Configuration complete - ready to save", foreground="green")
        else:
            missing = []
            if not server: missing.append("SSH Server")
            if not port: missing.append("SSH Port")
            if not user: missing.append("SSH Username")
            if not password: missing.append("SSH Password")
            if not program: missing.append("Remote Program")

            self.ssh_status.config(text=f"Missing: {', '.join(missing)}", foreground="red")

    def create_rx30_desktop_icon(self):
        """Create RX30 desktop icon with proper permissions"""
        try:
            current_user = getpass.getuser()
            desktop_path = os.path.expanduser(f"~{current_user}/Desktop/rx30.desktop")
            applications_path = '/usr/share/applications/rx30.desktop'

            # Desktop file content
            desktop_content = """[Desktop Entry]
Type=Application
Exec=/usr/local/bin/myscript.sh
Icon=/usr/share/pixmaps/rx30.png
Categories=Network;WebBrowser;
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name[en_US]=RX30
Name=RX30
Comment[en_US]=RX30
Comment=RX30
"""

            # Create user desktop file
            with open(desktop_path, 'w') as f:
                f.write(desktop_content)

            # Set permissions to 755 for current user
            os.chmod(desktop_path, 0o755)

            # Also create system-wide application file if we have root access
            if self.password:
                # Write system desktop file using sudo
                import tempfile
                with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
                    tmp.write(desktop_content)
                    tmp_path = tmp.name

                cmd = ['sudo', '-S', 'mv', tmp_path, applications_path]
                result = subprocess.run(cmd, input=f"{self.password}\n",
                                        capture_output=True, text=True, timeout=10)

                if result.returncode == 0:
                    # Set proper permissions for system file
                    cmd = ['sudo', '-S', 'chmod', '644', applications_path]
                    subprocess.run(cmd, input=f"{self.password}\n",
                                   capture_output=True, text=True, timeout=10)

            self.rx30_status_label.config(text="RX30 desktop icon created successfully", foreground="green")
            return True

        except Exception as e:
            self.rx30_status_label.config(text=f"Error creating RX30 icon: {str(e)}", foreground="red")
            return False

    def apply_ssh_changes(self):
        """Apply SSH configuration changes and save script"""
        try:
            server = self.ssh_server_entry.get().strip()
            port = self.ssh_port_entry.get().strip()
            user = self.ssh_user_entry.get().strip()
            password = self.ssh_pass_entry.get().strip()
            program = self.remote_program_entry.get().strip()

            # Validate inputs
            if not all([server, port, user, password, program]):
                messagebox.showerror("Error", "Please fill in all SSH configuration fields")
                return

            # Create the script content
            script_content = f"""#!/bin/bash

    # SSH Connection Configuration
    SSH_SERVER={server}
    SSH_PORT={port}
    SSH_USER={user}
    SSH_PASS={password}

    # Export password for sshpass
    export SSHPASS="$SSH_PASS"

    # Execute remote program via SSH
    sshpass -p "$SSH_PASS" ssh -X -o StrictHostKeyChecking=no -p $SSH_PORT $SSH_USER@$SSH_SERVER "{program}"
    """

            # Save the script
            script_path = '/usr/local/bin/myscript.sh'

            # Check if we have write permission or need sudo
            script_dir = os.path.dirname(script_path)
            if not os.path.exists(script_dir):
                try:
                    os.makedirs(script_dir, exist_ok=True)
                except PermissionError:
                    # Try with sudo
                    if self.password is None:
                        messagebox.showerror("Permission Denied",
                                             "Need root access to create script directory")
                        return

                    cmd = ['sudo', '-S', 'mkdir', '-p', script_dir]
                    result = subprocess.run(cmd, input=f"{self.password}\n",
                                            capture_output=True, text=True, timeout=30)
                    if result.returncode != 0:
                        messagebox.showerror("Error", f"Failed to create directory: {result.stderr}")
                        return

            # Write the script
            try:
                with open(script_path, 'w') as f:
                    f.write(script_content)

                # Make executable
                os.chmod(script_path, 0o755)

            except PermissionError:
                # Try with sudo
                if self.password is None:
                    messagebox.showerror("Permission Denied",
                                         "Need root access to save script")
                    return

                # Use a temporary file and move with sudo
                import tempfile
                with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
                    tmp.write(script_content)
                    tmp_path = tmp.name

                # Make temp file executable and move with sudo
                os.chmod(tmp_path, 0o755)
                cmd = ['sudo', '-S', 'mv', tmp_path, script_path]
                result = subprocess.run(cmd, input=f"{self.password}\n",
                                        capture_output=True, text=True, timeout=30)

                if result.returncode != 0:
                    messagebox.showerror("Error", f"Failed to save script: {result.stderr}")
                    return

            # Handle RX30 desktop icon based on checkbox state
            if self.rx30_icon_var.get():
                # Create icon if checked
                self.create_rx30_desktop_icon()
            else:
                # Remove icon if unchecked
                self.remove_rx30_desktop_icon()

            # Reset the manual change flag after applying changes
            self.rx30_icon_manually_changed = False

            messagebox.showinfo("Success", f"SSH script saved to {script_path}")

            # AUTO-REFRESH: Update the current configuration display
            # Add a small delay to ensure file is written before reading
            self.root.after(500, self.force_refresh_ssh_config)

        except Exception as e:
            messagebox.showerror("Error", f"Error applying SSH changes: {str(e)}")

    def force_refresh_ssh_config(self):
        """Force refresh SSH configuration display"""
        self.refresh_ssh_config()
        self.update_ssh_status()
        self.check_rx30_icon_status()

    def test_ssh(self):
        """Test SSH connection with current settings"""
        try:
            server = self.ssh_server_entry.get().strip()
            port = self.ssh_port_entry.get().strip()
            user = self.ssh_user_entry.get().strip()
            password = self.ssh_pass_entry.get().strip()

            if not all([server, port, user, password]):
                messagebox.showerror("Error", "Please fill in all SSH connection fields")
                return

            # Test connection using sshpass
            cmd = ['sshpass', '-p', password, 'ssh', '-o', 'StrictHostKeyChecking=no',
                   '-o', 'ConnectTimeout=10', '-p', port, f'{user}@{server}', 'echo "Connection successful"']

            result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)

            if result.returncode == 0:
                messagebox.showinfo("SSH Test", "SSH connection test successful!")
            else:
                messagebox.showerror("SSH Test", f"SSH connection failed: {result.stderr}")

        except subprocess.TimeoutExpired:
            messagebox.showerror("SSH Test", "SSH connection timed out")
        except Exception as e:
            messagebox.showerror("SSH Test", f"SSH test error: {str(e)}")

    def authenticate(self, event=None):
        """Authenticate with root password"""
        password = self.password_entry.get().strip()

        if not password:
            messagebox.showerror("Error", "Please enter the root password")
            return

        # Test the password by trying to run a command with sudo
        try:
            result = subprocess.run(['sudo', '-S', 'echo', 'test'],
                                    input=f"{password}\n", capture_output=True, text=True, timeout=5)

            if result.returncode == 0:
                self.password = password
                self.auth_status.config(text="Authenticated", foreground="green")
                self.password_entry.config(state="disabled")

                # Enable all tabs
                for i in range(1, 5):  # Tabs 1, 2, 3, 4
                    self.notebook.tab(i, state="normal")

                messagebox.showinfo("Success", "Authentication successful! All tabs are now enabled.")

                # Refresh services and status after authentication
                self.refresh_scanner_services()
                self.refresh_printer_services()
                self.check_cups_status()
                self.check_rx30_icon_status()

            else:
                self.auth_status.config(text="Authentication failed", foreground="red")
                messagebox.showerror("Error", "Authentication failed. Please check the root password.")

        except Exception as e:
            self.auth_status.config(text="Authentication error", foreground="red")
            messagebox.showerror("Error", f"Authentication error: {str(e)}")

    def configure_network(self):
        """Configure network interface using nmcli commands"""
        try:
            if self.password is None:
                messagebox.showerror("Authentication Required",
                                     "Please authenticate first in the Authentication tab.")
                return

            interface = self.interface_var.get()
            if not interface:
                messagebox.showerror("Error", "Please select a network interface")
                return

            config_type = self.config_type.get()

            if config_type == "static":
                ip = self.ip_entry.get().strip()
                subnet = self.subnet_entry.get().strip()
                gateway = self.gateway_entry.get().strip()
                dns = self.dns_entry.get().strip()

                if not all([ip, subnet, gateway]):
                    messagebox.showerror("Error", "Please fill in all required network fields for static configuration")
                    return

                # Convert subnet mask to CIDR
                cidr = self.subnet_to_cidr(subnet)

                # Use nmcli to configure static IP
                commands = [
                    f"sudo nmcli connection delete '{interface}' 2>/dev/null || true",  # Remove existing connection
                    f"sudo nmcli connection add type ethernet con-name '{interface}' ifname '{interface}'",
                    f"sudo nmcli connection modify '{interface}' ipv4.addresses {ip}/{cidr}",
                    f"sudo nmcli connection modify '{interface}' ipv4.gateway {gateway}",
                    f"sudo nmcli connection modify '{interface}' ipv4.method manual",
                    f"sudo nmcli connection up '{interface}'"
                ]

                # Add DNS if provided
                if dns:
                    dns_servers = dns.replace(',', ' ')
                    commands.insert(-1, f"sudo nmcli connection modify '{interface}' ipv4.dns '{dns_servers}'")

                # Execute commands
                for cmd in commands:
                    result = subprocess.run(cmd, shell=True, input=f"{self.password}\n",
                                            capture_output=True, text=True, timeout=30)
                    if result.returncode != 0 and "connection with uuid" not in result.stderr:
                        # Ignore errors about connection not existing for delete command
                        if "delete" not in cmd or "2>/dev/null" not in cmd:
                            messagebox.showerror("Error", f"Failed to configure network: {result.stderr}")
                            return

                messagebox.showinfo("Success", f"Network interface {interface} configured successfully with static IP")

            else:  # DHCP
                # Use nmcli to configure DHCP
                commands = [
                    f"sudo nmcli connection delete '{interface}' 2>/dev/null || true",  # Remove existing connection
                    f"sudo nmcli connection add type ethernet con-name '{interface}' ifname '{interface}'",
                    f"sudo nmcli connection modify '{interface}' ipv4.method auto",
                    f"sudo nmcli connection up '{interface}'"
                ]

                for cmd in commands:
                    result = subprocess.run(cmd, shell=True, input=f"{self.password}\n",
                                            capture_output=True, text=True, timeout=30)
                    if result.returncode != 0 and "connection with uuid" not in result.stderr:
                        # Ignore errors about connection not existing for delete command
                        if "delete" not in cmd or "2>/dev/null" not in cmd:
                            messagebox.showerror("Error", f"Failed to configure network: {result.stderr}")
                            return

                messagebox.showinfo("Success", f"Network interface {interface} configured successfully with DHCP")

            # Refresh interface details to show new configuration
            self.root.after(2000, self.refresh_interfaces)  # Wait a bit for changes to take effect

        except Exception as e:
            messagebox.showerror("Error", f"Error configuring network: {str(e)}")

    def subnet_to_cidr(self, subnet):
        """Convert subnet mask to CIDR notation"""
        try:
            # Count the number of 1 bits in the subnet mask
            return sum(bin(int(x)).count('1') for x in subnet.split('.'))
        except:
            return 24  # Default to /24 if conversion fails


def main():
    # Check if running as root (warn if yes)
    if os.geteuid() == 0:
        if not messagebox.askyesno("Warning",
                                   "You are already running as root.\n"
                                   "This application is designed to elevate privileges.\n"
                                   "Continue anyway?"):
            sys.exit()

    root = tk.Tk()
    app = FirstBootApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()
