import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
import threading
import subprocess
import requests
from urllib.parse import urljoin, unquote
from bs4 import BeautifulSoup
import re
import os
from datetime import datetime
import time
import json
import socket

# Try to import socks support
try:
    import socks
    import sockshandler
    SOCKS_AVAILABLE = True
except ImportError:
    SOCKS_AVAILABLE = False
    print("Warning: PySocks not installed. SOCKS proxy support disabled.")

# Try to import yaml
try:
    import yaml
    YAML_AVAILABLE = True
except ImportError:
    YAML_AVAILABLE = False
    print("Warning: PyYAML not installed. YAML proxy file support disabled.")
 
class EnhancedBrowser:
    """Enhanced HTML browser with SOCKS5 proxy support and directory listing view."""
    def __init__(self, parent_frame, proxy_callback, load_in_downloader_callback=None):
        self.parent = parent_frame
        self.get_proxy = proxy_callback
        self.load_in_downloader_callback = load_in_downloader_callback
        self.history = []
        self.history_pos = -1
        self.current_url = ""
        self.page_source = ""
        self.showing_source = False
        self.bookmarks = []
        self.find_window = None
        self.current_entries = []  # Store directory entries for sorting/filtering
        self.is_directory_listing = False
        self.tooltip = None  # For tooltips
        
        # Download control
        self.download_cancelled = False
        self.current_download_thread = None
        self.active_downloads = []
        
        # Icons
        self.icons = {
            'folder': u"\U0001f4c1",
            'file': u"\U0001f4c4",
            'parent': u"\U0001f4c2",
            'up': u"\u2b06",
            'download': u"\u2b07",
            'image': u"\U0001f5bc",
            'video': u"\U0001f3ac",
            'audio': u"\U0001f3b5",
            'archive': u"\U0001f4e6",
            'code': u"\U0001f4bb",
            'pdf': u"\U0001f4d6",
            'text': u"\U0001f4dd"
        }
        
        # Colors for UI
        self.colors = {
            'bg': '#f0f0f0',
            'link': '#0066cc',
            'link_hover': '#003d7a',
            'title': '#1a1a1a',
            'heading': '#2d2d2d',
            'text': '#333333',
            'code': '#555555',
            'quote': '#666666',
            'folder': '#f0a500',
            'folder_hover': '#d49000',
            'file': '#333333',
            'size': '#666666',
            'date': '#888888'
        }
        
        self.setup_ui()
    
    def setup_ui(self):
        """Setup enhanced browser UI with modern toolbar."""
        # Main toolbar frame
        toolbar = ttk.Frame(self.parent)
        toolbar.pack(fill=tk.X, padx=5, pady=2)
        
        # Navigation buttons frame
        nav_frame = ttk.Frame(toolbar)
        nav_frame.pack(side=tk.LEFT, fill=tk.Y)
        
        # Navigation buttons with symbols and tooltips
        self.back_btn = ttk.Button(nav_frame, text=u"\u25c0", command=self.go_back, width=3, state='disabled')
        self.back_btn.pack(side=tk.LEFT, padx=1)
        self.create_tooltip(self.back_btn, "Go Back (Alt+Left)")
        
        self.forward_btn = ttk.Button(nav_frame, text=u"\u25b6", command=self.go_forward, width=3, state='disabled')
        self.forward_btn.pack(side=tk.LEFT, padx=1)
        self.create_tooltip(self.forward_btn, "Go Forward (Alt+Right)")
        
        self.refresh_btn = ttk.Button(nav_frame, text=u"\u27f3", command=self.refresh, width=3)
        self.refresh_btn.pack(side=tk.LEFT, padx=1)
        self.create_tooltip(self.refresh_btn, "Refresh Page (F5)")
        
        # Stop button
        self.stop_btn = ttk.Button(nav_frame, text=u"\u2715", command=self.stop_loading, width=3, state='disabled')
        self.stop_btn.pack(side=tk.LEFT, padx=1)
        self.create_tooltip(self.stop_btn, "Stop Loading")
        
        # Home button
        self.home_btn = ttk.Button(nav_frame, text=u"\u2302", command=self.go_home, width=3)
        self.home_btn.pack(side=tk.LEFT, padx=(1, 10))
        self.create_tooltip(self.home_btn, "Go Home")
        
        # URL frame
        url_frame = ttk.Frame(toolbar)
        url_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        # Protocol indicator
        self.protocol_label = ttk.Label(url_frame, text="http://", foreground="gray", width=8)
        self.protocol_label.pack(side=tk.LEFT, padx=2)
        
        # URL entry
        self.url_var = tk.StringVar()
        self.url_entry = ttk.Entry(url_frame, textvariable=self.url_var, font=('Segoe UI', 10))
        self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        self.url_entry.bind('<Return>', lambda e: self.navigate())
        
        # Go button
        ttk.Button(url_frame, text=u"\u27a4", command=self.navigate, width=3).pack(side=tk.LEFT, padx=2)
        
        # Tools frame
        tools_frame = ttk.Frame(toolbar)
        tools_frame.pack(side=tk.RIGHT, fill=tk.Y)
        
        # Tool buttons
        ttk.Button(tools_frame, text=u"\U0001f50d", command=self.show_find_dialog, width=3).pack(side=tk.LEFT, padx=1)
        ttk.Button(tools_frame, text=u"\u2630", command=self.show_menu, width=3).pack(side=tk.LEFT, padx=1)
        
        # Second toolbar for proxy and options
        options_bar = ttk.Frame(self.parent)
        options_bar.pack(fill=tk.X, padx=5, pady=(0, 2))
        
        # Proxy status with indicator
        proxy_frame = ttk.LabelFrame(options_bar, text="Connection", padding=(5, 2))
        proxy_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        
        self.proxy_indicator = tk.Canvas(proxy_frame, width=12, height=12, highlightthickness=0)
        self.proxy_indicator.pack(side=tk.LEFT, padx=2)
        self.proxy_indicator.create_oval(2, 2, 10, 10, fill='gray', tags='indicator')
        
        self.proxy_label = ttk.Label(proxy_frame, text="Direct Connection", foreground="gray")
        self.proxy_label.pack(side=tk.LEFT, padx=5)
        
        # View options
        view_frame = ttk.Frame(options_bar)
        view_frame.pack(side=tk.LEFT, fill=tk.Y)
        
        self.view_mode = tk.StringVar(value='rendered')
        ttk.Radiobutton(view_frame, text="Rendered", variable=self.view_mode, 
                       value='rendered', command=self.toggle_view).pack(side=tk.LEFT, padx=2)
        ttk.Radiobutton(view_frame, text="Source", variable=self.view_mode, 
                       value='source', command=self.toggle_view).pack(side=tk.LEFT, padx=2)
        
        # Main content area with PanedWindow
        self.paned = ttk.PanedWindow(self.parent, orient=tk.HORIZONTAL)
        self.paned.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # Sidebar frame
        self.sidebar = ttk.Frame(self.paned, width=200)
        self.paned.add(self.sidebar, weight=0)
        
        # Sidebar notebook for bookmarks and history
        sidebar_notebook = ttk.Notebook(self.sidebar)
        sidebar_notebook.pack(fill=tk.BOTH, expand=True)
        
        # Bookmarks tab
        bookmarks_frame = ttk.Frame(sidebar_notebook, padding=5)
        sidebar_notebook.add(bookmarks_frame, text=u"\u2606 Bookmarks")
        
        self.bookmarks_listbox = tk.Listbox(bookmarks_frame, selectmode=tk.SINGLE, 
                                           font=('Segoe UI', 9))
        self.bookmarks_listbox.pack(fill=tk.BOTH, expand=True)
        self.bookmarks_listbox.bind('<Double-Button-1>', self.on_bookmark_double_click)
        
        bm_btn_frame = ttk.Frame(bookmarks_frame)
        bm_btn_frame.pack(fill=tk.X, pady=(5, 0))
        ttk.Button(bm_btn_frame, text="Add", command=self.add_bookmark).pack(side=tk.LEFT, padx=2)
        ttk.Button(bm_btn_frame, text="Remove", command=self.remove_bookmark).pack(side=tk.LEFT, padx=2)
        
        # History tab
        history_frame = ttk.Frame(sidebar_notebook, padding=5)
        sidebar_notebook.add(history_frame, text=u"\u23f3 History")
        
        self.history_listbox = tk.Listbox(history_frame, selectmode=tk.SINGLE,
                                         font=('Segoe UI', 9))
        self.history_listbox.pack(fill=tk.BOTH, expand=True)
        self.history_listbox.bind('<Double-Button-1>', self.on_history_double_click)
        
        ttk.Button(history_frame, text="Clear History", command=self.clear_history).pack(fill=tk.X, pady=(5, 0))
        
        # Content frame
        content_frame = ttk.Frame(self.paned)
        self.paned.add(content_frame, weight=1)
        
        # Create notebook for different views
        self.content_notebook = ttk.Notebook(content_frame)
        self.content_notebook.pack(fill=tk.BOTH, expand=True)
        
        # Directory listing view (Treeview)
        dir_frame = ttk.Frame(self.content_notebook, padding=5)
        self.content_notebook.add(dir_frame, text=u"\U0001f4c1 Directory View")
        
        # Toolbar for directory view
        dir_toolbar = ttk.Frame(dir_frame)
        dir_toolbar.pack(fill=tk.X, pady=(0, 5))
        
        ttk.Label(dir_toolbar, text="Sort by:").pack(side=tk.LEFT, padx=5)
        self.sort_var = tk.StringVar(value='name')
        ttk.Radiobutton(dir_toolbar, text="Name", variable=self.sort_var, 
                       value='name', command=self.sort_entries).pack(side=tk.LEFT, padx=2)
        ttk.Radiobutton(dir_toolbar, text="Date", variable=self.sort_var, 
                       value='date', command=self.sort_entries).pack(side=tk.LEFT, padx=2)
        ttk.Radiobutton(dir_toolbar, text="Size", variable=self.sort_var, 
                       value='size', command=self.sort_entries).pack(side=tk.LEFT, padx=2)
        
        ttk.Separator(dir_toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=10)
        
        ttk.Label(dir_toolbar, text="Filter:").pack(side=tk.LEFT, padx=5)
        self.filter_var = tk.StringVar()
        filter_entry = ttk.Entry(dir_toolbar, textvariable=self.filter_var, width=20)
        filter_entry.pack(side=tk.LEFT, padx=2)
        filter_entry.bind('<KeyRelease>', lambda e: self.filter_entries())
        ttk.Button(dir_toolbar, text="Clear", command=self.clear_filter, width=8).pack(side=tk.LEFT, padx=2)
        
        # Download toolbar with tooltips
        dl_toolbar = ttk.Frame(dir_frame)
        dl_toolbar.pack(fill=tk.X, pady=(5, 0))
        
        self.download_selected_btn = ttk.Button(dl_toolbar, text=u"\u2b07 Download Selected", 
                  command=self.download_selected)
        self.download_selected_btn.pack(side=tk.LEFT, padx=2)
        self.create_tooltip(self.download_selected_btn, "Download the selected file")
        
        self.download_all_btn = ttk.Button(dl_toolbar, text=u"\u2b07 Download All Files", 
                  command=self.download_all_files)
        self.download_all_btn.pack(side=tk.LEFT, padx=2)
        self.create_tooltip(self.download_all_btn, "Download all files in current directory")
        
        self.download_recursive_btn = ttk.Button(dl_toolbar, text=u"\u2b07 Download Folder (Recursive)", 
                  command=self.download_folder_recursive)
        self.download_recursive_btn.pack(side=tk.LEFT, padx=2)
        self.create_tooltip(self.download_recursive_btn, "Download entire folder including subdirectories")
        
        # Stop download button
        self.stop_download_btn = ttk.Button(dl_toolbar, text=u"\u23f9 Stop Downloads", 
                  command=self.stop_all_downloads, state='disabled')
        self.stop_download_btn.pack(side=tk.LEFT, padx=2)
        self.create_tooltip(self.stop_download_btn, "Cancel all active downloads")
        
        ttk.Separator(dl_toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=10)
        
        self.load_downloader_btn = ttk.Button(dl_toolbar, text=u"\u2192 Load in Downloader", 
                  command=self.load_in_downloader)
        self.load_downloader_btn.pack(side=tk.LEFT, padx=2)
        self.create_tooltip(self.load_downloader_btn, "Send current URL to Downloader tab")
        
        self.copy_url_btn = ttk.Button(dl_toolbar, text=u"\u2398 Copy Selected URL", 
                  command=self.copy_selected_url)
        self.copy_url_btn.pack(side=tk.LEFT, padx=2)
        self.create_tooltip(self.copy_url_btn, "Copy selected item URL to clipboard")
        
        # Treeview for directory listing
        tree_frame = ttk.Frame(dir_frame)
        tree_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        columns = ('icon', 'name', 'date', 'size', 'type')
        self.dir_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=20)
        
        self.dir_tree.heading('icon', text='')
        self.dir_tree.heading('name', text='Name')
        self.dir_tree.heading('date', text='Date Modified')
        self.dir_tree.heading('size', text='Size')
        self.dir_tree.heading('type', text='Type')
        
        self.dir_tree.column('icon', width=40, anchor='center')
        self.dir_tree.column('name', width=400, anchor='w')
        self.dir_tree.column('date', width=150, anchor='center')
        self.dir_tree.column('size', width=100, anchor='e')
        self.dir_tree.column('type', width=100, anchor='center')
        
        scrollbar_y = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.dir_tree.yview)
        scrollbar_x = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL, command=self.dir_tree.xview)
        self.dir_tree.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
        
        self.dir_tree.grid(row=0, column=0, sticky='nsew')
        scrollbar_y.grid(row=0, column=1, sticky='ns')
        scrollbar_x.grid(row=1, column=0, sticky='ew')
        
        tree_frame.grid_rowconfigure(0, weight=1)
        tree_frame.grid_columnconfigure(0, weight=1)
        
        # Bind double-click and Enter
        self.dir_tree.bind('<Double-1>', self.on_dir_item_double_click)
        self.dir_tree.bind('<Return>', self.on_dir_item_double_click)
        
        # Context menu
        self.context_menu = tk.Menu(self.parent, tearoff=0)
        self.context_menu.add_command(label=u"\u2b07 Download File", command=self.download_selected)
        self.context_menu.add_command(label=u"\u2398 Copy URL", command=self.copy_selected_url)
        self.context_menu.add_separator()
        self.context_menu.add_command(label=u"\ud83d\udcc1 Open Folder", command=self.open_selected_folder)
        self.context_menu.add_command(label=u"\u2192 Load in Downloader", command=self.load_in_downloader)
        
        self.dir_tree.bind('<Button-3>', self.show_context_menu)
        
        # HTML view (Text widget)
        html_frame = ttk.Frame(self.content_notebook, padding=5)
        self.content_notebook.add(html_frame, text=u"\U0001f4c4 HTML View")
        
        # HTML display with custom styling
        self.text_widget = tk.Text(html_frame, wrap=tk.WORD, font=('Segoe UI', 10),
                                  bg='white', fg=self.colors['text'],
                                  padx=10, pady=10,
                                  relief=tk.FLAT,
                                  highlightthickness=0)
        self.text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # Custom scrollbar
        scrollbar_frame = ttk.Frame(html_frame)
        scrollbar_frame.pack(side=tk.RIGHT, fill=tk.Y)
        
        scrollbar = ttk.Scrollbar(scrollbar_frame, command=self.text_widget.yview)
        scrollbar.pack(fill=tk.Y, expand=True)
        self.text_widget.config(yscrollcommand=scrollbar.set)
        
        # Configure text tags with enhanced styling
        self.setup_text_tags()
        
        # Make links clickable
        self.text_widget.bind("<Button-1>", self.on_link_click)
        self.text_widget.bind("<Button-3>", self.on_right_click)
        self.text_widget.bind("<Motion>", self.on_mouse_move)
        self.text_widget.config(cursor="", state=tk.DISABLED)
        
        # Status bar with progress
        status_frame = ttk.Frame(self.parent, relief=tk.SUNKEN)
        status_frame.pack(fill=tk.X, padx=5, pady=(0, 5))
        
        self.status_var = tk.StringVar(value="Ready")
        self.status_label = ttk.Label(status_frame, textvariable=self.status_var, 
                                     anchor=tk.W, padding=(5, 2))
        self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        # Progress bar
        self.progress_var = tk.DoubleVar(value=0)
        self.progress_bar = ttk.Progressbar(status_frame, variable=self.progress_var, 
                                           mode='indeterminate', length=100)
        self.progress_bar.pack(side=tk.RIGHT, padx=5)
        
        # Links storage
        self.links = {}
        self.link_counter = 0
        
        # Loading flag
        self.is_loading = False
        
        # Keyboard shortcuts
        self.parent.bind("<Alt-Left>", lambda e: self.go_back())
        self.parent.bind("<Alt-Right>", lambda e: self.go_forward())
        self.parent.bind("<F5>", lambda e: self.refresh())
        self.parent.bind("<Control-r>", lambda e: self.refresh())
        self.parent.bind("<Control-l>", lambda e: self.url_entry.focus())
        self.parent.bind("<Escape>", lambda e: self.stop_loading())
    
    def create_tooltip(self, widget, text):
        """Create a tooltip for a widget."""
        def enter(event):
            x, y, _, _ = widget.bbox("insert")
            x += widget.winfo_rootx() + 25
            y += widget.winfo_rooty() + 25
            
            # Create tooltip window
            self.tooltip = tk.Toplevel(widget)
            self.tooltip.wm_overrideredirect(True)
            self.tooltip.wm_geometry(f"+{x}+{y}")
            
            label = tk.Label(self.tooltip, text=text, justify=tk.LEFT,
                           background="#ffffe0", relief=tk.SOLID, borderwidth=1,
                           font=("Segoe UI", 9, "normal"), padx=5, pady=2)
            label.pack()
        
        def leave(event):
            if self.tooltip is not None:
                self.tooltip.destroy()
                self.tooltip = None
        
        widget.bind("<Enter>", enter)
        widget.bind("<Leave>", leave)
    
    def setup_text_tags(self):
        """Setup text tags with enhanced styling."""
        # Title
        self.text_widget.tag_configure("title", 
                                      font=('Segoe UI', 18, 'bold'),
                                      foreground=self.colors['title'],
                                      spacing1=10, spacing3=10)
        
        # Headings
        self.text_widget.tag_configure("h1", 
                                      font=('Segoe UI', 16, 'bold'),
                                      foreground=self.colors['heading'],
                                      spacing1=15, spacing3=5)
        self.text_widget.tag_configure("h2", 
                                      font=('Segoe UI', 14, 'bold'),
                                      foreground=self.colors['heading'],
                                      spacing1=12, spacing3=4)
        self.text_widget.tag_configure("h3", 
                                      font=('Segoe UI', 12, 'bold'),
                                      foreground=self.colors['heading'],
                                      spacing1=10, spacing3=3)
        
        # Links
        self.text_widget.tag_configure("link", 
                                      foreground=self.colors['link'],
                                      underline=True,
                                      font=('Segoe UI', 10))
        self.text_widget.tag_configure("link_hover",
                                      foreground=self.colors['link_hover'])
        
        # Text styles
        self.text_widget.tag_configure("bold", font=('Segoe UI', 10, 'bold'))
        self.text_widget.tag_configure("italic", font=('Segoe UI', 10, 'italic'))
        self.text_widget.tag_configure("code", 
                                      font=('Consolas', 9),
                                      foreground=self.colors['code'],
                                      background='#f5f5f5')
        self.text_widget.tag_configure("quote",
                                      foreground=self.colors['quote'],
                                      font=('Segoe UI', 10, 'italic'),
                                      lmargin1=20, lmargin2=20)
        
        # Lists
        self.text_widget.tag_configure("bullet", lmargin1=20, lmargin2=30)
        self.text_widget.tag_configure("numbered", lmargin1=20, lmargin2=30)
        
        # Separator
        self.text_widget.tag_configure("separator",
                                      font=('Segoe UI', 2),
                                      spacing1=5, spacing3=5)
        
        # Error message
        self.text_widget.tag_configure("error",
                                      foreground='#cc0000',
                                      font=('Segoe UI', 10))
    
    def sort_entries(self):
        """Sort directory entries."""
        if not self.current_entries:
            return
        
        sort_by = self.sort_var.get()
        
        if sort_by == 'name':
            # Folders first, then files, sorted by name
            self.current_entries.sort(key=lambda x: (not x.get('is_folder', False), x.get('name', '').lower()))
        elif sort_by == 'date':
            self.current_entries.sort(key=lambda x: x.get('date_raw', ''), reverse=True)
        elif sort_by == 'size':
            # Folders first, then by size
            self.current_entries.sort(key=lambda x: (not x.get('is_folder', False), x.get('size_bytes', 0)), reverse=True)
        
        self.update_dir_tree()
    
    def filter_entries(self):
        """Filter directory entries based on search text."""
        filter_text = self.filter_var.get().lower()
        if not filter_text:
            self.update_dir_tree()
            return
        
        filtered = [e for e in self.current_entries if filter_text in e.get('name', '').lower()]
        self.update_dir_tree(filtered)
    
    def clear_filter(self):
        """Clear the filter."""
        self.filter_var.set('')
        self.update_dir_tree()
    
    def update_dir_tree(self, entries=None):
        """Update the directory treeview with entries."""
        if entries is None:
            entries = self.current_entries
        
        # Clear existing
        for item in self.dir_tree.get_children():
            self.dir_tree.delete(item)
        
        # Add entries
        for entry in entries:
            icon = entry.get('icon', self.icons['file'])
            values = (
                icon,
                entry.get('name', ''),
                entry.get('date', '-'),
                entry.get('size', '-'),
                entry.get('type', 'File')
            )
            self.dir_tree.insert('', 'end', values=values, tags=(entry.get('url', ''),))
    
    def on_dir_item_double_click(self, event=None):
        """Handle double-click on directory item."""
        selection = self.dir_tree.selection()
        if not selection:
            return
        
        item = selection[0]
        values = self.dir_tree.item(item, 'values')
        if not values:
            return
        
        # Get URL from tags
        url = self.dir_tree.item(item, 'tags')[0] if self.dir_tree.item(item, 'tags') else None
        if url:
            self.load_url(url)
    
    def get_file_icon(self, filename):
        """Get appropriate icon for file type."""
        ext = os.path.splitext(filename.lower())[1]
        
        if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg']:
            return self.icons['image']
        elif ext in ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.m4v']:
            return self.icons['video']
        elif ext in ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a']:
            return self.icons['audio']
        elif ext in ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2']:
            return self.icons['archive']
        elif ext in ['.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.h', '.php']:
            return self.icons['code']
        elif ext == '.pdf':
            return self.icons['pdf']
        elif ext in ['.txt', '.md', '.doc', '.docx']:
            return self.icons['text']
        else:
            return self.icons['file']
    
    def parse_directory_listing(self, soup, base_url):
        """Parse HTTP directory listing (Apache/nginx/h5ai style)."""
        entries = []
        
        # Try to find table rows (Apache style)
        rows = soup.find_all('tr')
        
        for row in rows:
            cells = row.find_all(['td', 'th'])
            if len(cells) >= 2:
                # Find link in first cell
                link = cells[0].find('a')
                if link:
                    href = link.get('href', '')
                    name = link.get_text().strip()
                    
                    # Skip parent directory link in entries (we handle it separately)
                    if name in ['Parent Directory', '..', '../']:
                        continue
                    
                    full_url = urljoin(base_url, href)
                    is_folder = name.endswith('/') or href.endswith('/')
                    
                    # Extract date and size if available
                    date = '-'
                    size = '-'
                    size_bytes = 0
                    
                    if len(cells) >= 3:
                        date = cells[1].get_text().strip()
                        size_text = cells[2].get_text().strip()
                        size = size_text
                        
                        # Parse size
                        size_bytes = self.parse_size(size_text)
                    
                    # Get icon
                    if is_folder:
                        icon = self.icons['folder']
                        file_type = 'Folder'
                    else:
                        icon = self.get_file_icon(name)
                        file_type = 'File'
                    
                    entries.append({
                        'name': name.rstrip('/'),
                        'url': full_url,
                        'is_folder': is_folder,
                        'icon': icon,
                        'date': date,
                        'date_raw': date,
                        'size': size if not is_folder else '-',
                        'size_bytes': size_bytes if not is_folder else 0,
                        'type': file_type
                    })
        
        # If no table rows found, try h5ai or other styles
        if not entries:
            entries = self.parse_h5ai_listing(soup, base_url)
        
        # Try to fetch sizes for files that don't have them
        # This is done asynchronously to not block the UI
        if entries:
            threading.Thread(target=self.fetch_missing_sizes, args=(entries,), daemon=True).start()
        
        # If still no entries, try to find any links
        if not entries:
            links = soup.find_all('a')
            for link in links:
                href = link.get('href', '')
                name = link.get_text().strip()
                
                # Skip certain links
                if not href or href.startswith('#') or href.startswith('javascript:'):
                    continue
                if name in ['Name', 'Last modified', 'Size', 'Description']:
                    continue
                
                full_url = urljoin(base_url, href)
                is_folder = name.endswith('/') or href.endswith('/')
                
                # Check for parent directory
                is_parent = name in ['Parent Directory', '..', '../']
                
                if is_folder:
                    icon = self.icons['parent'] if is_parent else self.icons['folder']
                    file_type = 'Parent Directory' if is_parent else 'Folder'
                else:
                    icon = self.get_file_icon(name)
                    file_type = 'File'
                
                entries.append({
                    'name': name.rstrip('/'),
                    'url': full_url,
                    'is_folder': is_folder or is_parent,
                    'is_parent': is_parent,
                    'icon': icon,
                    'date': '-',
                    'date_raw': '',
                    'size': '-',
                    'size_bytes': 0,
                    'type': file_type
                })
        
        return entries
    
    def parse_h5ai_listing(self, soup, base_url):
        """Parse h5ai-style directory listings."""
        entries = []
        
        # h5ai uses various structures - try multiple approaches
        
        # Approach 1: Look for li items with specific classes
        items = soup.find_all(['li', 'div', 'tr'], class_=re.compile(r'item|file|folder|directory|row', re.I))
        
        for item in items:
            link = item.find('a')
            if not link:
                continue
            
            href = link.get('href', '')
            name = link.get_text().strip()
            
            if not href or not name:
                continue
            
            # Skip header/navigation links
            if name in ['Name', 'Last modified', 'Size', 'Description', 'Parent Directory', '..', '../']:
                continue
            
            full_url = urljoin(base_url, href)
            is_folder = ('folder' in ' '.join(item.get('class', [])).lower() or 
                        'directory' in ' '.join(item.get('class', [])).lower() or 
                        href.endswith('/'))
            
            # Try to find date and size
            date = '-'
            size = '-'
            size_bytes = 0
            
            # Look for date in various elements
            date_selectors = ['.date', '.time', '.modified', '[class*="date"]', '[class*="time"]']
            for selector in date_selectors:
                date_elem = item.select_one(selector)
                if date_elem:
                    date = date_elem.get_text().strip()
                    break
            
            # Look for size in various elements
            size_selectors = ['.size', '[class*="size"]', '.filesize']
            for selector in size_selectors:
                size_elem = item.select_one(selector)
                if size_elem:
                    size_text = size_elem.get_text().strip()
                    size = size_text
                    size_bytes = self.parse_size(size_text)
                    break
            
            # Check data attributes
            if size == '-' and item.get('data-size'):
                size_bytes = int(item.get('data-size', 0))
                size = self.format_size(size_bytes)
            
            if is_folder:
                icon = self.icons['folder']
                file_type = 'Folder'
            else:
                icon = self.get_file_icon(name)
                file_type = 'File'
            
            entries.append({
                'name': name.rstrip('/'),
                'url': full_url,
                'is_folder': is_folder,
                'icon': icon,
                'date': date,
                'date_raw': date,
                'size': size,
                'size_bytes': size_bytes,
                'type': file_type
            })
        
        # Approach 2: Look for any link with href and try to find associated metadata
        if not entries:
            links = soup.find_all('a', href=True)
            for link in links:
                href = link.get('href', '')
                name = link.get_text().strip()
                
                if not href or not name:
                    continue
                if name in ['Name', 'Last modified', 'Size', 'Description', 'Parent Directory']:
                    continue
                
                full_url = urljoin(base_url, href)
                is_folder = href.endswith('/')
                
                # Look for size in sibling or parent elements
                size = '-'
                size_bytes = 0
                parent = link.parent
                if parent:
                    # Check for size in text following the link
                    next_sibling = link.next_sibling
                    if next_sibling:
                        sibling_text = str(next_sibling).strip()
                        size_match = re.search(r'(\d+(?:\.\d+)?\s*[KMGT]B?)', sibling_text, re.I)
                        if size_match:
                            size = size_match.group(1)
                            size_bytes = self.parse_size(size)
                
                if is_folder:
                    icon = self.icons['folder']
                    file_type = 'Folder'
                else:
                    icon = self.get_file_icon(name)
                    file_type = 'File'
                
                entries.append({
                    'name': name.rstrip('/'),
                    'url': full_url,
                    'is_folder': is_folder,
                    'icon': icon,
                    'date': '-',
                    'date_raw': '',
                    'size': size,
                    'size_bytes': size_bytes,
                    'type': file_type
                })
        
        return entries
    
    def parse_size(self, size_text):
        """Parse size text to bytes."""
        if not size_text:
            return 0
        
        size_text = str(size_text).strip().upper()
        if not size_text or size_text == '-':
            return 0
        
        multipliers = {
            'B': 1,
            'K': 1024,
            'KB': 1024,
            'M': 1024**2,
            'MB': 1024**2,
            'G': 1024**3,
            'GB': 1024**3,
            'T': 1024**4,
            'TB': 1024**4
        }
        
        # Match number and unit (handle various formats)
        match = re.match(r'([\d.,]+)\s*([KMGT]?B?)', size_text)
        if match:
            try:
                value = float(match.group(1).replace(',', ''))
                unit = match.group(2) or 'B'
                return int(value * multipliers.get(unit, 1))
            except:
                pass
        
        return 0
    
    def format_size(self, size_bytes):
        """Format bytes to human readable string."""
        if size_bytes == 0:
            return '-'
        
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if abs(size_bytes) < 1024.0:
                if unit == 'B':
                    return f"{int(size_bytes)} {unit}"
                return f"{size_bytes:.1f} {unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.1f} PB"
    
    def fetch_missing_sizes(self, entries):
        """Fetch sizes for files that don't have them via HEAD requests."""
        proxy_config = self.get_proxy()
        
        for entry in entries:
            if entry.get('is_folder', False):
                continue
            if entry.get('size', '-') != '-':
                continue  # Already have size
            
            url = entry.get('url', '')
            if not url:
                continue
            
            old_socket = None
            
            try:
                if proxy_config:
                    proxy_type = proxy_config.get('type', 'HTTP')
                    server = proxy_config.get('server', '')
                    port = proxy_config.get('port', 0)
                    username = proxy_config.get('username', '')
                    password = proxy_config.get('password', '')
                    
                    if proxy_type in ['SOCKS5', 'SOCKS4'] and SOCKS_AVAILABLE:
                        import socks as socks_module
                        old_socket = socket.socket
                        
                        if proxy_type == 'SOCKS5':
                            socks_module.set_default_proxy(
                                socks_module.SOCKS5, server, port,
                                username=username if username else None,
                                password=password if password else None
                            )
                        else:
                            socks_module.set_default_proxy(socks_module.SOCKS4, server, port)
                        
                        socket.socket = socks_module.socksocket
                        
                        resp = requests.head(url, timeout=10, allow_redirects=True)
                    else:
                        if username and password:
                            proxy_url = f"http://{username}:{password}@{server}:{port}"
                        else:
                            proxy_url = f"http://{server}:{port}"
                        
                        proxies = {'http': proxy_url, 'https': proxy_url}
                        resp = requests.head(url, timeout=10, allow_redirects=True, proxies=proxies)
                else:
                    resp = requests.head(url, timeout=10, allow_redirects=True)
                
                content_length = resp.headers.get('content-length')
                if content_length:
                    size_bytes = int(content_length)
                    entry['size_bytes'] = size_bytes
                    entry['size'] = self.format_size(size_bytes)
                    
                    # Update the treeview
                    self.parent.after(100, self.update_dir_tree)
                
            except Exception as e:
                # Silently fail - not all servers support HEAD requests
                pass
            finally:
                if old_socket:
                    socket.socket = old_socket
    
    def show_context_menu(self, event):
        """Show context menu on right-click."""
        item = self.dir_tree.identify_row(event.y)
        if item:
            self.dir_tree.selection_set(item)
            try:
                self.context_menu.post(event.x_root, event.y_root)
            finally:
                self.context_menu.grab_release()
    
    def get_selected_entry(self):
        """Get the currently selected directory entry."""
        selection = self.dir_tree.selection()
        if not selection:
            return None
        
        item = selection[0]
        idx = self.dir_tree.index(item)
        
        # Filter visible entries
        filter_text = self.filter_var.get().lower()
        if filter_text:
            visible_entries = [e for e in self.current_entries if filter_text in e.get('name', '').lower()]
        else:
            visible_entries = self.current_entries
        
        if 0 <= idx < len(visible_entries):
            return visible_entries[idx]
        return None
    
    def download_selected(self):
        """Download the selected file."""
        entry = self.get_selected_entry()
        if not entry:
            messagebox.showwarning("Warning", "Please select a file to download")
            return
        
        if entry.get('is_folder', False):
            messagebox.showinfo("Info", "Please use 'Download Folder (Recursive)' for folders")
            return
        
        url = entry.get('url', '')
        if not url:
            return
        
        # Open save dialog (decode URL-encoded filename)
        filename = unquote(os.path.basename(url))
        save_path = filedialog.asksaveasfilename(
            initialfile=filename,
            defaultextension="",
            filetypes=[("All files", "*.*")]
        )
        
        if save_path:
            self.download_cancelled = False
            self.download_file(url, save_path)
    
    def download_file(self, url, save_path, callback=None):
        """Download a single file."""
        def do_download():
            old_socket = None
            try:
                self.parent.after(0, lambda: self.status_var.set(f"Downloading: {os.path.basename(save_path)}"))
                
                # Get proxy configuration
                proxy_config = self.get_proxy()
                
                if proxy_config:
                    proxy_type = proxy_config.get('type', 'HTTP')
                    server = proxy_config.get('server', '')
                    port = proxy_config.get('port', 0)
                    username = proxy_config.get('username', '')
                    password = proxy_config.get('password', '')
                    
                    if proxy_type in ['SOCKS5', 'SOCKS4'] and SOCKS_AVAILABLE:
                        import socks as socks_module
                        old_socket = socket.socket
                        
                        if proxy_type == 'SOCKS5':
                            socks_module.set_default_proxy(
                                socks_module.SOCKS5, server, port,
                                username=username if username else None,
                                password=password if password else None
                            )
                        else:
                            socks_module.set_default_proxy(socks_module.SOCKS4, server, port)
                        
                        socket.socket = socks_module.socksocket
                        
                        resp = requests.get(url, timeout=60, stream=True)
                    else:
                        if username and password:
                            proxy_url = f"http://{username}:{password}@{server}:{port}"
                        else:
                            proxy_url = f"http://{server}:{port}"
                        
                        proxies = {'http': proxy_url, 'https': proxy_url}
                        resp = requests.get(url, timeout=60, stream=True, proxies=proxies)
                else:
                    resp = requests.get(url, timeout=60, stream=True)
                
                resp.raise_for_status()
                
                # Download with progress
                total_size = int(resp.headers.get('content-length', 0))
                downloaded = 0
                chunk_size = 8192
                
                with open(save_path, 'wb') as f:
                    for chunk in resp.iter_content(chunk_size=chunk_size):
                        if self.download_cancelled:
                            break
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            
                            if total_size > 0:
                                progress = int((downloaded / total_size) * 100)
                                self.parent.after(0, lambda p=progress, name=os.path.basename(save_path): 
                                    self.status_var.set(f"Downloading {name}: {p}%"))
                
                self.parent.after(0, lambda: self.status_var.set(f"Downloaded: {os.path.basename(save_path)}"))
                
                if callback:
                    self.parent.after(0, callback)
                
            except Exception as e:
                if not self.download_cancelled:
                    self.parent.after(0, lambda: self.status_var.set(f"Download failed: {str(e)}"))
                if os.path.exists(save_path):
                    try:
                        os.remove(save_path)
                    except:
                        pass
            finally:
                if old_socket:
                    socket.socket = old_socket
                self.parent.after(0, lambda: self.stop_download_btn.config(state='disabled'))
        
        self.download_cancelled = False
        self.stop_download_btn.config(state='normal')
        threading.Thread(target=do_download, daemon=True).start()
    
    def download_all_files(self):
        """Download all files in the current directory."""
        files = [e for e in self.current_entries if not e.get('is_folder', False)]
        
        if not files:
            messagebox.showinfo("Info", "No files to download in this directory")
            return
        
        # Ask for output directory
        output_dir = filedialog.askdirectory(title="Select download folder")
        if not output_dir:
            return
        
        self.download_cancelled = False
        self.stop_download_btn.config(state='normal')
        downloaded_count = [0]  # Use list to allow modification in nested function
        
        def download_next(idx=0):
            if self.download_cancelled:
                self.parent.after(0, lambda: self.status_var.set(f"Download cancelled. {downloaded_count[0]} files downloaded."))
                self.parent.after(0, lambda: self.stop_download_btn.config(state='disabled'))
                return
            
            if idx >= len(files):
                self.parent.after(0, lambda: self.status_var.set(f"Downloaded {downloaded_count[0]} files"))
                self.parent.after(0, lambda: self.stop_download_btn.config(state='disabled'))
                messagebox.showinfo("Complete", f"Downloaded {downloaded_count[0]} files successfully!")
                return
            
            file_entry = files[idx]
            url = file_entry.get('url', '')
            # Decode URL-encoded filename and get clean name
            filename = unquote(os.path.basename(url))
            save_path = os.path.join(output_dir, filename)
            
            self.parent.after(0, lambda i=idx+1, total=len(files), name=filename: 
                self.status_var.set(f"Downloading {i}/{total}: {name}"))
            
            def on_complete():
                downloaded_count[0] += 1
                download_next(idx + 1)
            
            self.download_file(url, save_path, on_complete)
        
        download_next(0)
    
    def download_folder_recursive(self):
        """Download entire folder including subdirectories."""
        if not self.current_url:
            return
        
        # Ask for output directory
        output_dir = filedialog.askdirectory(title="Select download folder")
        if not output_dir:
            return
        
        # Extract folder name from URL (decode URL encoding)
        folder_name = unquote(os.path.basename(self.current_url.rstrip('/'))) or "download"
        base_path = os.path.join(output_dir, folder_name)
        
        messagebox.showinfo("Info", "Recursive download started. This may take a while...\nCheck status bar for progress.")
        
        def download_recursive(url, local_path):
            """Recursively download a directory."""
            old_socket = None
            proxy_config = self.get_proxy()
            proxy_type = None
            server = ''
            port = 0
            username = ''
            password = ''
            proxies = None
            
            try:
                # Ensure directory exists
                os.makedirs(local_path, exist_ok=True)
                
                # Get directory listing
                
                if proxy_config:
                    proxy_type = proxy_config.get('type', 'HTTP')
                    server = proxy_config.get('server', '')
                    port = proxy_config.get('port', 0)
                    username = proxy_config.get('username', '')
                    password = proxy_config.get('password', '')
                    
                    if proxy_type in ['SOCKS5', 'SOCKS4'] and SOCKS_AVAILABLE:
                        import socks as socks_module
                        old_socket = socket.socket
                        
                        if proxy_type == 'SOCKS5':
                            socks_module.set_default_proxy(
                                socks_module.SOCKS5, server, port,
                                username=username if username else None,
                                password=password if password else None
                            )
                        else:
                            socks_module.set_default_proxy(socks_module.SOCKS4, server, port)
                        
                        socket.socket = socks_module.socksocket
                        
                        resp = requests.get(url, timeout=30)
                    else:
                        if username and password:
                            proxy_url = f"http://{username}:{password}@{server}:{port}"
                        else:
                            proxy_url = f"http://{server}:{port}"
                        
                        proxies = {'http': proxy_url, 'https': proxy_url}
                        resp = requests.get(url, timeout=30, proxies=proxies)
                else:
                    resp = requests.get(url, timeout=30)
                
                resp.raise_for_status()
                
                soup = BeautifulSoup(resp.text, 'html.parser')
                entries = self.parse_directory_listing(soup, url)
                
                if old_socket:
                    socket.socket = old_socket
                
                # Download files and recurse into folders
                for entry in entries:
                    if entry.get('is_parent', False):
                        continue
                    
                    entry_url = entry.get('url', '')
                    entry_name = entry.get('name', '')
                    
                    if entry.get('is_folder', False):
                        # Recurse into subdirectory
                        sub_path = os.path.join(local_path, entry_name)
                        download_recursive(entry_url, sub_path)
                    else:
                        # Download file
                        file_path = os.path.join(local_path, entry_name)
                        self.parent.after(0, lambda p=local_path: self.status_var.set(f"Downloading to: {p}"))
                        
                        # Simple download (blocking for recursion)
                        try:
                            if proxy_config and proxy_type in ['SOCKS5', 'SOCKS4'] and SOCKS_AVAILABLE:
                                import socks as socks_module
                                old_socket = socket.socket
                                
                                if proxy_type == 'SOCKS5':
                                    socks_module.set_default_proxy(
                                        socks_module.SOCKS5, server, port,
                                        username=username if username else None,
                                        password=password if password else None
                                    )
                                else:
                                    socks_module.set_default_proxy(socks_module.SOCKS4, server, port)
                                
                                socket.socket = socks_module.socksocket
                                
                                file_resp = requests.get(entry_url, timeout=60)
                            elif proxy_config:
                                file_resp = requests.get(entry_url, timeout=60, proxies=proxies)
                            else:
                                file_resp = requests.get(entry_url, timeout=60)
                            
                            with open(file_path, 'wb') as f:
                                f.write(file_resp.content)
                            
                            if old_socket:
                                socket.socket = old_socket
                                old_socket = None
                                
                        except Exception as e:
                            print(f"Failed to download {entry_name}: {e}")
                            if old_socket:
                                socket.socket = old_socket
                                old_socket = None
                
            except Exception as e:
                print(f"Error downloading {url}: {e}")
                if old_socket:
                    socket.socket = old_socket
        
        def start_download():
            download_recursive(self.current_url, base_path)
            self.parent.after(0, lambda: self.status_var.set(f"Recursive download complete: {base_path}"))
            messagebox.showinfo("Complete", f"Folder downloaded to:\n{base_path}")
        
        self.current_download_thread = threading.Thread(target=start_download, daemon=True)
        self.current_download_thread.start()
        self.stop_download_btn.config(state='normal')
        self.download_cancelled = False
    
    def stop_all_downloads(self):
        """Stop all active downloads."""
        self.download_cancelled = True
        self.stop_download_btn.config(state='disabled')
        self.status_var.set("Downloads cancelled by user")
        messagebox.showinfo("Download Cancelled", "All downloads have been cancelled.")
    
    def copy_selected_url(self):
        """Copy the selected item's URL to clipboard."""
        entry = self.get_selected_entry()
        if entry:
            url = entry.get('url', '')
            if url:
                self.parent.clipboard_clear()
                self.parent.clipboard_append(url)
                self.status_var.set("URL copied to clipboard")
    
    def open_selected_folder(self):
        """Open the selected folder."""
        entry = self.get_selected_entry()
        if entry and entry.get('is_folder', False):
            url = entry.get('url', '')
            if url:
                self.load_url(url)
    
    def load_in_downloader(self):
        """Load current URL in the downloader tab."""
        if self.current_url:
            if self.load_in_downloader_callback:
                self.load_in_downloader_callback(self.current_url)
                self.status_var.set("Loaded in downloader tab")
            else:
                messagebox.showinfo("Info", f"Current URL:\n{self.current_url}\n\nCopy this URL and paste it in the Downloader tab.")
    
    def update_proxy_status(self, proxy_info):
        """Update proxy status display with visual indicator."""
        if proxy_info:
            self.proxy_label.config(text=f"Proxy: {proxy_info}", foreground="#00aa00")
            self.proxy_indicator.itemconfig('indicator', fill='#00aa00')
        else:
            self.proxy_label.config(text="Direct Connection", foreground="gray")
            self.proxy_indicator.itemconfig('indicator', fill='gray')
    
    def update_nav_buttons(self):
        """Update navigation button states."""
        if self.history_pos > 0:
            self.back_btn.config(state='normal')
        else:
            self.back_btn.config(state='disabled')
        
        if self.history_pos < len(self.history) - 1:
            self.forward_btn.config(state='normal')
        else:
            self.forward_btn.config(state='disabled')
    
    def go_back(self):
        """Go back in history."""
        if self.history_pos > 0:
            self.history_pos -= 1
            url = self.history[self.history_pos]
            self.load_url(url, add_history=False)
    
    def go_forward(self):
        """Go forward in history."""
        if self.history_pos < len(self.history) - 1:
            self.history_pos += 1
            url = self.history[self.history_pos]
            self.load_url(url, add_history=False)
    
    def refresh(self):
        """Refresh current page."""
        if self.current_url:
            self.load_url(self.current_url, add_history=False)
    
    def stop_loading(self):
        """Stop loading current page."""
        self.is_loading = False
    
    def go_home(self):
        """Navigate to home page."""
        self.load_url("http://example.com")
    
    def navigate(self):
        """Navigate to entered URL."""
        url = self.url_var.get().strip()
        if url:
            if not url.startswith(('http://', 'https://')):
                url = 'http://' + url
            self.load_url(url)
    
    def load_url(self, url, add_history=True):
        """Load URL content through proxy with enhanced UI feedback."""
        self.is_loading = True
        self.stop_btn.config(state='normal')
        self.progress_bar.start(10)
        self.status_var.set(f"Loading: {url}")
        self.parent.update_idletasks()
        
        # Store old socket for restoration
        old_socket = None
        self.page_source = ""
        
        try:
            # Get proxy configuration
            proxy_config = self.get_proxy()
            
            if proxy_config:
                proxy_type = proxy_config.get('type', 'HTTP')
                server = proxy_config.get('server', '')
                port = proxy_config.get('port', 0)
                username = proxy_config.get('username', '')
                password = proxy_config.get('password', '')
                
                if proxy_type in ['SOCKS5', 'SOCKS4'] and SOCKS_AVAILABLE:
                    import socks as socks_module
                    old_socket = socket.socket
                    
                    if proxy_type == 'SOCKS5':
                        socks_module.set_default_proxy(
                            socks_module.SOCKS5, server, port,
                            username=username if username else None,
                            password=password if password else None
                        )
                    else:
                        socks_module.set_default_proxy(socks_module.SOCKS4, server, port)
                    
                    socket.socket = socks_module.socksocket
                    
                    # Make request through SOCKS
                    resp = requests.get(url, timeout=30)
                else:
                    # HTTP proxy
                    if username and password:
                        proxy_url = f"http://{username}:{password}@{server}:{port}"
                    else:
                        proxy_url = f"http://{server}:{port}"
                    
                    proxies = {'http': proxy_url, 'https': proxy_url}
                    resp = requests.get(url, timeout=30, proxies=proxies)
            else:
                # No proxy
                resp = requests.get(url, timeout=30)
            
            resp.raise_for_status()
            self.page_source = resp.text
            
            # Parse HTML
            soup = BeautifulSoup(resp.text, 'html.parser')
            
            # Update protocol label
            if url.startswith('https'):
                self.protocol_label.config(text="https://", foreground="green")
            else:
                self.protocol_label.config(text="http://", foreground="gray")
            
            # Display content based on view mode
            if not self.showing_source:
                self.display_html(soup, url)
            else:
                self.display_source()
            
            # Update URL and history
            self.current_url = url
            self.url_var.set(url)
            
            if add_history:
                # Remove forward history if we navigated to a new page
                if self.history_pos < len(self.history) - 1:
                    self.history = self.history[:self.history_pos + 1]
                
                self.history.append(url)
                self.history_pos = len(self.history) - 1
                self.update_history_listbox()
            
            self.update_nav_buttons()
            self.status_var.set(f"Loaded: {url}")
            
        except Exception as e:
            self.status_var.set(f"Error: {str(e)[:50]}")
            self.text_widget.config(state=tk.NORMAL)
            self.text_widget.delete(1.0, tk.END)
            self.text_widget.insert(tk.END, f"Error loading page:\n\n", "error")
            self.text_widget.insert(tk.END, str(e))
            self.text_widget.config(state=tk.DISABLED)
        
        finally:
            # Restore original socket
            if old_socket:
                socket.socket = old_socket
            
            self.is_loading = False
            self.stop_btn.config(state='disabled')
            self.progress_bar.stop()
    
    def display_html(self, soup, base_url):
        """Display HTML content with enhanced rendering."""
        # Check if this is a directory listing
        self.current_entries = self.parse_directory_listing(soup, base_url)
        self.is_directory_listing = len(self.current_entries) > 0
        
        if self.is_directory_listing and not self.showing_source:
            # Show directory view
            self.content_notebook.select(0)  # Select directory tab
            self.sort_entries()  # This will populate the tree
        else:
            # Show HTML view
            self.content_notebook.select(1)  # Select HTML tab
            self.text_widget.config(state=tk.NORMAL)
            self.text_widget.delete(1.0, tk.END)
            self.links = {}
            self.link_counter = 0
            
            # Title
            title = soup.find('title')
            if title:
                title_text = title.get_text().strip()
                self.text_widget.insert(tk.END, title_text + "\n", "title")
                self.text_widget.insert(tk.END, "\n")
            
            # Remove unwanted elements
            for element in soup.find_all(['script', 'style', 'nav', 'aside', 'footer']):
                element.decompose()
            
            # Process body content
            body = soup.body if soup.body else soup
            self.process_element(body, base_url)
            
            self.text_widget.config(state=tk.DISABLED)
            self.text_widget.see(1.0)
    
    def process_element(self, element, base_url):
        """Recursively process HTML elements with enhanced formatting."""
        if not element or not self.is_loading:
            return
        
        # Skip invisible elements
        if element.name in ['script', 'style', 'meta', 'link', 'noscript']:
            return
        
        # Handle different element types
        handlers = {
            'a': self.handle_link,
            'h1': lambda e, url: self.handle_heading(e, url, 'h1'),
            'h2': lambda e, url: self.handle_heading(e, url, 'h2'),
            'h3': lambda e, url: self.handle_heading(e, url, 'h3'),
            'h4': lambda e, url: self.handle_heading(e, url, 'h3'),
            'h5': lambda e, url: self.handle_heading(e, url, 'h3'),
            'h6': lambda e, url: self.handle_heading(e, url, 'h3'),
            'p': self.handle_paragraph,
            'br': self.handle_break,
            'b': lambda e, url: self.handle_styled_text(e, url, 'bold'),
            'strong': lambda e, url: self.handle_styled_text(e, url, 'bold'),
            'i': lambda e, url: self.handle_styled_text(e, url, 'italic'),
            'em': lambda e, url: self.handle_styled_text(e, url, 'italic'),
            'code': lambda e, url: self.handle_styled_text(e, url, 'code'),
            'pre': lambda e, url: self.handle_styled_text(e, url, 'code'),
            'blockquote': lambda e, url: self.handle_styled_text(e, url, 'quote'),
            'ul': lambda e, url: self.handle_list(e, url, 'bullet'),
            'ol': lambda e, url: self.handle_list(e, url, 'numbered'),
            'li': self.handle_list_item,
            'img': self.handle_image,
            'hr': self.handle_separator,
            'div': self.handle_div,
            'span': self.handle_span,
        }
        
        if element.name in handlers:
            handlers[element.name](element, base_url)
        else:
            # Process children for unknown elements
            self.process_children(element, base_url)
            
            # Add spacing for block elements
            if element.name in ['section', 'article', 'header', 'main']:
                self.text_widget.insert(tk.END, "\n")
    
    def handle_link(self, element, base_url):
        """Handle anchor links."""
        href = element.get('href', '')
        if href:
            full_url = urljoin(base_url, href)
            text = element.get_text().strip()
            if text:
                tag_name = f"link_{self.link_counter}"
                self.links[tag_name] = full_url
                self.text_widget.insert(tk.END, text, ("link", tag_name))
                self.text_widget.tag_bind(tag_name, "<Enter>", 
                    lambda e, tag=tag_name: self.on_link_enter(tag))
                self.text_widget.tag_bind(tag_name, "<Leave>", 
                    lambda e, tag=tag_name: self.on_link_leave(tag))
                self.text_widget.tag_bind(tag_name, "<Button-1>", 
                    lambda e, url=full_url: self.load_url(url))
                self.link_counter += 1
    
    def handle_heading(self, element, base_url, tag):
        """Handle heading elements."""
        text = element.get_text().strip()
        if text:
            self.text_widget.insert(tk.END, "\n" + text + "\n", tag)
    
    def handle_paragraph(self, element, base_url):
        """Handle paragraph elements."""
        text = element.get_text().strip()
        if text:
            self.process_children(element, base_url)
            self.text_widget.insert(tk.END, "\n\n")
    
    def handle_break(self, element, base_url):
        """Handle line breaks."""
        self.text_widget.insert(tk.END, "\n")
    
    def handle_styled_text(self, element, base_url, tag):
        """Handle styled text elements."""
        self.text_widget.insert(tk.END, element.get_text().strip() + " ", tag)
    
    def handle_list(self, element, base_url, list_type):
        """Handle list elements."""
        items = element.find_all('li', recursive=False)
        for i, item in enumerate(items, 1):
            if list_type == 'bullet':
                self.text_widget.insert(tk.END, "\n  \u2022 ", list_type)
            else:
                self.text_widget.insert(tk.END, f"\n  {i}. ", list_type)
            self.process_children(item, base_url)
        self.text_widget.insert(tk.END, "\n\n")
    
    def handle_list_item(self, element, base_url):
        """Handle list item elements."""
        self.process_children(element, base_url)
    
    def handle_image(self, element, base_url):
        """Handle image elements."""
        alt = element.get('alt', '')
        src = element.get('src', '')
        title = element.get('title', '')
        display_text = alt or title or "Image"
        self.text_widget.insert(tk.END, f"[\U0001f5bc {display_text}] ", 'italic')
    
    def handle_separator(self, element, base_url):
        """Handle horizontal rule."""
        self.text_widget.insert(tk.END, "\n" + "\u2500" * 50 + "\n", "separator")
    
    def handle_div(self, element, base_url):
        """Handle div elements."""
        self.process_children(element, base_url)
        self.text_widget.insert(tk.END, "\n")
    
    def handle_span(self, element, base_url):
        """Handle span elements."""
        self.process_children(element, base_url)
    
    def process_children(self, element, base_url):
        """Process child elements."""
        for child in element.children:
            if isinstance(child, str):
                text = child.strip()
                if text:
                    self.text_widget.insert(tk.END, text + " ")
            else:
                self.process_element(child, base_url)
    
    def display_source(self):
        """Display page source code."""
        self.content_notebook.select(1)  # Show HTML tab for source
        self.text_widget.config(state=tk.NORMAL)
        self.text_widget.delete(1.0, tk.END)
        self.text_widget.insert(tk.END, "Page Source:\n", "h2")
        self.text_widget.insert(tk.END, "=" * 50 + "\n\n")
        self.text_widget.insert(tk.END, self.page_source, 'code')
        self.text_widget.config(state=tk.DISABLED)
    
    def toggle_view(self):
        """Toggle between rendered and source view."""
        self.showing_source = (self.view_mode.get() == 'source')
        if self.current_url:
            if self.showing_source:
                self.display_source()
            else:
                soup = BeautifulSoup(self.page_source, 'html.parser')
                self.display_html(soup, self.current_url)
                if self.is_directory_listing:
                    self.content_notebook.select(0)  # Show directory view
    
    def on_link_enter(self, tag):
        """Handle mouse entering a link."""
        self.text_widget.tag_config(tag, foreground=self.colors['link_hover'])
        self.status_var.set(self.links.get(tag, ''))
    
    def on_link_leave(self, tag):
        """Handle mouse leaving a link."""
        self.text_widget.tag_config(tag, foreground=self.colors['link'])
        self.status_var.set(self.current_url or "Ready")
    
    def on_link_click(self, event):
        """Handle link clicks."""
        index = self.text_widget.index(f"@{event.x},{event.y}")
        tags = self.text_widget.tag_names(index)
        
        for tag in tags:
            if tag in self.links:
                self.load_url(self.links[tag])
                return
    
    def on_right_click(self, event):
        """Handle right-click for context menu."""
        # Could add context menu here
        pass
    
    def on_mouse_move(self, event):
        """Change cursor when hovering over links."""
        index = self.text_widget.index(f"@{event.x},{event.y}")
        tags = self.text_widget.tag_names(index)
        
        is_link = any(tag in self.links for tag in tags)
        self.text_widget.config(cursor="hand2" if is_link else "")
    
    def show_find_dialog(self):
        """Show find text dialog."""
        if self.find_window and self.find_window.winfo_exists():
            self.find_window.lift()
            return
        
        self.find_window = tk.Toplevel(self.parent)
        self.find_window.title("Find")
        self.find_window.geometry("300x100")
        self.find_window.transient(self.parent)
        
        frame = ttk.Frame(self.find_window, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(frame, text="Find:").pack(side=tk.LEFT)
        find_entry = ttk.Entry(frame, width=30)
        find_entry.pack(side=tk.LEFT, padx=5)
        find_entry.focus()
        
        def do_find():
            query = find_entry.get()
            if query:
                self.find_text(query)
        
        ttk.Button(frame, text="Find", command=do_find).pack(side=tk.LEFT)
        find_entry.bind('<Return>', lambda e: do_find())
    
    def find_text(self, query):
        """Find text in the page."""
        self.text_widget.tag_remove('found', 1.0, tk.END)
        
        idx = 1.0
        while True:
            idx = self.text_widget.search(query, idx, nocase=True, stopindex=tk.END)
            if not idx:
                break
            lastidx = f"{idx}+{len(query)}c"
            self.text_widget.tag_add('found', idx, lastidx)
            idx = lastidx
        
        self.text_widget.tag_config('found', background='yellow', foreground='black')
    
    def show_menu(self):
        """Show browser menu."""
        menu = tk.Menu(self.parent, tearoff=0)
        menu.add_command(label=u"\U0001f4be Save Page", command=self.save_page)
        menu.add_command(label=u"\u2398 Copy URL", command=self.copy_url)
        menu.add_separator()
        menu.add_command(label=u"\u2699 Settings", command=self.show_settings)
        
        try:
            menu.post(self.parent.winfo_pointerx(), self.parent.winfo_pointery())
        finally:
            menu.grab_release()
    
    def save_page(self):
        """Save current page to file."""
        if self.page_source:
            from tkinter import filedialog
            filename = filedialog.asksaveasfilename(
                defaultextension=".html",
                filetypes=[("HTML files", "*.html"), ("Text files", "*.txt"), ("All files", "*.*")]
            )
            if filename:
                try:
                    with open(filename, 'w', encoding='utf-8') as f:
                        f.write(self.page_source)
                    self.status_var.set(f"Saved: {filename}")
                except Exception as e:
                    self.status_var.set(f"Save failed: {str(e)}")
    
    def copy_url(self):
        """Copy current URL to clipboard."""
        self.parent.clipboard_clear()
        self.parent.clipboard_append(self.current_url)
        self.status_var.set("URL copied to clipboard")
    
    def show_settings(self):
        """Show browser settings dialog."""
        # Could add browser-specific settings here
        pass
    
    def add_bookmark(self):
        """Add current page to bookmarks."""
        if self.current_url and self.current_url not in self.bookmarks:
            self.bookmarks.append(self.current_url)
            self.update_bookmarks_listbox()
            self.status_var.set("Bookmark added")
    
    def remove_bookmark(self):
        """Remove selected bookmark."""
        selection = self.bookmarks_listbox.curselection()
        if selection:
            idx = selection[0]
            if 0 <= idx < len(self.bookmarks):
                del self.bookmarks[idx]
                self.update_bookmarks_listbox()
                self.status_var.set("Bookmark removed")
    
    def update_bookmarks_listbox(self):
        """Update bookmarks listbox."""
        self.bookmarks_listbox.delete(0, tk.END)
        for url in self.bookmarks:
            # Show shortened URL
            display = url[:50] + "..." if len(url) > 50 else url
            self.bookmarks_listbox.insert(tk.END, display)
    
    def on_bookmark_double_click(self, event):
        """Handle bookmark double-click."""
        selection = self.bookmarks_listbox.curselection()
        if selection:
            idx = selection[0]
            if 0 <= idx < len(self.bookmarks):
                self.load_url(self.bookmarks[idx])
    
    def update_history_listbox(self):
        """Update history listbox."""
        self.history_listbox.delete(0, tk.END)
        for url in reversed(self.history):
            display = url[:50] + "..." if len(url) > 50 else url
            self.history_listbox.insert(tk.END, display)
    
    def on_history_double_click(self, event):
        """Handle history double-click."""
        selection = self.history_listbox.curselection()
        if selection:
            idx = selection[0]
            url_idx = len(self.history) - 1 - idx
            if 0 <= url_idx < len(self.history):
                self.load_url(self.history[url_idx])
    
    def clear_history(self):
        """Clear browsing history."""
        self.history = []
        self.history_pos = -1
        self.update_history_listbox()
        self.update_nav_buttons()
        self.status_var.set("History cleared")


class HttpDownloaderGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("HTTP Directory Downloader with Browser")
        self.root.geometry("1200x900")
        self.root.minsize(900, 600)
        
        # Variables
        self.url_var = tk.StringVar()
        self.output_var = tk.StringVar(value=os.path.abspath("./downloads"))
        self.extensions_var = tk.StringVar(value="mkv,mp4,avi,mov,wmv,flv,webm,m4v")
        self.pattern_var = tk.StringVar()
        self.bandwidth_var = tk.StringVar(value="0")
        self.proxy_ip_var = tk.StringVar()
        self.proxy_port_var = tk.StringVar()
        self.proxy_protocol_var = tk.StringVar(value="HTTP")
        self.proxy_auth_var = tk.BooleanVar(value=False)
        self.proxy_user_var = tk.StringVar()
        self.proxy_pass_var = tk.StringVar()
        self.use_proxy_var = tk.BooleanVar(value=False)
        self.proxy_status_var = tk.StringVar(value="No proxy configured")
        
        # YAML proxy file variables
        self.yaml_file_var = tk.StringVar()
        self.use_yaml_proxy_var = tk.BooleanVar(value=False)
        self.yaml_proxies = []  # List to store loaded proxies from YAML
        self.selected_yaml_proxy = tk.StringVar()  # Currently selected proxy name
        self.files = []
        self.is_downloading = False
        self.download_thread = None
        self.current_process = None
        self.failed_files = []
        
        # Progress tracking
        self.current_file_size = 0
        self.current_file_downloaded = 0
        
        self.setup_ui()
        self.check_wget()
        self.load_settings()
    
    def check_wget(self):
        """Check if wget is available."""
        try:
            result = subprocess.run(['wget', '--version'], capture_output=True, text=True, shell=True)
            if result.returncode != 0:
                self.log("WARNING: wget may not be properly installed")
        except Exception as e:
            self.log(f"WARNING: wget check failed: {e}")
    
    def get_settings_file(self):
        """Get path to settings file."""
        return os.path.join(os.path.dirname(__file__), 'downloader_settings.json')
    
    def load_settings(self):
        """Load saved settings."""
        settings_file = self.get_settings_file()
        try:
            if os.path.exists(settings_file):
                with open(settings_file, 'r') as f:
                    settings = json.load(f)
                self.url_var.set(settings.get('url', ""))
                self.output_var.set(settings.get('output_dir', os.path.abspath("./downloads")))
                self.extensions_var.set(settings.get('extensions', "mkv,mp4,avi,mov,wmv,flv,webm,m4v"))
                self.bandwidth_var.set(settings.get('bandwidth', "0"))
                self.proxy_ip_var.set(settings.get('proxy_ip', ""))
                self.proxy_port_var.set(settings.get('proxy_port', ""))
                self.proxy_protocol_var.set(settings.get('proxy_protocol', "HTTP"))
                self.proxy_auth_var.set(settings.get('proxy_auth', False))
                self.proxy_user_var.set(settings.get('proxy_user', ""))
                self.proxy_pass_var.set(settings.get('proxy_pass', ""))
                self.use_proxy_var.set(settings.get('use_proxy', False))
                self.yaml_file_var.set(settings.get('yaml_file', ""))
                # Auto-load YAML proxies if file exists
                yaml_file = self.yaml_file_var.get()
                if yaml_file and os.path.exists(yaml_file):
                    self.root.after(100, self.load_yaml_proxies)
                self.log("Settings loaded")
        except Exception as e:
            self.log(f"Could not load settings: {e}")
    
    def save_settings(self):
        """Save current settings."""
        settings_file = self.get_settings_file()
        try:
            settings = {
                'url': self.url_var.get(),
                'output_dir': self.output_var.get(),
                'extensions': self.extensions_var.get(),
                'bandwidth': self.bandwidth_var.get(),
                'proxy_ip': self.proxy_ip_var.get(),
                'proxy_port': self.proxy_port_var.get(),
                'proxy_protocol': self.proxy_protocol_var.get(),
                'proxy_auth': self.proxy_auth_var.get(),
                'proxy_user': self.proxy_user_var.get(),
                'proxy_pass': self.proxy_pass_var.get(),
                'use_proxy': self.use_proxy_var.get(),
                'yaml_file': self.yaml_file_var.get()
            }
            with open(settings_file, 'w') as f:
                json.dump(settings, f)
            self.log("Settings saved")
        except Exception as e:
            self.log(f"Could not save settings: {e}")
    
    def get_proxy_dict(self):
        """Get proxy dictionary for requests library."""
        if self.use_proxy_var.get():
            ip = self.proxy_ip_var.get().strip()
            port = self.proxy_port_var.get().strip()
            protocol = self.proxy_protocol_var.get()
            auth_enabled = self.proxy_auth_var.get()
            username = self.proxy_user_var.get()
            password = self.proxy_pass_var.get()
            
            if ip and port:
                if protocol == "HTTP":
                    if auth_enabled and username and password:
                        proxy_url = f"http://{username}:{password}@{ip}:{port}"
                    else:
                        proxy_url = f"http://{ip}:{port}"
                    return {
                        'http': proxy_url,
                        'https': proxy_url
                    }
                elif protocol in ["SOCKS4", "SOCKS5"]:
                    # For requests with socks support
                    socks_ver = "socks5" if protocol == "SOCKS5" else "socks4"
                    if auth_enabled and username and password and protocol == "SOCKS5":
                        # SOCKS5 with auth
                        proxy_url = f"{socks_ver}://{username}:{password}@{ip}:{port}"
                    else:
                        proxy_url = f"{socks_ver}://{ip}:{port}"
                    return {
                        'http': proxy_url,
                        'https': proxy_url
                    }
        return None
    
    def create_socks_session(self):
        """Create a requests session configured for SOCKS proxy."""
        if not SOCKS_AVAILABLE:
            return None
            
        import socks as socks_module
            
        ip = self.proxy_ip_var.get().strip()
        port = int(self.proxy_port_var.get().strip())
        protocol = self.proxy_protocol_var.get()
        auth_enabled = self.proxy_auth_var.get()
        username = self.proxy_user_var.get()
        password = self.proxy_pass_var.get()
        
        # Set default socket
        old_socket = socket.socket
        
        if protocol == "SOCKS5":
            socks_module.set_default_proxy(socks_module.SOCKS5, ip, port, 
                                   username=username if auth_enabled else None,
                                   password=password if auth_enabled else None)
        elif protocol == "SOCKS4":
            socks_module.set_default_proxy(socks_module.SOCKS4, ip, port)
        else:
            return None
            
        socket.socket = socks_module.socksocket
        
        # Create session
        session = requests.Session()
        
        return session, old_socket
    
    def apply_yaml_proxy(self):
        """Apply selected YAML proxy to manual fields for use."""
        selected_name = self.selected_yaml_proxy.get()
        if not selected_name:
            return None
        
        # Find the proxy in loaded YAML
        for proxy in self.yaml_proxies:
            name = proxy.get('name', f"{proxy['server']}:{proxy['port']}")
            if name == selected_name:
                return proxy
        return None
    
    def test_proxy(self):
        """Test proxy connection."""
        ip = self.proxy_ip_var.get().strip()
        port = self.proxy_port_var.get().strip()
        protocol = self.proxy_protocol_var.get()
        
        if not ip or not port:
            messagebox.showerror("Error", "Please enter both Proxy Address and Port")
            return
        
        try:
            port_num = int(port)
            if port_num < 1 or port_num > 65535:
                messagebox.showerror("Error", "Port must be between 1 and 65535")
                return
        except ValueError:
            messagebox.showerror("Error", "Port must be a valid number")
            return
        
        if protocol in ["SOCKS4", "SOCKS5"] and not SOCKS_AVAILABLE:
            messagebox.showerror("Error", "PySocks is not installed. Please install it with:\npip install requests[socks]\nor\npip install pysocks")
            return
        
        self.log(f"Testing {protocol} proxy: {ip}:{port}")
        self.proxy_status_var.set("Testing...")
        
        # Test in background thread
        threading.Thread(target=self._do_test_proxy, args=(ip, port_num, protocol), daemon=True).start()
    
    def _do_test_proxy(self, ip, port, protocol):
        """Test proxy connection in background."""
        try:
            # Test TCP connection first
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(10)
            result = sock.connect_ex((ip, port))
            sock.close()
            
            if result != 0:
                self.root.after(0, lambda: self.proxy_status_var.set(f"TCP Connection Failed"))
                self.root.after(0, lambda: self.log(f"ERROR: Cannot connect to proxy {ip}:{port} (TCP)"))
                return
            
            auth_enabled = self.proxy_auth_var.get()
            username = self.proxy_user_var.get()
            password = self.proxy_pass_var.get()
            
            success = False
            error_msg = ""
            
            if protocol == "HTTP":
                # Test HTTP through proxy
                if auth_enabled and username and password:
                    proxy_url = f"http://{username}:{password}@{ip}:{port}"
                else:
                    proxy_url = f"http://{ip}:{port}"
                    
                proxies = {
                    'http': proxy_url,
                    'https': proxy_url
                }
                
                # Try to connect through proxy to a test URL
                test_urls = [
                    'http://httpbin.org/ip',
                    'http://icanhazip.com',
                    'http://ipinfo.io/json'
                ]
                
                for test_url in test_urls:
                    try:
                        response = requests.get(test_url, proxies=proxies, timeout=15)
                        if response.status_code == 200:
                            success = True
                            self.root.after(0, lambda: self.log(f"HTTP Proxy working! Connected via: {ip}:{port}"))
                            break
                    except Exception as e:
                        error_msg = str(e)
                        continue
                        
            elif protocol in ["SOCKS4", "SOCKS5"]:
                # Test SOCKS proxy
                import socks as socks_module
                
                # Save old socket
                old_socket = socket.socket
                
                try:
                    if protocol == "SOCKS5":
                        socks_module.set_default_proxy(socks_module.SOCKS5, ip, port,
                                              username=username if auth_enabled else None,
                                              password=password if auth_enabled else None)
                    else:  # SOCKS4
                        socks_module.set_default_proxy(socks_module.SOCKS4, ip, port)
                    
                    socket.socket = socks_module.socksocket
                    
                    # Test with requests using monkey-patched socket
                    test_urls = [
                        'http://httpbin.org/ip',
                        'http://icanhazip.com',
                        'http://ipinfo.io/json'
                    ]
                    
                    for test_url in test_urls:
                        try:
                            response = requests.get(test_url, timeout=15)
                            if response.status_code == 200:
                                success = True
                                auth_str = " (with auth)" if (auth_enabled and username) else ""
                                self.root.after(0, lambda: self.log(f"{protocol} Proxy working{auth_str}! Connected via: {ip}:{port}"))
                                break
                        except Exception as e:
                            error_msg = str(e)
                            continue
                            
                finally:
                    # Restore original socket
                    socket.socket = old_socket
            
            if success:
                self.root.after(0, lambda: self.proxy_status_var.set(f"Active: {ip}:{port}"))
                self.root.after(0, lambda: self.use_proxy_var.set(True))
                self.root.after(0, lambda proto=protocol: messagebox.showinfo("Success", f"{proto} Proxy is active and working!\n{ip}:{port}"))
            else:
                self.root.after(0, lambda: self.proxy_status_var.set(f"Test Failed"))
                self.root.after(0, lambda: self.log(f"ERROR: Proxy TCP OK but test failed: {error_msg}"))
                
        except Exception as e:
            self.root.after(0, lambda: self.proxy_status_var.set(f"Error: {str(e)[:30]}"))
            self.root.after(0, lambda: self.log(f"ERROR testing proxy: {e}"))
    
    def _on_mousewheel(self, event):
        """Handle mouse wheel scrolling."""
        if event.num == 4 or event.delta > 0:
            self.canvas.yview_scroll(-1, "units")
        elif event.num == 5 or event.delta < 0:
            self.canvas.yview_scroll(1, "units")
    
    def _on_canvas_configure(self, event):
        """Handle canvas resize to update scrollable frame width."""
        self.canvas.itemconfig(self.canvas_window, width=event.width)
    
    def setup_ui(self):
        # Create notebook for tabs
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # Tab 1: Downloader
        self.downloader_frame = ttk.Frame(self.notebook, padding="5")
        self.notebook.add(self.downloader_frame, text="Downloader")
        
        # Tab 2: Browser
        self.browser_frame = ttk.Frame(self.notebook, padding="5")
        self.notebook.add(self.browser_frame, text="Browser")
        
        # Initialize browser
        self.browser = EnhancedBrowser(self.browser_frame, self.get_active_proxy_config, self.load_url_in_downloader)
        
        # Bind tab change event to update browser proxy status
        self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)
        
        # Main container with canvas and scrollbar (for downloader tab)
        main_container = ttk.Frame(self.downloader_frame)
        main_container.pack(fill=tk.BOTH, expand=True)
        
        # Create canvas and scrollbar
        self.canvas = tk.Canvas(main_container, highlightthickness=0)
        scrollbar = ttk.Scrollbar(main_container, orient="vertical", command=self.canvas.yview)
        
        # Create scrollable frame inside canvas
        self.scrollable_frame = ttk.Frame(self.canvas, padding="10")
        
        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
        )
        
        self.canvas_window = self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        
        # Configure scrollbar
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.canvas.configure(yscrollcommand=scrollbar.set)
        
        # Bind mouse wheel to scroll
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
        self.canvas.bind_all("<Button-4>", self._on_mousewheel)
        self.canvas.bind_all("<Button-5>", self._on_mousewheel)
        
        # Bind canvas resize to update frame width
        self.canvas.bind("<Configure>", self._on_canvas_configure)
        
        # Main content frame (for all UI elements)
        main_frame = self.scrollable_frame
        main_frame.columnconfigure(1, weight=1)
        
        # Title
        title_label = ttk.Label(
            main_frame, 
            text="Ganjirs - HTTP Directory Downloader", 
            font=('Helvetica', 16, 'bold')
        )
        title_label.grid(row=0, column=0, columnspan=3, pady=(10, 0))

        # Creator
        creator_label = ttk.Label(
            main_frame,
            text="Created by RAKIB",
            font=('Helvetica', 10, 'italic')
        )
        creator_label.grid(row=0, column=0, columnspan=3, pady=(40, 10))
        
        # URL Input
        ttk.Label(main_frame, text="URL:").grid(row=1, column=0, sticky=tk.W, pady=5)
        url_entry = ttk.Entry(main_frame, textvariable=self.url_var, width=70)
        url_entry.grid(row=1, column=1, sticky="we", pady=5, padx=5)
        url_entry.bind('<FocusOut>', lambda e: self.save_settings())
        
        scan_btn = ttk.Button(main_frame, text="Scan", command=self.scan_files, width=15)
        scan_btn.grid(row=1, column=2, pady=5, padx=5)
        
        # Output Directory
        ttk.Label(main_frame, text="Output:").grid(row=2, column=0, sticky=tk.W, pady=5)
        output_entry = ttk.Entry(main_frame, textvariable=self.output_var, width=70)
        output_entry.grid(row=2, column=1, sticky="we", pady=5, padx=5)
        
        browse_btn = ttk.Button(main_frame, text="Browse", command=self.browse_output, width=15)
        browse_btn.grid(row=2, column=2, pady=5, padx=5)
        
        # YAML Proxy File Frame
        yaml_frame = ttk.LabelFrame(main_frame, text="YAML Proxy File", padding="10")
        yaml_frame.grid(row=3, column=0, columnspan=3, sticky="we", pady=10)
        yaml_frame.columnconfigure(1, weight=1)
        
        # Row 0: YAML file path
        ttk.Label(yaml_frame, text="YAML File:").grid(row=0, column=0, sticky=tk.W)
        yaml_entry = ttk.Entry(yaml_frame, textvariable=self.yaml_file_var, width=60)
        yaml_entry.grid(row=0, column=1, sticky="we", padx=5)
        yaml_entry.bind('<FocusOut>', lambda e: self.save_settings())
        
        yaml_btn_frame = ttk.Frame(yaml_frame)
        yaml_btn_frame.grid(row=0, column=2, padx=5)
        
        ttk.Button(yaml_btn_frame, text="Browse", command=self.browse_yaml_file, width=10).grid(row=0, column=0, padx=2)
        ttk.Button(yaml_btn_frame, text="Load", command=self.load_yaml_proxies, width=10).grid(row=0, column=1, padx=2)
        
        # Row 1: Proxy selector
        ttk.Label(yaml_frame, text="Select Proxy:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
        
        selector_frame = ttk.Frame(yaml_frame)
        selector_frame.grid(row=1, column=1, columnspan=2, sticky="we", pady=(10, 0))
        selector_frame.columnconfigure(0, weight=1)
        
        self.yaml_proxy_combo = ttk.Combobox(selector_frame, textvariable=self.selected_yaml_proxy, 
                                              state="readonly", width=50)
        self.yaml_proxy_combo.grid(row=0, column=0, sticky="we")
        self.yaml_proxy_combo.bind('<<ComboboxSelected>>', self.on_yaml_proxy_selected)
        
        ttk.Button(selector_frame, text="Use Selected", command=self.use_selected_yaml_proxy, width=12).grid(row=0, column=1, padx=(10, 0))
        
        # Checkbox to use YAML proxy directly
        ttk.Checkbutton(yaml_frame, text="Use YAML Proxy for Downloads", variable=self.use_yaml_proxy_var).grid(row=2, column=0, columnspan=3, sticky=tk.W, pady=(10, 0))
        
        # Separator
        ttk.Separator(main_frame, orient='horizontal').grid(row=4, column=0, columnspan=3, sticky="we", pady=5)
        
        # Proxy Configuration Frame (Manual)
        proxy_frame = ttk.LabelFrame(main_frame, text="Manual Proxy Configuration", padding="10")
        proxy_frame.grid(row=5, column=0, columnspan=3, sticky="we", pady=10)
        proxy_frame.columnconfigure(1, weight=0)
        proxy_frame.columnconfigure(3, weight=0)
        
        # Row 0: Server settings
        ttk.Label(proxy_frame, text="Address:").grid(row=0, column=0, sticky=tk.W)
        proxy_ip_entry = ttk.Entry(proxy_frame, textvariable=self.proxy_ip_var, width=20)
        proxy_ip_entry.grid(row=0, column=1, sticky=tk.W, padx=5)
        
        ttk.Label(proxy_frame, text="Port:").grid(row=0, column=2, sticky=tk.W, padx=(20, 0))
        proxy_port_entry = ttk.Entry(proxy_frame, textvariable=self.proxy_port_var, width=10)
        proxy_port_entry.grid(row=0, column=3, sticky=tk.W, padx=5)
        
        ttk.Label(proxy_frame, text="Protocol:").grid(row=0, column=4, sticky=tk.W, padx=(20, 0))
        self.proxy_protocol_combo = ttk.Combobox(proxy_frame, textvariable=self.proxy_protocol_var, 
                                                  values=["HTTP", "SOCKS4", "SOCKS5"], 
                                                  width=12, state="readonly")
        self.proxy_protocol_combo.grid(row=0, column=5, sticky=tk.W, padx=5)
        self.proxy_protocol_combo.set("HTTP")
        
        # Row 1: Authentication
        auth_frame = ttk.LabelFrame(proxy_frame, text="Authentication", padding="5")
        auth_frame.grid(row=1, column=0, columnspan=6, sticky="we", pady=(10, 0))
        auth_frame.columnconfigure(1, weight=1)
        
        self.proxy_auth_check = ttk.Checkbutton(auth_frame, text="Enable", variable=self.proxy_auth_var)
        self.proxy_auth_check.grid(row=0, column=0, sticky=tk.W)
        
        ttk.Label(auth_frame, text="Username:").grid(row=0, column=1, sticky=tk.W, padx=(20, 0))
        self.proxy_user_entry = ttk.Entry(auth_frame, textvariable=self.proxy_user_var, width=15)
        self.proxy_user_entry.grid(row=0, column=2, sticky=tk.W, padx=5)
        
        ttk.Label(auth_frame, text="Password:").grid(row=0, column=3, sticky=tk.W, padx=(20, 0))
        self.proxy_pass_entry = ttk.Entry(auth_frame, textvariable=self.proxy_pass_var, width=15, show="*")
        self.proxy_pass_entry.grid(row=0, column=4, sticky=tk.W, padx=5)
        
        # Row 2: Buttons and status
        btn_frame = ttk.Frame(proxy_frame)
        btn_frame.grid(row=2, column=0, columnspan=6, sticky="w", pady=(10, 0))
        
        ttk.Button(btn_frame, text="Test Proxy", command=self.test_proxy, width=12).grid(row=0, column=0, padx=(0, 5))
        ttk.Checkbutton(btn_frame, text="Use Proxy", variable=self.use_proxy_var).grid(row=0, column=1, padx=5)
        
        # Proxy status
        self.proxy_status_label = ttk.Label(btn_frame, textvariable=self.proxy_status_var, 
                                            font=('Consolas', 9), foreground='gray')
        self.proxy_status_label.grid(row=0, column=2, sticky=tk.W, padx=(20, 0))
        
        # Filters Frame
        filters_frame = ttk.LabelFrame(main_frame, text="Filters & Options", padding="10")
        filters_frame.grid(row=6, column=0, columnspan=3, sticky="we", pady=10)
        filters_frame.columnconfigure(1, weight=1)
        filters_frame.columnconfigure(3, weight=1)
        
        ttk.Label(filters_frame, text="Extensions:").grid(row=0, column=0, sticky=tk.W)
        ext_entry = ttk.Entry(filters_frame, textvariable=self.extensions_var, width=25)
        ext_entry.grid(row=0, column=1, sticky=tk.W, padx=5)
        
        ttk.Label(filters_frame, text="Pattern:").grid(row=0, column=2, sticky=tk.W, padx=(20, 0))
        pat_entry = ttk.Entry(filters_frame, textvariable=self.pattern_var, width=25)
        pat_entry.grid(row=0, column=3, sticky=tk.W, padx=5)
        
        ttk.Label(filters_frame, text="Speed Limit (KB/s, 0=unlimited):").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
        bandwidth_entry = ttk.Entry(filters_frame, textvariable=self.bandwidth_var, width=10)
        bandwidth_entry.grid(row=1, column=1, sticky=tk.W, padx=5, pady=(10, 0))
        
        # Quick preset buttons
        preset_frame = ttk.Frame(filters_frame)
        preset_frame.grid(row=1, column=2, columnspan=2, sticky=tk.W, padx=(20, 0), pady=(10, 0))
        
        ttk.Button(preset_frame, text="Video", command=lambda: self.set_extensions("mkv,mp4,avi,mov,wmv,flv,webm,m4v"), width=8).grid(row=0, column=0, padx=2)
        ttk.Button(preset_frame, text="Audio", command=lambda: self.set_extensions("mp3,wav,flac,aac,ogg,wma,m4a"), width=8).grid(row=0, column=1, padx=2)
        ttk.Button(preset_frame, text="Docs", command=lambda: self.set_extensions("pdf,doc,docx,xls,xlsx,ppt,pptx,txt"), width=8).grid(row=0, column=2, padx=2)
        ttk.Button(preset_frame, text="Archives", command=lambda: self.set_extensions("zip,rar,7z,tar,gz,bz2,xz"), width=8).grid(row=0, column=3, padx=2)
        ttk.Button(preset_frame, text="Images", command=lambda: self.set_extensions("jpg,jpeg,png,gif,bmp,webp,svg"), width=8).grid(row=0, column=4, padx=2)
        ttk.Button(preset_frame, text="All", command=lambda: self.set_extensions(""), width=8).grid(row=0, column=5, padx=2)
        
        # Files List Frame
        list_frame = ttk.LabelFrame(main_frame, text="Files (Click checkbox to select)", padding="10")
        list_frame.grid(row=7, column=0, columnspan=3, sticky="nsew", pady=10)
        list_frame.columnconfigure(0, weight=1)
        list_frame.rowconfigure(0, weight=1)
        
        # Treeview for files
        columns = ('select', 'name', 'status', 'size', 'progress')
        self.tree = ttk.Treeview(list_frame, columns=columns, show='headings', height=12)
        
        self.tree.heading('select', text='[ ]')
        self.tree.heading('name', text='Filename')
        self.tree.heading('status', text='Status')
        self.tree.heading('size', text='Size')
        self.tree.heading('progress', text='Progress')
        
        self.tree.column('select', width=40, anchor='center')
        self.tree.column('name', width=450)
        self.tree.column('status', width=80, anchor='center')
        self.tree.column('size', width=80, anchor='center')
        self.tree.column('progress', width=100, anchor='center')
        
        # Scrollbar
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)
        
        self.tree.grid(row=0, column=0, sticky="nsew")
        scrollbar.grid(row=0, column=1, sticky="ns")
        
        # Select buttons
        btn_frame = ttk.Frame(list_frame)
        btn_frame.grid(row=1, column=0, columnspan=2, pady=5)
        
        ttk.Button(btn_frame, text="Select All", command=self.select_all).grid(row=0, column=0, padx=5)
        ttk.Button(btn_frame, text="Select None", command=self.select_none).grid(row=0, column=1, padx=5)
        ttk.Button(btn_frame, text="Select Video", command=self.select_video).grid(row=0, column=2, padx=5)
        ttk.Button(btn_frame, text="Invert Selection", command=self.invert_selection).grid(row=0, column=3, padx=5)
        ttk.Button(btn_frame, text="Retry Failed", command=self.retry_failed).grid(row=0, column=4, padx=5)
        
        # Bind checkbox click
        self.tree.bind('<ButtonRelease-1>', self.on_tree_click)
        
        # Progress Frame
        progress_frame = ttk.LabelFrame(main_frame, text="Download Progress", padding="10")
        progress_frame.grid(row=8, column=0, columnspan=3, sticky="we", pady=10)
        progress_frame.columnconfigure(0, weight=1)
        
        # Overall progress
        ttk.Label(progress_frame, text="Overall:").grid(row=0, column=0, sticky=tk.W)
        self.progress_var = tk.DoubleVar(value=0)
        self.progress_bar = ttk.Progressbar(
            progress_frame, 
            variable=self.progress_var, 
            maximum=100, 
            length=800,
            mode='determinate'
        )
        self.progress_bar.grid(row=1, column=0, sticky="we", padx=5, pady=5)
        
        # Current file progress
        ttk.Label(progress_frame, text="Current File:").grid(row=2, column=0, sticky=tk.W)
        self.current_progress_var = tk.DoubleVar(value=0)
        self.current_progress_bar = ttk.Progressbar(
            progress_frame, 
            variable=self.current_progress_var, 
            maximum=100, 
            length=800,
            mode='determinate'
        )
        self.current_progress_bar.grid(row=3, column=0, sticky="we", padx=5, pady=5)
        
        # Status labels
        self.status_var = tk.StringVar(value="Ready - Enter URL and click Scan")
        self.status_label = ttk.Label(progress_frame, textvariable=self.status_var, font=('Helvetica', 10, 'bold'))
        self.status_label.grid(row=4, column=0, pady=5)
        
        self.current_file_var = tk.StringVar(value="")
        self.current_file_label = ttk.Label(progress_frame, textvariable=self.current_file_var)
        self.current_file_label.grid(row=5, column=0, pady=2)
        
        # Speed and ETA frame
        info_frame = ttk.Frame(progress_frame)
        info_frame.grid(row=6, column=0, pady=5)
        
        self.downloaded_var = tk.StringVar(value="Downloaded: -")
        ttk.Label(info_frame, textvariable=self.downloaded_var, font=('Consolas', 9)).grid(row=0, column=0, padx=15)
        
        self.speed_var = tk.StringVar(value="Speed: -")
        ttk.Label(info_frame, textvariable=self.speed_var, font=('Consolas', 9)).grid(row=0, column=1, padx=15)
        
        self.eta_var = tk.StringVar(value="ETA: -")
        ttk.Label(info_frame, textvariable=self.eta_var, font=('Consolas', 9)).grid(row=0, column=2, padx=15)
        
        self.elapsed_var = tk.StringVar(value="Elapsed: -")
        ttk.Label(info_frame, textvariable=self.elapsed_var, font=('Consolas', 9)).grid(row=0, column=3, padx=15)
        
        # Log Area
        log_frame = ttk.LabelFrame(main_frame, text="Download Log", padding="5")
        log_frame.grid(row=9, column=0, columnspan=3, sticky="we", pady=5)
        log_frame.columnconfigure(0, weight=1)
        
        self.log_text = scrolledtext.ScrolledText(
            log_frame, 
            height=8, 
            wrap=tk.WORD,
            font=('Consolas', 9)
        )
        self.log_text.grid(row=0, column=0, sticky="we")
        
        # Action Buttons
        action_frame = ttk.Frame(main_frame)
        action_frame.grid(row=10, column=0, columnspan=3, pady=10)
        
        self.download_btn = ttk.Button(
            action_frame, 
            text="Download Selected", 
            command=self.start_download,
            width=25
        )
        self.download_btn.grid(row=0, column=0, padx=5)
        
        self.stop_btn = ttk.Button(
            action_frame, 
            text="Stop Download", 
            command=self.stop_download,
            width=20,
            state='disabled'
        )
        self.stop_btn.grid(row=0, column=1, padx=5)
        
        ttk.Button(action_frame, text="Clear Log", command=self.clear_log, width=15).grid(
            row=0, column=2, padx=5
        )
        
        ttk.Button(action_frame, text="Open Folder", command=self.open_folder, width=15).grid(
            row=0, column=3, padx=5
        )
        
        ttk.Button(action_frame, text="Save Settings", command=self.save_settings, width=15).grid(
            row=0, column=4, padx=5
        )
    
    def log(self, message):
        """Add message to log."""
        timestamp = datetime.now().strftime("%H:%M:%S")
        try:
            self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
            self.log_text.see(tk.END)
            self.log_text.update_idletasks()
        except:
            pass
    
    def clear_log(self):
        self.log_text.delete(1.0, tk.END)
    
    def open_folder(self):
        """Open output folder."""
        folder = self.output_var.get()
        if os.path.exists(folder):
            os.startfile(folder)
        else:
            messagebox.showinfo("Info", f"Folder does not exist yet: {folder}")
    
    def get_active_proxy_config(self):
        """Get currently active proxy configuration for browser."""
        # Check if using YAML proxy
        if self.use_yaml_proxy_var.get():
            selected_name = self.selected_yaml_proxy.get()
            if selected_name and self.yaml_proxies:
                for proxy in self.yaml_proxies:
                    name = proxy.get('name', f"{proxy['server']}:{proxy['port']}")
                    if name == selected_name:
                        return {
                            'type': proxy.get('type', 'http').upper(),
                            'server': proxy['server'],
                            'port': proxy['port'],
                            'username': proxy.get('username', ''),
                            'password': proxy.get('password', '')
                        }
        
        # Check if using manual proxy
        if self.use_proxy_var.get():
            ip = self.proxy_ip_var.get().strip()
            port = self.proxy_port_var.get().strip()
            if ip and port:
                try:
                    port_num = int(port)
                    return {
                        'type': self.proxy_protocol_var.get(),
                        'server': ip,
                        'port': port_num,
                        'username': self.proxy_user_var.get() if self.proxy_auth_var.get() else '',
                        'password': self.proxy_pass_var.get() if self.proxy_auth_var.get() else ''
                    }
                except ValueError:
                    pass
        
        return None
    
    def on_tab_changed(self, event=None):
        """Handle tab change event."""
        current_tab = self.notebook.index(self.notebook.select())
        if current_tab == 1:  # Browser tab
            # Update browser proxy status
            proxy_config = self.get_active_proxy_config()
            if proxy_config:
                proxy_str = f"{proxy_config['type']} {proxy_config['server']}:{proxy_config['port']}"
                self.browser.update_proxy_status(proxy_str)
            else:
                self.browser.update_proxy_status(None)
    
    def load_url_in_downloader(self, url):
        """Load a URL from the browser into the downloader tab and scan."""
        # Switch to downloader tab
        self.notebook.select(0)
        
        # Set the URL
        self.url_var.set(url)
        
        # Save settings
        self.save_settings()
        
        # Automatically scan
        self.scan_files()
    
    def browse_output(self):
        """Open directory browser."""
        directory = filedialog.askdirectory()
        if directory:
            self.output_var.set(directory)
    
    def browse_yaml_file(self):
        """Open file browser for YAML file."""
        file_path = filedialog.askopenfilename(
            title="Select YAML Proxy File",
            filetypes=[("YAML files", "*.yaml *.yml"), ("All files", "*.*")]
        )
        if file_path:
            self.yaml_file_var.set(file_path)
    
    def load_yaml_proxies(self):
        """Load proxies from YAML file."""
        if not YAML_AVAILABLE:
            messagebox.showerror("Error", "PyYAML is not installed. Please install it with:\npip install pyyaml")
            return
        
        file_path = self.yaml_file_var.get().strip()
        if not file_path:
            messagebox.showerror("Error", "Please select a YAML file first")
            return
        
        if not os.path.exists(file_path):
            messagebox.showerror("Error", f"File not found: {file_path}")
            return
        
        try:
            import yaml as yaml_module
            with open(file_path, 'r', encoding='utf-8') as f:
                data = yaml_module.safe_load(f)
            
            if not data or 'proxies' not in data:
                messagebox.showerror("Error", "Invalid YAML format. Expected 'proxies' section.")
                return
            
            self.yaml_proxies = []
            proxy_names = []
            
            for proxy in data['proxies']:
                if 'server' in proxy and 'port' in proxy:
                    self.yaml_proxies.append(proxy)
                    name = proxy.get('name', f"{proxy['server']}:{proxy['port']}")
                    proxy_names.append(name)
            
            if not proxy_names:
                messagebox.showwarning("Warning", "No valid proxies found in YAML file")
                return
            
            # Update combobox
            self.yaml_proxy_combo['values'] = proxy_names
            self.log(f"Loaded {len(proxy_names)} proxies from YAML file")
            messagebox.showinfo("Success", f"Loaded {len(proxy_names)} proxies from YAML file")
            
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load YAML file:\n{str(e)}")
            self.log(f"ERROR loading YAML file: {e}")
    
    def on_yaml_proxy_selected(self, event=None):
        """Handle YAML proxy selection."""
        selected_name = self.selected_yaml_proxy.get()
        if not selected_name:
            return
        
        # Find the proxy details
        for proxy in self.yaml_proxies:
            name = proxy.get('name', f"{proxy['server']}:{proxy['port']}")
            if name == selected_name:
                # Display info in log
                proxy_type = proxy.get('type', 'unknown').upper()
                server = proxy['server']
                port = proxy['port']
                has_auth = 'username' in proxy and 'password' in proxy
                auth_str = " (with auth)" if has_auth else ""
                self.log(f"Selected {proxy_type} proxy: {server}:{port}{auth_str}")
                break
    
    def use_selected_yaml_proxy(self):
        """Copy selected YAML proxy to manual fields."""
        selected_name = self.selected_yaml_proxy.get()
        if not selected_name:
            messagebox.showwarning("Warning", "Please select a proxy from the list first")
            return
        
        # Find the proxy details
        for proxy in self.yaml_proxies:
            name = proxy.get('name', f"{proxy['server']}:{proxy['port']}")
            if name == selected_name:
                # Copy to manual fields
                self.proxy_ip_var.set(proxy['server'])
                self.proxy_port_var.set(str(proxy['port']))
                
                # Set protocol
                proxy_type = proxy.get('type', 'http').upper()
                if proxy_type in ['SOCKS5', 'SOCKS4']:
                    self.proxy_protocol_var.set(proxy_type)
                else:
                    self.proxy_protocol_var.set('HTTP')
                
                # Set authentication
                if 'username' in proxy:
                    self.proxy_auth_var.set(True)
                    self.proxy_user_var.set(proxy['username'])
                    if 'password' in proxy:
                        self.proxy_pass_var.set(proxy['password'])
                else:
                    self.proxy_auth_var.set(False)
                    self.proxy_user_var.set('')
                    self.proxy_pass_var.set('')
                
                self.log(f"Copied proxy settings from YAML: {name}")
                self.proxy_status_var.set(f"Loaded from YAML: {name}")
                messagebox.showinfo("Success", f"Proxy settings copied from:\n{name}")
                break
    
    def is_file_link(self, href):
        """Check if href points to a file."""
        if not href:
            return False
        
        if href.startswith('http://') or href.startswith('https://'):
            return False
        
        skip_patterns = [
            r'^\?', r'^#', r'^javascript:', r'^mailto:', r'^tel:',
            r'/\s*$', r'^\.\.$', r'^\.$', r'^C=', r'^O=',
        ]
        for pattern in skip_patterns:
            if re.match(pattern, href, re.IGNORECASE):
                return False
        return True
    
    def scan_files(self):
        """Scan URL for files."""
        url = self.url_var.get().strip()
        if not url:
            messagebox.showerror("Error", "Please enter a URL")
            return
        
        self.status_var.set("Scanning...")
        self.log(f"Scanning: {url}")
        
        # Clear existing
        for item in self.tree.get_children():
            self.tree.delete(item)
        self.files = []
        
        # Disable scan button
        self.status_var.set("Scanning... Please wait")
        
        # Scan in background
        threading.Thread(target=self._do_scan, args=(url,), daemon=True).start()
    
    def _do_scan(self, url):
        """Background scanning."""
        try:
            self.log("Connecting to server...")
            
            protocol = self.proxy_protocol_var.get()
            proxies = None
            session = None
            old_socket = None
            
            # Check if using YAML proxy
            if self.use_yaml_proxy_var.get():
                yaml_proxy = self.apply_yaml_proxy()
                if yaml_proxy:
                    proxy_type = yaml_proxy.get('type', 'http').upper()
                    server = yaml_proxy['server']
                    port = yaml_proxy['port']
                    username = yaml_proxy.get('username', '')
                    password = yaml_proxy.get('password', '')
                    has_auth = username and password
                    auth_str = " (with auth)" if has_auth else ""
                    self.log(f"Using YAML {proxy_type} proxy: {server}:{port}{auth_str}")
                    
                    # Temporarily set proxy settings
                    self.proxy_ip_var.set(server)
                    self.proxy_port_var.set(str(port))
                    self.proxy_protocol_var.set(proxy_type if proxy_type in ['SOCKS5', 'SOCKS4'] else 'HTTP')
                    if has_auth:
                        self.proxy_auth_var.set(True)
                        self.proxy_user_var.set(username)
                        self.proxy_pass_var.set(password)
                    else:
                        self.proxy_auth_var.set(False)
                        self.proxy_user_var.set('')
                        self.proxy_pass_var.set('')
                    
                    if proxy_type in ["SOCKS5", "SOCKS4"]:
                        result = self.create_socks_session()
                        if result:
                            session, old_socket = result
                        else:
                            self.root.after(0, lambda: self.log("ERROR: Failed to create SOCKS session"))
                            return
                    else:
                        proxies = self.get_proxy_dict()
            elif self.use_proxy_var.get():
                ip = self.proxy_ip_var.get().strip()
                port = self.proxy_port_var.get().strip()
                self.log(f"Using {protocol} proxy: {ip}:{port}")
                
                if protocol in ["SOCKS4", "SOCKS5"]:
                    # Use SOCKS session
                    result = self.create_socks_session()
                    if result:
                        session, old_socket = result
                    else:
                        self.root.after(0, lambda: self.log("ERROR: Failed to create SOCKS session"))
                        return
                else:
                    # Use HTTP proxy
                    proxies = self.get_proxy_dict()
            
            # Make request
            if session:
                resp = session.get(url, timeout=30)
            else:
                resp = requests.get(url, timeout=30, proxies=proxies)
                
            # Restore socket if SOCKS was used
            if old_socket:
                socket.socket = old_socket
                
            resp.raise_for_status()
            soup = BeautifulSoup(resp.text, "html.parser")
            
            found_files = []
            links = soup.find_all("a")
            self.log(f"Found {len(links)} links on page")
            
            for a in links:
                href = a.get("href")
                if href and isinstance(href, str) and self.is_file_link(href):
                    decoded = unquote(href)
                    filename = decoded.split('/')[-1]
                    full_url = urljoin(url, href)
                    
                    # Apply filters
                    extensions = [e.strip().lower() for e in self.extensions_var.get().split(',') if e.strip()]
                    if extensions:
                        # Normalize extensions (remove leading dot if present)
                        normalized_exts = [e.lstrip('.') for e in extensions]
                        file_ext = filename.lower().split('.')[-1] if '.' in filename else ''
                        has_ext = file_ext in normalized_exts
                        if not has_ext:
                            continue
                    
                    pattern = self.pattern_var.get().strip()
                    if pattern and pattern.lower() not in filename.lower():
                        continue
                    
                    # Try to get file size from HEAD request
                    size_str = "-"
                    size_bytes = 0
                    try:
                        if session:
                            head_resp = session.head(full_url, timeout=10, allow_redirects=True)
                        else:
                            head_resp = requests.head(full_url, timeout=10, allow_redirects=True, proxies=proxies)
                        if 'Content-Length' in head_resp.headers:
                            size_bytes = int(head_resp.headers['Content-Length'])
                            size_str = self._format_size(size_bytes)
                    except:
                        pass
                    
                    found_files.append({
                        'url': full_url,
                        'name': filename,
                        'selected': True,
                        'size': size_str,
                        'size_bytes': size_bytes
                    })
            
            self.files = found_files
            
            # Update UI in main thread
            self.root.after(0, self._update_file_list)
            
        except Exception as e:
            self.root.after(0, lambda: self.log(f"ERROR: {str(e)}"))
            self.root.after(0, lambda: self.status_var.set("Scan failed - Check URL"))
    
    def _update_file_list(self):
        """Update the file list in UI."""
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        dest = self.output_var.get()
        
        for i, f in enumerate(self.files):
            remote_size = f.get('size_bytes', 0)
            status, local_size, progress = self._check_file_status(f['name'], dest, remote_size)
            # Use remote size if available, otherwise local size
            size_str = f.get('size', local_size)
            self.tree.insert('', 'end', iid=str(i), values=(
                '[x]' if f['selected'] else '[ ]',
                f['name'][:60],
                status,
                size_str,
                progress
            ))
        
        self.status_var.set(f"Found {len(self.files)} files - Select files and click Download")
        self.log(f"Found {len(self.files)} files. Select files and click Download.")
    
    def _check_file_status(self, filename, dest, remote_size_bytes=0):
        """Check if file already exists and is complete."""
        filepath = os.path.join(dest, filename)
        if os.path.exists(filepath):
            local_size = os.path.getsize(filepath)
            local_size_str = self._format_size(local_size)
            
            # If we know the remote size, check if file is complete
            if remote_size_bytes > 0:
                if local_size >= remote_size_bytes:
                    return "Done", local_size_str, "100%"
                else:
                    # File exists but is incomplete
                    progress = min(100, int((local_size / remote_size_bytes) * 100))
                    return "Incomplete", local_size_str, f"{progress}%"
            else:
                # Don't know remote size, assume it's done if it exists
                return "Done", local_size_str, "100%"
        return "Pending", "-", "0%"   
    
    def _format_size(self, size_bytes):
        """Format bytes to human readable."""
        if size_bytes == 0:
            return "0 B"
        for unit in ['B', 'KB', 'MB', 'GB']:
            if abs(size_bytes) < 1024.0:
                return f"{size_bytes:.1f} {unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.1f} TB"
    
    def on_tree_click(self, event):
        """Handle treeview click."""
        region = self.tree.identify("region", event.x, event.y)
        if region == "cell":
            column = self.tree.identify_column(event.x)
            if column == '#1':  # Checkbox column
                item = self.tree.identify_row(event.y)
                if item:
                    idx = int(item)
                    self.files[idx]['selected'] = not self.files[idx]['selected']
                    self.tree.set(item, 'select', '[x]' if self.files[idx]['selected'] else '[ ]')
    
    def select_all(self):
        """Select all files."""
        for i, f in enumerate(self.files):
            f['selected'] = True
            self.tree.set(str(i), 'select', '[x]')
    
    def select_none(self):
        """Deselect all files."""
        for i, f in enumerate(self.files):
            f['selected'] = False
            self.tree.set(str(i), 'select', '[ ]')
    
    def select_video(self):
        """Select video files (mkv, mp4, avi, mov, wmv, flv, webm, m4v)."""
        video_exts = ['.mkv', '.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v']
        for i, f in enumerate(self.files):
            if any(f['name'].lower().endswith(ext) for ext in video_exts):
                f['selected'] = True
                self.tree.set(str(i), 'select', '[x]')
            else:
                f['selected'] = False
                self.tree.set(str(i), 'select', '[ ]')
    
    def invert_selection(self):
        """Invert selection."""
        for i, f in enumerate(self.files):
            f['selected'] = not f['selected']
            self.tree.set(str(i), 'select', '[x]' if f['selected'] else '[ ]')
    
    def set_extensions(self, extensions):
        """Set extensions filter from preset."""
        self.extensions_var.set(extensions)
        self.log(f"Extensions filter set to: {extensions if extensions else 'All files'}")
    
    def retry_failed(self):
        """Select only failed files for retry."""
        for i, f in enumerate(self.files):
            status = self.tree.set(str(i), 'status')
            if status in ['Failed', 'Error']:
                f['selected'] = True
                self.tree.set(str(i), 'select', '[x]')
            else:
                f['selected'] = False
                self.tree.set(str(i), 'select', '[ ]')
        self.log("Selected failed files for retry")
    
    def start_download(self):
        """Start downloading selected files."""
        selected = [f for f in self.files if f['selected']]
        if not selected:
            messagebox.showwarning("Warning", "No files selected! Click the [ ] checkbox to select files.")
            return
        
        # Check output folder
        dest = self.output_var.get()
        try:
            os.makedirs(dest, exist_ok=True)
            self.log(f"Output folder: {dest}")
        except Exception as e:
            messagebox.showerror("Error", f"Cannot create output folder: {e}")
            return
        
        self.is_downloading = True
        self.failed_files = []
        self.download_btn.config(state='disabled')
        self.stop_btn.config(state='normal')
        self.progress_var.set(0)
        self.current_progress_var.set(0)
        self.downloaded_var.set("Downloaded: -")
        self.speed_var.set("Speed: -")
        self.eta_var.set("ETA: -")
        
        self.log(f"="*60)
        self.log(f"STARTING DOWNLOAD: {len(selected)} files")
        self.log(f"Output folder: {dest}")
        
        # Log proxy info if using
        if self.use_yaml_proxy_var.get():
            yaml_proxy = self.apply_yaml_proxy()
            if yaml_proxy:
                proxy_type = yaml_proxy.get('type', 'http').upper()
                server = yaml_proxy['server']
                port = yaml_proxy['port']
                username = yaml_proxy.get('username', '')
                auth = " (with auth)" if username else ""
                self.log(f"Using YAML {proxy_type} proxy: {server}:{port}{auth}")
        elif self.use_proxy_var.get():
            ip = self.proxy_ip_var.get().strip()
            port = self.proxy_port_var.get().strip()
            protocol = self.proxy_protocol_var.get()
            auth = " (with auth)" if (self.proxy_auth_var.get() and self.proxy_user_var.get()) else ""
            self.log(f"Using {protocol} proxy: {ip}:{port}{auth}")
        
        self.log(f"="*60)
        
        # Start timer
        self.start_time = time.time()
        
        self.download_thread = threading.Thread(
            target=self._do_download, 
            args=(selected, dest), 
            daemon=True
        )
        self.download_thread.start()
    
    def parse_wget_progress(self, line):
        """Parse wget progress output to extract percentage, speed, and eta."""
        line = line.strip()
        
        # Pattern for wget progress on Windows (various formats):
        # " 4100K .......... .......... .......... .......... ..........  1% 25.8M 5m8s"
        # "12288K .......... .......... .......... .......... ..........  4% 4.37M 3m37s"
        # "25.8M 5m8s" at the end means speed and eta
        
        # Match: downloaded_size progress_bar XX% speed eta
        # downloaded_size can be: 4100K, 12M, 1.5G
        # speed can be: 25.8M, 496K, 4.37M
        # eta can be: 5m8s, 4m42s, 3m37s, 12s, 2m
        
        pattern1 = r'\s*(\d+(?:\.\d+)?[KMGT]?)\s+(?:\.+\s+)*(\d+)%\s+(\d+(?:\.\d+)?)([KMGT]?)\s+(?:(\d+)m)?(?:(\d+)s)?'
        match1 = re.search(pattern1, line, re.IGNORECASE)
        if match1:
            percent = int(match1.group(2))
            speed_val = float(match1.group(3))
            speed_unit = match1.group(4) if match1.group(4) else "B"
            eta_min = match1.group(5)
            eta_sec = match1.group(6)
            
            # Format ETA
            eta_parts = []
            if eta_min:
                eta_parts.append(f"{eta_min}m")
            if eta_sec:
                eta_parts.append(f"{eta_sec}s")
            eta = "".join(eta_parts) if eta_parts else None
            
            speed_str = f"{speed_val:.2f} {speed_unit}B/s"
            return percent, speed_str, eta
        
        # Alternative pattern: just look for XX% and speed nearby
        # " 35%  4.37M 3m37s"
        pattern2 = r'(\d+)%\s+(\d+(?:\.\d+)?)([KMGT]?)B?\s+(?:(\d+)m)?(?:(\d+)s)?'
        match2 = re.search(pattern2, line, re.IGNORECASE)
        if match2:
            percent = int(match2.group(1))
            speed_val = float(match2.group(2))
            speed_unit = match2.group(3) if match2.group(3) else ""
            eta_min = match2.group(4)
            eta_sec = match2.group(5)
            
            eta_parts = []
            if eta_min:
                eta_parts.append(f"{eta_min}m")
            if eta_sec:
                eta_parts.append(f"{eta_sec}s")
            eta = "".join(eta_parts) if eta_parts else None
            
            speed_str = f"{speed_val:.2f} {speed_unit}B/s"
            return percent, speed_str, eta
        
        # Pattern 3: Simple percentage only
        pattern3 = r'(\d+)%'
        match3 = re.search(pattern3, line)
        if match3:
            percent = int(match3.group(1))
            return percent, None, None
        
        return None, None, None
    
    def _download_with_requests(self, f, filepath, idx, remote_size):
        """Download a file using Python requests with SOCKS proxy support."""
        import socks as socks_module
        
        filename = f['name']
        url = f['url']
        
        result = self.create_socks_session()
        if not result:
            self.root.after(0, lambda: self.log(f"[ERROR] {filename} - Failed to create SOCKS session"))
            return False
            
        session, old_socket = result
        
        try:
            # Check for partial download
            resume_byte_pos = 0
            if os.path.exists(filepath):
                resume_byte_pos = os.path.getsize(filepath)
            
            # Set up headers for resume
            headers = {}
            if resume_byte_pos > 0:
                headers['Range'] = f'bytes={resume_byte_pos}-'
                self.root.after(0, lambda: self.log(f"[RESUME] {filename} from {self._format_size(resume_byte_pos)}"))
            
            # Make request
            response = session.get(url, headers=headers, stream=True, timeout=30)
            response.raise_for_status()
            
            # Get total size
            if 'Content-Length' in response.headers:
                content_length = int(response.headers['Content-Length'])
                if resume_byte_pos > 0 and response.status_code == 206:
                    total_size = resume_byte_pos + content_length
                else:
                    total_size = content_length
            else:
                total_size = remote_size if remote_size > 0 else 0
            
            # Open file for writing/appending
            mode = 'ab' if resume_byte_pos > 0 and response.status_code == 206 else 'wb'
            downloaded = resume_byte_pos if mode == 'ab' else 0
            
            start_time = time.time()
            last_update = start_time
            
            with open(filepath, mode) as f_out:
                for chunk in response.iter_content(chunk_size=8192):
                    if not self.is_downloading:
                        break
                    if chunk:
                        f_out.write(chunk)
                        downloaded += len(chunk)
                        
                        # Update progress every 0.5 seconds
                        current_time = time.time()
                        if current_time - last_update > 0.5:
                            last_update = current_time
                            
                            if total_size > 0:
                                percent = min(100, int((downloaded / total_size) * 100))
                                self.root.after(0, lambda p=percent: self.current_progress_var.set(p))
                                self.root.after(0, lambda i=idx, p=percent: 
                                    self.tree.set(str(i), 'progress', f"{p}%"))
                                
                                # Calculate speed
                                elapsed = current_time - start_time
                                if elapsed > 0:
                                    speed_bps = downloaded / elapsed
                                    speed_str = self._format_size(speed_bps) + "/s"
                                    self.root.after(0, lambda s=speed_str: self.speed_var.set(f"Speed: {s}"))
                                    
                                    # Calculate ETA
                                    remaining = total_size - downloaded
                                    if speed_bps > 0:
                                        eta_seconds = remaining / speed_bps
                                        eta_str = self._format_time(eta_seconds)
                                        self.root.after(0, lambda e=eta_str: self.eta_var.set(f"ETA: {e}"))
                                    
                                    downloaded_str = self._format_size(downloaded)
                                    total_str = self._format_size(total_size)
                                    self.root.after(0, lambda d=downloaded_str, t=total_str: 
                                        self.downloaded_var.set(f"Downloaded: {d} / {t}"))
                            
                            # Update elapsed time
                            elapsed_str = self._format_time(current_time - self.start_time)
                            self.root.after(0, lambda e=elapsed_str: self.elapsed_var.set(f"Elapsed: {e}"))
            
            if not self.is_downloading:
                return False
            
            # Success
            size_str = self._format_size(os.path.getsize(filepath))
            self.root.after(0, lambda fn=filename, sz=size_str: 
                self.log(f"[OK] {fn} ({sz})"))
            self.root.after(0, lambda i=idx: 
                self.tree.set(str(i), 'status', 'Done'))
            self.root.after(0, lambda i=idx: 
                self.tree.set(str(i), 'progress', '100%'))
            self.root.after(0, lambda i=idx, sz=size_str: 
                self.tree.set(str(i), 'size', sz))
            
            return True
            
        except Exception as e:
            self.root.after(0, lambda fn=filename, err=str(e): 
                self.log(f"[ERROR] {fn}: {err}"))
            self.root.after(0, lambda i=idx: 
                self.tree.set(str(i), 'status', 'Error'))
            return False
            
        finally:
            # Restore original socket
            socket.socket = old_socket
    
    def _do_download(self, files, dest):
        """Download files in background with realtime progress."""
        total = len(files)
        completed = 0
        skipped = 0
        failed = 0
        retry_count = 2  # Number of retries for failed downloads
        
        for i, f in enumerate(files):
            if not self.is_downloading:
                self.root.after(0, lambda: self.log("DOWNLOAD STOPPED BY USER"))
                break
            
            filename = f['name']
            filepath = os.path.join(dest, filename)
            idx = self.files.index(f)
            
            # Check if file exists and is complete
            remote_size = f.get('size_bytes', 0)
            if os.path.exists(filepath) and remote_size > 0:
                local_size = os.path.getsize(filepath)
                if local_size >= remote_size:
                    # File is complete, skip it
                    self.root.after(0, lambda fn=filename, sz=self._format_size(local_size): 
                        self.log(f"[SKIP] {fn} ({sz}) - Already exists and complete"))
                    completed += 1
                    skipped += 1
                    
                    # Update progress
                    progress = (completed / total) * 100
                    self.root.after(0, lambda p=progress: self.progress_var.set(p))
                    self.root.after(0, lambda c=completed, t=total: 
                        self.status_var.set(f"Progress: {c}/{t} ({skipped} skipped, {failed} failed)"))
                    self.root.after(0, lambda i=idx: 
                        self.tree.set(str(i), 'status', 'Done'))
                    continue
                else:
                    # File exists but is incomplete, will resume
                    progress_pct = min(100, int((local_size / remote_size) * 100))
                    remaining = remote_size - local_size
                    self.root.after(0, lambda fn=filename, cur=self._format_size(local_size), 
                        tot=f.get('size', 'unknown'), pct=progress_pct, rem=self._format_size(remaining):
                        self.log(f"[RESUME] {fn} - {cur} of {tot} ({pct}% done, {rem} remaining)"))
                    # Continue to download (wget --continue will handle it)
            elif os.path.exists(filepath):
                # File exists but we don't know remote size, skip to be safe
                size = os.path.getsize(filepath)
                self.root.after(0, lambda fn=filename, sz=self._format_size(size): 
                    self.log(f"[SKIP] {fn} ({sz}) - Already exists (unknown remote size)"))
                completed += 1
                skipped += 1
                
                # Update progress
                progress = (completed / total) * 100
                self.root.after(0, lambda p=progress: self.progress_var.set(p))
                self.root.after(0, lambda c=completed, t=total: 
                    self.status_var.set(f"Progress: {c}/{t} ({skipped} skipped, {failed} failed)"))
                self.root.after(0, lambda i=idx: 
                    self.tree.set(str(i), 'status', 'Done'))
                continue
            
            # Download file with retries
            attempt = 0
            success = False
            
            while attempt < retry_count and not success and self.is_downloading:
                attempt += 1
                if attempt > 1:
                    self.root.after(0, lambda fn=filename, att=attempt: 
                        self.log(f"[RETRY {att-1}] {fn}"))
                
                self.root.after(0, lambda fn=filename: 
                    self.current_file_var.set(f"Downloading: {fn[:60]}"))
                self.root.after(0, lambda fn=filename: 
                    self.log(f"[START] {fn}"))
                self.root.after(0, lambda i=idx: 
                    self.tree.set(str(i), 'status', 'Downloading'))
                
                try:
                    # Check if using YAML proxy
                    yaml_proxy = None
                    if self.use_yaml_proxy_var.get():
                        yaml_proxy = self.apply_yaml_proxy()
                    
                    if yaml_proxy:
                        # Apply YAML proxy settings temporarily
                        proxy_type = yaml_proxy.get('type', 'http').upper()
                        self.proxy_ip_var.set(yaml_proxy['server'])
                        self.proxy_port_var.set(str(yaml_proxy['port']))
                        self.proxy_protocol_var.set(proxy_type if proxy_type in ['SOCKS5', 'SOCKS4'] else 'HTTP')
                        if 'username' in yaml_proxy:
                            self.proxy_auth_var.set(True)
                            self.proxy_user_var.set(yaml_proxy['username'])
                            if 'password' in yaml_proxy:
                                self.proxy_pass_var.set(yaml_proxy['password'])
                        else:
                            self.proxy_auth_var.set(False)
                            self.proxy_user_var.set('')
                            self.proxy_pass_var.set('')
                    
                    protocol = self.proxy_protocol_var.get()
                    use_socks = (self.use_proxy_var.get() or self.use_yaml_proxy_var.get()) and protocol in ["SOCKS4", "SOCKS5"]
                    
                    if use_socks:
                        # Use Python requests for SOCKS proxy downloads
                        success = self._download_with_requests(f, filepath, idx, remote_size)
                        if success:
                            completed += 1
                        else:
                            failed += 1
                            self.failed_files.append(f)
                            self.root.after(0, lambda i=idx: 
                                self.tree.set(str(i), 'status', 'Failed'))
                        
                        # Update overall progress
                        progress = (completed / total) * 100
                        self.root.after(0, lambda p=progress: self.progress_var.set(p))
                        self.root.after(0, lambda c=completed, t=total, s=skipped, f=failed: 
                            self.status_var.set(f"Progress: {c}/{t} ({s} skipped, {f} failed)"))
                        continue
                    
                    # Build wget command for HTTP proxy or no proxy
                    cmd = [
                        'wget',
                        '--continue',
                        '--tries=3',
                        '--timeout=30',
                        '--no-check-certificate',
                        '--progress=bar:force',
                        '-O', filepath,
                        f['url']
                    ]
                    
                    # Add bandwidth limit if specified
                    bandwidth = self.bandwidth_var.get().strip()
                    if bandwidth and bandwidth != "0":
                        try:
                            limit_kb = int(bandwidth)
                            if limit_kb > 0:
                                cmd.extend(['--limit-rate', f'{limit_kb}k'])
                                self.root.after(0, lambda kb=limit_kb: 
                                    self.log(f"Speed limit: {kb} KB/s"))
                        except ValueError:
                            pass
                    
                    # Add HTTP proxy if enabled
                    if (self.use_proxy_var.get() or self.use_yaml_proxy_var.get()) and protocol == "HTTP":
                        ip = self.proxy_ip_var.get().strip()
                        port = self.proxy_port_var.get().strip()
                        auth_enabled = self.proxy_auth_var.get()
                        username = self.proxy_user_var.get()
                        password = self.proxy_pass_var.get()
                        
                        if ip and port:
                            if auth_enabled and username and password:
                                proxy_url = f"http://{username}:{password}@{ip}:{port}"
                            else:
                                proxy_url = f"http://{ip}:{port}"
                            cmd.extend([
                                '-e', f'use_proxy=on',
                                '-e', f'http_proxy={proxy_url}',
                                '-e', f'https_proxy={proxy_url}'
                            ])
                    
                     # Store current file info for progress calculation
                    current_file_total = f.get('size_bytes', 0)
                    
                    # Run wget with realtime output
                    self.current_process = subprocess.Popen(
                        cmd,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT,
                        universal_newlines=True,
                        bufsize=1,
                        shell=True,
                        creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
                    )
                    
                    last_progress_update = 0
                    last_speed = None
                    last_eta = None
                    
                    # Read output in realtime
                    while self.current_process and self.current_process.poll() is None:
                        stdout = self.current_process.stdout
                        if stdout is None:
                            break
                        try:
                            line = stdout.readline()
                        except:
                            break
                        if not line:
                            # Small sleep to prevent CPU spinning
                            time.sleep(0.05)
                            continue
                        
                        # Debug: log first few lines to see wget output format
                        # if last_progress_update < 5:
                        #     self.root.after(0, lambda l=line: self.log(f"DBG: {repr(l[:100])}"))
                        
                        # Parse progress
                        percent, speed, eta = self.parse_wget_progress(line)
                        if percent is not None:
                            # Update speed and eta even if not updating UI
                            if speed:
                                last_speed = speed
                            if eta:
                                last_eta = eta
                            
                            # Throttle UI updates to avoid overwhelming the GUI
                            current_time = time.time()
                            if current_time - last_progress_update > 0.3:  # Update every 300ms
                                last_progress_update = current_time
                                
                                # Update progress bar
                                self.root.after(0, lambda p=percent: self.current_progress_var.set(p))
                                self.root.after(0, lambda i=idx, p=percent: 
                                    self.tree.set(str(i), 'progress', f"{p}%"))
                                
                                # Update speed
                                if last_speed:
                                    self.root.after(0, lambda s=last_speed: self.speed_var.set(f"Speed: {s}"))
                                
                                # Update ETA
                                if last_eta:
                                    self.root.after(0, lambda e=last_eta: self.eta_var.set(f"ETA: {e}"))
                                
                                # Update downloaded size
                                if current_file_total > 0:
                                    downloaded = int(current_file_total * (percent / 100))
                                    downloaded_str = self._format_size(downloaded)
                                    total_str = self._format_size(current_file_total)
                                    self.root.after(0, lambda d=downloaded_str, t=total_str: 
                                        self.downloaded_var.set(f"Downloaded: {d} / {t}"))
                                else:
                                    self.root.after(0, lambda p=percent: 
                                        self.downloaded_var.set(f"Downloaded: {p}%"))
                        
                        # Update elapsed time
                        elapsed = time.time() - self.start_time
                        elapsed_str = self._format_time(elapsed)
                        self.root.after(0, lambda e=elapsed_str: self.elapsed_var.set(f"Elapsed: {e}"))
                    
                    # Wait for process to complete
                    returncode = self.current_process.wait()
                    
                    if returncode == 0:
                        success = True
                        completed += 1
                        if os.path.exists(filepath):
                            size = os.path.getsize(filepath)
                            size_str = self._format_size(size)
                        else:
                            size_str = "Unknown"
                        
                        self.root.after(0, lambda fn=filename, sz=size_str: 
                            self.log(f"[OK] {fn} ({sz})"))
                        self.root.after(0, lambda i=idx: 
                            self.tree.set(str(i), 'status', 'Done'))
                        self.root.after(0, lambda i=idx: 
                            self.tree.set(str(i), 'progress', '100%'))
                        self.root.after(0, lambda i=idx, sz=size_str: 
                            self.tree.set(str(i), 'size', sz))
                    else:
                        # Download failed
                        if attempt < retry_count and self.is_downloading:
                            self.root.after(0, lambda fn=filename: 
                                self.log(f"[FAIL] {fn} - Will retry"))
                            time.sleep(2)  # Wait before retry
                        else:
                            failed += 1
                            self.root.after(0, lambda fn=filename: 
                                self.log(f"[FAIL] {fn} - All retries exhausted"))
                            self.root.after(0, lambda i=idx: 
                                self.tree.set(str(i), 'status', 'Failed'))
                            self.failed_files.append(f)
                    
                    self.current_process = None
                    
                except Exception as e:
                    if attempt < retry_count and self.is_downloading:
                        self.root.after(0, lambda fn=filename, err=str(e): 
                            self.log(f"[ERROR] {fn}: {err} - Will retry"))
                        time.sleep(2)
                    else:
                        failed += 1
                        self.root.after(0, lambda fn=filename, err=str(e): 
                            self.log(f"[ERROR] {fn}: {err}"))
                        self.root.after(0, lambda i=idx: 
                            self.tree.set(str(i), 'status', 'Error'))
                        self.failed_files.append(f)
                
                # Update overall progress
                progress = (completed / total) * 100
                self.root.after(0, lambda p=progress: self.progress_var.set(p))
                self.root.after(0, lambda c=completed, t=total, s=skipped, f=failed: 
                    self.status_var.set(f"Progress: {c}/{t} ({s} skipped, {f} failed)"))
        
        self.root.after(0, lambda: self._download_complete(completed, total, skipped, failed))
    
    def _format_time(self, seconds):
        """Format seconds to human readable time."""
        if seconds < 60:
            return f"{int(seconds)}s"
        elif seconds < 3600:
            minutes = int(seconds // 60)
            secs = int(seconds % 60)
            return f"{minutes}m {secs}s"
        else:
            hours = int(seconds // 3600)
            minutes = int((seconds % 3600) // 60)
            return f"{hours}h {minutes}m"
    
    def _download_complete(self, completed, total, skipped, failed):
        """Called when download completes."""
        self.is_downloading = False
        self.download_btn.config(state='normal')
        self.stop_btn.config(state='disabled')
        self.current_file_var.set("")
        self.current_progress_var.set(0)
        self.speed_var.set("Speed: -")
        self.eta_var.set("ETA: -")
        self.downloaded_var.set("Downloaded: -")
        
        elapsed = time.time() - self.start_time
        elapsed_str = self._format_time(elapsed)
        
        self.log(f"="*60)
        self.log(f"DOWNLOAD COMPLETE!")
        self.log(f"Total: {total}, Completed: {completed}, Skipped: {skipped}, Failed: {failed}")
        self.log(f"Elapsed time: {elapsed_str}")
        self.log(f"="*60)
        
        self.status_var.set(f"Complete! {completed}/{total} done ({skipped} skipped, {failed} failed)")
        self.progress_var.set(100)
        
        # Show completion message
        if failed > 0:
            message = (f"Download finished with some failures!\n\n"
                      f"Total: {total}\n"
                      f"Downloaded: {completed}\n"
                      f"Skipped: {skipped}\n"
                      f"Failed: {failed}\n\n"
                      f"Elapsed time: {elapsed_str}\n\n"
                      f"Click 'Retry Failed' to retry failed downloads.")
            messagebox.showwarning("Download Complete", message)
        else:
            message = (f"All downloads completed successfully!\n\n"
                      f"Total: {total}\n"
                      f"Downloaded: {completed}\n"
                      f"Skipped: {skipped}\n"
                      f"Failed: {failed}\n\n"
                      f"Elapsed time: {elapsed_str}")
            messagebox.showinfo("All Downloads Complete", message)
    
    def stop_download(self):
        """Stop current download."""
        self.is_downloading = False
        if self.current_process:
            try:
                self.current_process.terminate()
            except:
                pass
        self.log("[STOP] Stopping download...")
        self.stop_btn.config(state='disabled')

def main():
    root = tk.Tk()
    style = ttk.Style()
    style.theme_use('clam')
    
    app = HttpDownloaderGUI(root)
    
    # Handle window close
    def on_closing():
        if app.is_downloading:
            if messagebox.askokcancel("Quit", "Download in progress. Stop and quit?"):
                app.stop_download()
                root.after(1000, root.destroy)
        else:
            root.destroy()
    
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    main()
