package cz.kamma.kfmanager.ui;

import cz.kamma.kfmanager.MainApp;
import cz.kamma.kfmanager.config.AppConfig;
import cz.kamma.kfmanager.model.FileItem;
import cz.kamma.kfmanager.service.FileOperations;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

    /**
     * Main application window with two panels
     */
public class MainWindow extends JFrame {
    
    private FilePanel leftPanel;
    private FilePanel rightPanel;
    private FilePanel activePanel;
    private JPanel buttonPanel;
    private JToolBar toolBar;
    private JComboBox<String> commandLine;
    private JLabel cmdLabel;
    private AppConfig config;
    
    public MainWindow() {
        super("KF Manager v" + MainApp.APP_VERSION);
        
        // Set application icon
        loadAppIcon();
        
    // Load configuration
        config = new AppConfig();
        
        initComponents();
        setupKeyBindings();
        // Apply appearance from saved configuration at startup
        applyAppearanceSettings();
        
    // Restore window size and position from configuration
        config.restoreWindowState(this);
        
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        
    // Add listener to save configuration on exit
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                MainWindow.this.saveConfigAndExit();
            }
        });
        
    // After start, set focus and selection to the active panel
        String initialActiveSide = config.getActivePanel();
        SwingUtilities.invokeLater(() -> {
            if ("right".equalsIgnoreCase(initialActiveSide)) {
                activePanel = rightPanel;
                rightPanel.requestFocusOnCurrentTab();
            } else {
                activePanel = leftPanel;
                leftPanel.requestFocusOnCurrentTab();
            }
            updateActivePanelBorder();
            updateCommandLinePrompt();
        });
    }
    
    private void loadAppIcon() {
        try {
            java.net.URL iconURL = MainWindow.class.getResource("/icon.png");
            if (iconURL != null) {
                ImageIcon img = new ImageIcon(iconURL);
                setIconImage(img.getImage());
            }
        } catch (Exception e) {
            System.err.println("Could not load icon: " + e.getMessage());
        }
    }
    
    private void initComponents() {
        setLayout(new BorderLayout());
        
        // Toolbar
        createToolBar();
        
    // Panel containing the two file panels
        JPanel mainPanel = new JPanel(new GridLayout(1, 2, 5, 0));
        
    // Left panel - load path and ViewMode from configuration
        String leftPath = config.getLeftPanelPath();
        leftPanel = new FilePanel(leftPath);
    leftPanel.setAppConfig(config);
    // Provide a callback so tabs inside the panel can request switching panels with TAB
    leftPanel.setSwitchPanelCallback(() -> switchPanelsFromChild());
    leftPanel.setOnDirectoryChangedAll(() -> updateCommandLinePrompt());
        
    // Load and set ViewMode for left panel
        try {
            ViewMode leftViewMode = ViewMode.valueOf(config.getLeftPanelViewMode());
            leftPanel.setViewMode(leftViewMode, false);
        } catch (IllegalArgumentException e) {
            // Default value FULL is already set
        }
        
    // Right panel - load path and ViewMode from configuration
        String rightPath = config.getRightPanelPath();
        rightPanel = new FilePanel(rightPath);
    rightPanel.setAppConfig(config);
    // Provide a callback so tabs inside the panel can request switching panels with TAB
    rightPanel.setSwitchPanelCallback(() -> switchPanelsFromChild());
    rightPanel.setOnDirectoryChangedAll(() -> updateCommandLinePrompt());
        
    // Load and set ViewMode for right panel
        try {
            ViewMode rightViewMode = ViewMode.valueOf(config.getRightPanelViewMode());
            rightPanel.setViewMode(rightViewMode, false);
        } catch (IllegalArgumentException e) {
            // Default value FULL is already set
        }
        
        mainPanel.add(leftPanel);
        mainPanel.add(rightPanel);
        
        add(mainPanel, BorderLayout.CENTER);
        
    // Restore active panel from configuration
        String savedActive = config.getActivePanel();
        if ("right".equalsIgnoreCase(savedActive)) {
            activePanel = rightPanel;
        } else {
            activePanel = leftPanel;
        }
        updateActivePanelBorder();
        updateCommandLinePrompt();

        // Restore saved tabs for both panels if present in configuration
        try {
            int leftCount = config.getLeftPanelTabCount();
            if (leftCount > 0) {
                java.util.List<String> paths = new java.util.ArrayList<>();
                java.util.List<String> modes = new java.util.ArrayList<>();
                java.util.List<String> focusedItems = new java.util.ArrayList<>();
                for (int i = 0; i < leftCount; i++) {
                    String p = config.getLeftPanelTabPath(i);
                    if (p == null) p = System.getProperty("user.home");
                    paths.add(p);
                    modes.add(config.getLeftPanelTabViewMode(i));
                    focusedItems.add(config.getLeftPanelTabFocusedItem(i));
                }
                int sel = config.getLeftPanelSelectedIndex();
                leftPanel.restoreTabs(paths, modes, focusedItems, sel, false);
            }
        } catch (Exception ex) {
            // ignore and keep default
        }

        try {
            int rightCount = config.getRightPanelTabCount();
            if (rightCount > 0) {
                java.util.List<String> paths = new java.util.ArrayList<>();
                java.util.List<String> modes = new java.util.ArrayList<>();
                java.util.List<String> focusedItems = new java.util.ArrayList<>();
                for (int i = 0; i < rightCount; i++) {
                    String p = config.getRightPanelTabPath(i);
                    if (p == null) p = System.getProperty("user.home");
                    paths.add(p);
                    modes.add(config.getRightPanelTabViewMode(i));
                    focusedItems.add(config.getRightPanelTabFocusedItem(i));
                }
                int sel = config.getRightPanelSelectedIndex();
                rightPanel.restoreTabs(paths, modes, focusedItems, sel, false);
            }
        } catch (Exception ex) {
            // ignore and keep default
        }
        
        // Global focus listener to track which panel is active based on focused component
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("permanentFocusOwner", evt -> {
            Component focused = (Component) evt.getNewValue();
            if (focused != null) {
                if (SwingUtilities.isDescendingFrom(focused, leftPanel)) {
                    activePanel = leftPanel;
                    updateActivePanelBorder();
                    leftPanel.getFileTable().repaint();
                    rightPanel.getFileTable().repaint();
                    updateCommandLinePrompt();
                } else if (SwingUtilities.isDescendingFrom(focused, rightPanel)) {
                    activePanel = rightPanel;
                    updateActivePanelBorder();
                    leftPanel.getFileTable().repaint();
                    rightPanel.getFileTable().repaint();
                    updateCommandLinePrompt();
                }
            }
        });

    // Focus listeners to track active panel and ensure selection
        leftPanel.getFileTable().addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                activePanel = leftPanel;
                updateActivePanelBorder();
                updateCommandLinePrompt();
                // Ensure some row is selected
                JTable leftTable = leftPanel.getFileTable();
                if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) {
                    leftTable.setRowSelectionInterval(0, 0);
                }
            }
            
            @Override
            public void focusLost(FocusEvent e) {
                // Repaint on focus loss
                leftPanel.getFileTable().repaint();
            }
        });
        
        rightPanel.getFileTable().addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                activePanel = rightPanel;
                updateActivePanelBorder();
                updateCommandLinePrompt();
                // Ensure some row is selected
                JTable rightTable = rightPanel.getFileTable();
                if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) {
                    rightTable.setRowSelectionInterval(0, 0);
                }
            }
            
            @Override
            public void focusLost(FocusEvent e) {
                // Repaint on focus loss
                rightPanel.getFileTable().repaint();
            }
        });

        // Click on panel anywhere should request focus to its table
        leftPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                leftPanel.getFileTable().requestFocusInWindow();
            }
        });
        rightPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                rightPanel.getFileTable().requestFocusInWindow();
            }
        });
        
    // Add TAB handler to switch between panels
        addTabKeyHandler(leftPanel.getFileTable());
        addTabKeyHandler(rightPanel.getFileTable());
        
        // Add command line focus redirection
        addCommandLineRedirect(leftPanel.getFileTable());
        addCommandLineRedirect(rightPanel.getFileTable());
        
        // Container for everything below the file panels
        JPanel bottomContainer = new JPanel(new BorderLayout());
        
        // Command line panel
        JPanel cmdPanel = new JPanel(new BorderLayout(5, 0));
        cmdPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 0, 5));
        
        cmdLabel = new JLabel(System.getProperty("user.name") + ":" + (activePanel != null ? activePanel.getCurrentDirectory().getAbsolutePath() : "") + ">");
        cmdLabel.setFont(new Font("Monospaced", Font.BOLD, 12));
        cmdPanel.add(cmdLabel, BorderLayout.WEST);
        
        commandLine = new JComboBox<>();
        commandLine.setEditable(true);
        commandLine.setFont(new Font("Monospaced", Font.PLAIN, 12));
        
        // Handle the editor component (usually a JTextField)
        Component editorComp = commandLine.getEditor().getEditorComponent();
        if (editorComp instanceof JTextField) {
            JTextField tf = (JTextField) editorComp;
            tf.setFocusTraversalKeysEnabled(false);
            tf.addActionListener(e -> executeCommand(tf.getText()));

            // Enable standard clipboard operations (Cut, Copy, Paste) even if not focused initially
            tf.getComponentPopupMenu(); // Ensure it has a menu or at least default actions works

            // Context menu with Clipboard operations
            JPopupMenu clipboardMenu = new JPopupMenu();
            Action cutAction = new javax.swing.text.DefaultEditorKit.CutAction();
            cutAction.putValue(Action.NAME, "Cut");
            clipboardMenu.add(cutAction);

            Action copyAction = new javax.swing.text.DefaultEditorKit.CopyAction();
            copyAction.putValue(Action.NAME, "Copy");
            clipboardMenu.add(copyAction);

            Action pasteAction = new javax.swing.text.DefaultEditorKit.PasteAction();
            pasteAction.putValue(Action.NAME, "Paste");
            clipboardMenu.add(pasteAction);

            clipboardMenu.addSeparator();

            Action selectAllAction = new AbstractAction("Select All") {
                @Override
                public void actionPerformed(ActionEvent e) {
                    tf.selectAll();
                }
            };
            clipboardMenu.add(selectAllAction);

            tf.setComponentPopupMenu(clipboardMenu);
            
            // Let the panels catch focus back if user presses ESC or TAB in command line
            tf.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        tf.setText("");
                        if (activePanel != null && activePanel.getFileTable() != null) {
                            activePanel.getFileTable().requestFocusInWindow();
                        }
                        e.consume();
                    } else if (e.getKeyCode() == KeyEvent.VK_TAB) {
                        activePanel.getFileTable().requestFocusInWindow();
                        e.consume();
                    } else if (e.getKeyCode() == KeyEvent.VK_E && e.isControlDown()) {
                        showCommandLineHistory();
                        e.consume();
                    }
                }
            });
        }
        
        cmdPanel.add(commandLine, BorderLayout.CENTER);
        
        // Load history from config
        java.util.List<String> history = config.getCommandLineHistory();
        for (int i = 0; i < history.size(); i++) {
            commandLine.addItem(history.get(i));
        }
        commandLine.getEditor().setItem(""); // Ensure it starts empty
        
        bottomContainer.add(cmdPanel, BorderLayout.NORTH);
        
        // Bottom panel with buttons
        createButtonPanel();
        bottomContainer.add(buttonPanel, BorderLayout.SOUTH);
        
        add(bottomContainer, BorderLayout.SOUTH);
        
    // Menu
        createMenuBar();

        // Request focus for the active panel
        SwingUtilities.invokeLater(() -> {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        });
    }
    
    /**
     * Create toolbar with buttons for changing view mode
     */
    private void createToolBar() {
        if (toolBar == null) {
            toolBar = new JToolBar();
            toolBar.setFloatable(false);
            toolBar.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            add(toolBar, BorderLayout.NORTH);
            
            // Enable Drag and Drop for adding shortcuts
            toolBar.setTransferHandler(new TransferHandler() {
                @Override
                public boolean canImport(TransferSupport support) {
                    return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
                }

                @Override
                @SuppressWarnings("unchecked")
                public boolean importData(TransferSupport support) {
                    if (!canImport(support)) return false;
                    try {
                        List<File> files = (List<File>) support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
                        if (files != null && !files.isEmpty()) {
                            showAddToolbarShortcutDialog(files.get(0));
                        }
                        return true;
                    } catch (Exception e) {
                        return false;
                    }
                }
            });
        }
        
        toolBar.removeAll();
        
    // Button for BRIEF mode
    JButton btnBrief = new JButton("☰ Brief");
    btnBrief.setToolTipText("Brief mode - multiple columns (Ctrl+F1)");
        btnBrief.setFocusable(false);
        btnBrief.addActionListener(e -> {
            if (activePanel != null) {
                activePanel.setViewMode(ViewMode.BRIEF);
            }
        });
        
    // Button for FULL mode
    JButton btnFull = new JButton("▤ Full");
    btnFull.setToolTipText("Full mode - full information (Ctrl+F2)");
        btnFull.setFocusable(false);
        btnFull.addActionListener(e -> {
            if (activePanel != null) {
                activePanel.setViewMode(ViewMode.FULL);
            }
        });
        
        toolBar.add(btnBrief);
        toolBar.add(btnFull);
        
        toolBar.addSeparator();
        
        // Load custom shortcuts from config
        List<AppConfig.ToolbarShortcut> shortcuts = config.getToolbarShortcuts();
        int btnSize = config.getToolbarButtonSize();
        int iconSize = config.getToolbarIconSize();

        for (AppConfig.ToolbarShortcut s : shortcuts) {
            JButton btn = new JButton();
            btn.setPreferredSize(new Dimension(btnSize, btnSize));
            btn.setMinimumSize(new Dimension(btnSize, btnSize));
            btn.setMaximumSize(new Dimension(btnSize, btnSize));
            
            boolean hasIcon = false;
            if (s.iconPath != null && !s.iconPath.isEmpty()) {
                try {
                    File iconFile = new File(s.iconPath);
                    if (iconFile.exists()) {
                        btn.setIcon(new ImageIcon(new ImageIcon(s.iconPath).getImage().getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH)));
                        hasIcon = true;
                    }
                } catch (Exception ignore) {}
            }
            
            // If no icon found, use the label text, otherwise use label as tooltip
            if (!hasIcon) {
                btn.setText(s.label);
            }
            btn.setToolTipText(s.label + " (" + s.command + ")");
            
            btn.setFocusable(false);
            btn.addActionListener(e -> executeNative(s.command, s.workingDir));
            
            // Context menu for Edit/Delete
            JPopupMenu shortcutPopup = new JPopupMenu();
            JMenuItem editItem = new JMenuItem("Edit");
            editItem.addActionListener(ae -> showEditToolbarShortcutDialog(s));
            JMenuItem deleteItem = new JMenuItem("Delete");
            deleteItem.addActionListener(ae -> {
                int choice = JOptionPane.showConfirmDialog(MainWindow.this, "Remove this shortcut?", "Toolbar", JOptionPane.YES_NO_OPTION);
                if (choice == JOptionPane.YES_OPTION) {
                    removeToolbarShortcut(s);
                }
            });
            shortcutPopup.add(editItem);
            shortcutPopup.add(deleteItem);

            btn.addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    if (e.isPopupTrigger()) {
                        shortcutPopup.show(e.getComponent(), e.getX(), e.getY());
                    }
                }
                @Override
                public void mouseReleased(MouseEvent e) {
                    if (e.isPopupTrigger()) {
                        shortcutPopup.show(e.getComponent(), e.getX(), e.getY());
                    }
                }
            });
            
            toolBar.add(btn);
        }
        
        toolBar.revalidate();
        toolBar.repaint();
    }
    
    private void showAddToolbarShortcutDialog(File file) {
        showToolbarShortcutEditor(null, file);
    }

    private void showEditToolbarShortcutDialog(AppConfig.ToolbarShortcut shortcut) {
        showToolbarShortcutEditor(shortcut, null);
    }

    private void showToolbarShortcutEditor(AppConfig.ToolbarShortcut existing, File file) {
        String initialLabel = (existing != null) ? existing.label : (file != null ? file.getName() : "");
        String initialCmd = (existing != null) ? existing.command : (file != null ? file.getAbsolutePath() : "");
        String initialWorkDir = (existing != null) ? existing.workingDir : 
                (file != null ? (file.isDirectory() ? file.getAbsolutePath() : file.getParent()) : "");
        String initialIcon = (existing != null) ? existing.iconPath : "";

        JTextField labelField = new JTextField(initialLabel);
        JTextField cmdField = new JTextField(initialCmd);
        JTextField workingDirField = new JTextField(initialWorkDir);
        JTextField iconField = new JTextField(initialIcon);
        
        JPanel panel = new JPanel(new GridLayout(0, 1));
        panel.add(new JLabel("Label:"));
        panel.add(labelField);
        panel.add(new JLabel("Command:"));
        panel.add(cmdField);
        panel.add(new JLabel("Working directory:"));
        
        JPanel workDirPanel = new JPanel(new BorderLayout());
        workDirPanel.add(workingDirField, BorderLayout.CENTER);
        JButton browseWorkDir = new JButton("...");
        browseWorkDir.addActionListener(e -> {
            JFileChooser chooser = new JFileChooser(workingDirField.getText());
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
                workingDirField.setText(chooser.getSelectedFile().getAbsolutePath());
            }
        });
        workDirPanel.add(browseWorkDir, BorderLayout.EAST);
        panel.add(workDirPanel);

        panel.add(new JLabel("Icon path (optional):"));
        
        JPanel iconPanel = new JPanel(new BorderLayout());
        iconPanel.add(iconField, BorderLayout.CENTER);
        JButton browseIcon = new JButton("...");
        browseIcon.addActionListener(e -> {
            String startPath = iconField.getText().trim();
            if (startPath.isEmpty()) {
                startPath = workingDirField.getText().trim();
                if (startPath.isEmpty()) {
                    startPath = cmdField.getText().trim();
                }
            }
            
            JFileChooser chooser = new JFileChooser(startPath);
            if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
                iconField.setText(chooser.getSelectedFile().getAbsolutePath());
            }
        });
        iconPanel.add(browseIcon, BorderLayout.EAST);
        panel.add(iconPanel);
        
        String title = (existing != null) ? "Edit Toolbar Shortcut" : "Add Toolbar Shortcut";
        int result = JOptionPane.showConfirmDialog(this, panel, title, JOptionPane.OK_CANCEL_OPTION);
        if (result == JOptionPane.OK_OPTION) {
            String label = labelField.getText().trim();
            String command = cmdField.getText().trim();
            String icon = iconField.getText().trim();
            String workDir = workingDirField.getText().trim();
            
            if (!command.isEmpty()) {
                if (label.isEmpty()) label = (file != null ? file.getName() : "Shortcut");
                List<AppConfig.ToolbarShortcut> shortcuts = config.getToolbarShortcuts();
                
                if (existing != null) {
                    // Find and replace
                    for (int i = 0; i < shortcuts.size(); i++) {
                        AppConfig.ToolbarShortcut s = shortcuts.get(i);
                        // Identify by original command/label if they haven't changed yet in memory
                        if (s.command.equals(existing.command) && s.label.equals(existing.label)) {
                            shortcuts.set(i, new AppConfig.ToolbarShortcut(command, label, icon, workDir));
                            break;
                        }
                    }
                } else {
                    shortcuts.add(new AppConfig.ToolbarShortcut(command, label, icon, workDir));
                }
                
                config.saveToolbarShortcuts(shortcuts);
                createToolBar(); // refresh
            }
        }
    }
    
    private void removeToolbarShortcut(AppConfig.ToolbarShortcut shortcut) {
        List<AppConfig.ToolbarShortcut> shortcuts = config.getToolbarShortcuts();
        shortcuts.removeIf(s -> s.command.equals(shortcut.command) && s.label.equals(shortcut.label));
        config.saveToolbarShortcuts(shortcuts);
        createToolBar();
    }
    
    /**
     * Create button panel (like Total Commander)
     */
    private void createButtonPanel() {
        buttonPanel = new JPanel(new GridLayout(1, 9, 5, 5));
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
    JButton btnView = new JButton("F3 View");
        btnView.addActionListener(e -> viewFile());
        
    JButton btnEdit = new JButton("F4 Edit");
        btnEdit.addActionListener(e -> editFile());
        
    JButton btnCopy = new JButton("F5 Copy");
        btnCopy.addActionListener(e -> copyFiles());
        
    JButton btnMove = new JButton("F6 Move");
        btnMove.addActionListener(e -> moveFiles());
        
    JButton btnNewDir = new JButton("F7 New Dir");
        btnNewDir.addActionListener(e -> createNewDirectory());
        
    JButton btnDelete = new JButton("F8 Delete");
        btnDelete.addActionListener(e -> deleteFiles());
        
    JButton btnTerminal = new JButton("F9 Terminal");
        btnTerminal.addActionListener(e -> openTerminal());
        
    JButton btnRename = new JButton("Shift+F6 Rename");
        btnRename.addActionListener(e -> renameFile());
        
    JButton btnExit = new JButton("F10 Exit");
        btnExit.addActionListener(e -> saveConfigAndExit());
        
        buttonPanel.add(btnView);
        buttonPanel.add(btnEdit);
        buttonPanel.add(btnCopy);
        buttonPanel.add(btnMove);
        buttonPanel.add(btnNewDir);
        buttonPanel.add(btnDelete);
        buttonPanel.add(btnTerminal);
        buttonPanel.add(btnRename);
        buttonPanel.add(btnExit);
    }
    
    /**
     * Create menu bar
     */
    private void createMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        
    // File menu
    JMenu fileMenu = new JMenu("File");
        
    JMenuItem searchItem = new JMenuItem("Search...");
        searchItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
        searchItem.addActionListener(e -> showSearchDialog());

    JMenuItem selectWildcardItem = new JMenuItem("Select by wildcard...");
        selectWildcardItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
        selectWildcardItem.addActionListener(e -> showWildcardSelectDialog());
        
    JMenuItem refreshItem = new JMenuItem("Refresh");
        refreshItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, InputEvent.CTRL_DOWN_MASK));
        refreshItem.addActionListener(e -> refreshPanels());
        
    JMenuItem exitItem = new JMenuItem("Exit");
        exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0));
        exitItem.addActionListener(e -> saveConfigAndExit());
        
        fileMenu.add(searchItem);
        fileMenu.add(selectWildcardItem);
        fileMenu.add(refreshItem);
        fileMenu.addSeparator();
        fileMenu.add(exitItem);
        
    // View menu
    JMenu viewMenu = new JMenu("View");
        
    JMenuItem fullViewItem = new JMenuItem("Full details");
        fullViewItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, InputEvent.CTRL_DOWN_MASK));
        fullViewItem.addActionListener(e -> setActiveViewMode(ViewMode.FULL));
        
    JMenuItem briefViewItem = new JMenuItem("Names only");
        briefViewItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.CTRL_DOWN_MASK));
        briefViewItem.addActionListener(e -> setActiveViewMode(ViewMode.BRIEF));
        
        viewMenu.add(fullViewItem);
        viewMenu.add(briefViewItem);

    // Settings menu
    JMenu settingsMenu = new JMenu("Settings");
    JMenuItem appearanceItem = new JMenuItem("Appearance...");
        appearanceItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.ALT_DOWN_MASK));
        appearanceItem.addActionListener(e -> showSettingsDialog());
    settingsMenu.add(appearanceItem);
        
    // Help menu
    JMenu helpMenu = new JMenu("Help");
        
    JMenuItem aboutItem = new JMenuItem("About");
        aboutItem.addActionListener(e -> showAboutDialog());
        
        helpMenu.add(aboutItem);
        
    menuBar.add(fileMenu);
        menuBar.add(viewMenu);
        menuBar.add(settingsMenu);
        menuBar.add(helpMenu);
        
        setJMenuBar(menuBar);
    }

    /**
     * Show settings dialog and apply appearance changes
     */
    private void showSettingsDialog() {
        SettingsDialog dlg = new SettingsDialog(this, config, () -> applyAppearanceSettings());
        dlg.setVisible(true);
        // After dialog closed, ensure appearance applied
        applyAppearanceSettings();
        requestFocusInActivePanel();
    }

    /**
     * Apply appearance settings (font/colors) from config to UI components.
     */
    private void applyAppearanceSettings() {
        Font gfont = config.getGlobalFont();
        if (gfont != null) {
            // Apply to toolbars, buttons and tables
            SwingUtilities.invokeLater(() -> {
                for (Component c : getContentPane().getComponents()) {
                    c.setFont(gfont);
                }
                // Apply to panels' tables
                if (leftPanel != null && leftPanel.getFileTable() != null) {
                    leftPanel.applyGlobalFont(gfont);
                }
                if (rightPanel != null && rightPanel.getFileTable() != null) {
                    rightPanel.applyGlobalFont(gfont);
                }
            });
        }

        Color bg = config.getBackgroundColor();
        if (bg != null) {
            SwingUtilities.invokeLater(() -> {
                updateComponentBackground(getContentPane(), bg);
                if (leftPanel != null) leftPanel.applyBackgroundColor(bg);
                if (rightPanel != null) rightPanel.applyBackgroundColor(bg);

                // Update command line colors
                if (commandLine != null) {
                    Component ed = commandLine.getEditor().getEditorComponent();
                    if (ed instanceof JTextField) {
                        JTextField tf = (JTextField) ed;
                        tf.setBackground(bg);
                        boolean dark = isDark(bg);
                        tf.setForeground(dark ? Color.WHITE : Color.BLACK);
                        Color selColor = config.getSelectionColor();
                        if (selColor != null) {
                            tf.setSelectionColor(selColor);
                            tf.setCaretColor(selColor);
                        } else {
                            tf.setCaretColor(dark ? Color.WHITE : Color.BLACK);
                        }
                    }
                }
            });
        }

        Color sel = config.getSelectionColor();
        if (sel != null) {
            if (leftPanel != null) leftPanel.applySelectionColor(sel);
            if (rightPanel != null) rightPanel.applySelectionColor(sel);

            // Apply selection color to command line editor for cursor and selection
            if (commandLine != null) {
                Component ed = commandLine.getEditor().getEditorComponent();
                if (ed instanceof JTextField) {
                    JTextField tf = (JTextField) ed;
                    tf.setSelectionColor(sel);
                    tf.setCaretColor(sel);
                    tf.setSelectedTextColor(Color.WHITE); // Ensure selected text is readable on selection bg
                }
            }

            // Ensure the active panel border uses the updated configuration color immediately
            SwingUtilities.invokeLater(() -> updateActivePanelBorder());
        }

        Color mark = config.getMarkedColor();
        if (mark != null) {
            if (leftPanel != null) leftPanel.applyMarkedColor(mark);
            if (rightPanel != null) rightPanel.applyMarkedColor(mark);
        }

        // Refresh toolbar if sizes changed
        SwingUtilities.invokeLater(() -> createToolBar());

        // Re-propagate AppConfig to panels so they can pick up sorting and other config changes
        SwingUtilities.invokeLater(() -> {
            try {
                if (leftPanel != null) leftPanel.setAppConfig(config);
            } catch (Exception ignore) {}
            try {
                if (rightPanel != null) rightPanel.setAppConfig(config);
            } catch (Exception ignore) {}
        });
    }

    private void updateComponentBackground(Container container, Color bg) {
        if (container == null) return;
        container.setBackground(bg);
        boolean dark = isDark(bg);
        Color selColor = config != null ? config.getSelectionColor() : null;

        for (Component c : container.getComponents()) {
            if (c instanceof JPanel || c instanceof JToolBar || c instanceof JScrollPane || c instanceof JViewport || c instanceof JTabbedPane || c instanceof JButton) {
                c.setBackground(bg);
            }
            if (c instanceof JLabel || c instanceof JCheckBox || c instanceof JRadioButton || c instanceof JButton) {
                c.setForeground(dark ? Color.WHITE : Color.BLACK);
            }
            if (c instanceof javax.swing.text.JTextComponent) {
                javax.swing.text.JTextComponent tc = (javax.swing.text.JTextComponent) c;
                tc.setBackground(bg);
                tc.setForeground(dark ? Color.WHITE : Color.BLACK);
                if (selColor != null) {
                    tc.setSelectionColor(selColor);
                    tc.setCaretColor(selColor);
                } else {
                    tc.setCaretColor(dark ? Color.WHITE : Color.BLACK);
                }
            }
            if (c instanceof Container) {
                updateComponentBackground((Container) c, bg);
            }
        }
    }

    private boolean isDark(Color c) {
        if (c == null) return false;
        double darkness = 1 - (0.299 * c.getRed() + 0.587 * c.getGreen() + 0.114 * c.getBlue()) / 255;
        return darkness >= 0.5;
    }
    
    /**
     * Setup keyboard shortcuts
     */
    private void setupKeyBindings() {
        JRootPane rootPane = getRootPane();
        
        // F3 - Viewer
        rootPane.registerKeyboardAction(e -> viewFile(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
        // F4 - Editor
        rootPane.registerKeyboardAction(e -> editFile(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // F5 - Copy
        rootPane.registerKeyboardAction(e -> copyFiles(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Alt+F5 - Zip
        rootPane.registerKeyboardAction(e -> zipFiles(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F5, InputEvent.ALT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Alt+F9 - Unzip
        rootPane.registerKeyboardAction(e -> unzipFiles(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F9, InputEvent.ALT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Alt+O - Settings
        rootPane.registerKeyboardAction(e -> showSettingsDialog(),
                KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.ALT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // F6 - Move
        rootPane.registerKeyboardAction(e -> moveFiles(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Shift+F6 - Rename (alternative to F9)
        rootPane.registerKeyboardAction(e -> renameFile(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F6, InputEvent.SHIFT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // F7 - New directory
        rootPane.registerKeyboardAction(e -> createNewDirectory(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // F8 - Delete
        rootPane.registerKeyboardAction(e -> deleteFiles(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);

    // F9 - Open terminal
        rootPane.registerKeyboardAction(e -> openTerminal(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);

        // Ctrl+TAB - switch tabs in active panel
        rootPane.registerKeyboardAction(e -> {
            if (activePanel != null) {
                activePanel.nextTab();
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

        // ESC - global escape to return focus to panels
        rootPane.registerKeyboardAction(e -> {
            boolean textCleared = false;
            Object currentItem = commandLine.getEditor().getItem();
            if (currentItem != null && !currentItem.toString().isEmpty()) {
                commandLine.getEditor().setItem("");
                textCleared = true;
            }
            
            // If we just cleared text, or if focus is not in any table, return focus to active panel
            Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
            boolean inTable = (focusOwner instanceof JTable);
            
            if (textCleared || !inTable) {
                if (activePanel != null && activePanel.getFileTable() != null) {
                    activePanel.getFileTable().requestFocusInWindow();
                }
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Delete key - global delete binding (also added per-table)
    rootPane.registerKeyboardAction(e -> deleteFiles(),
        KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
    // Shift+Delete - treat as delete as well
    rootPane.registerKeyboardAction(e -> deleteFiles(),
        KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // No direct F9 keyboard binding: inline rename should only be triggered by Shift+F6

    // TAB - switch panel (global) - works even when additional tabs are opened
    rootPane.registerKeyboardAction(e -> switchPanels(),
        KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Alt+F1 - Select drive for left panel
        rootPane.registerKeyboardAction(e -> selectDriveForLeftPanel(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F1, InputEvent.ALT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Alt+F2 - Select drive for right panel
        rootPane.registerKeyboardAction(e -> selectDriveForRightPanel(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.ALT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Alt+F7 - Search (changed from Ctrl+F)
        rootPane.registerKeyboardAction(e -> showSearchDialog(),
                KeyStroke.getKeyStroke(KeyEvent.VK_F7, InputEvent.ALT_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Ctrl+F1 - Full details
        rootPane.registerKeyboardAction(e -> setActiveViewMode(ViewMode.FULL),
                KeyStroke.getKeyStroke(KeyEvent.VK_F1, InputEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Ctrl+F2 - Names only
        rootPane.registerKeyboardAction(e -> setActiveViewMode(ViewMode.BRIEF),
                KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Ctrl+T - New tab
        rootPane.registerKeyboardAction(e -> addNewTabToActivePanel(),
                KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
        
    // Ctrl+W - Close current tab
        rootPane.registerKeyboardAction(e -> closeCurrentTabInActivePanel(),
                KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Ctrl+` - Home directory
        rootPane.registerKeyboardAction(e -> {
            if (activePanel != null && activePanel.getCurrentTab() != null) {
                activePanel.getCurrentTab().loadDirectory(new File(System.getProperty("user.home")));
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_QUOTE, InputEvent.CTRL_DOWN_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Ctrl+C - Copy to clipboard
        rootPane.registerKeyboardAction(e -> {
            if (activePanel != null && activePanel.getCurrentTab() != null) {
                activePanel.getCurrentTab().copyToClipboard(false);
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Ctrl+X - Cut to clipboard
        rootPane.registerKeyboardAction(e -> {
            if (activePanel != null && activePanel.getCurrentTab() != null) {
                activePanel.getCurrentTab().copyToClipboard(true);
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Ctrl+V - Paste from clipboard
        rootPane.registerKeyboardAction(e -> {
            if (activePanel != null && activePanel.getCurrentTab() != null) {
                activePanel.getCurrentTab().pasteFromClipboard();
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Ctrl+E - Command line history
        rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
                KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);

    // Ctrl+A - Select files by wildcard
        rootPane.registerKeyboardAction(e -> showWildcardSelectDialog(),
                KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
    }
    
    /**
     * Update panel borders according to active panel
     */
    private void updateActivePanelBorder() {
        Color selColor = config.getSelectionColor();
        if (selColor == null) selColor = new Color(184, 207, 229);
        
        // Delegate active state visualization to the panels themselves
        leftPanel.setActive(activePanel == leftPanel, selColor);
        rightPanel.setActive(activePanel == rightPanel, selColor);
    }
    
    /**
     * Switch between panels
     */
    private void switchPanels() {
        // Determine which panel currently (or recently) has focus by inspecting the focus owner.
        java.awt.Component owner = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
        boolean ownerInLeft = false;
        boolean ownerInRight = false;
        if (owner != null) {
            Component p = owner;
            while (p != null) {
                if (p == leftPanel) {
                    ownerInLeft = true;
                    break;
                }
                if (p == rightPanel) {
                    ownerInRight = true;
                    break;
                }
                p = p.getParent();
            }
        }

        if (ownerInLeft) {
            rightPanel.requestFocusOnCurrentTab();
            activePanel = rightPanel;
        } else if (ownerInRight) {
            leftPanel.requestFocusOnCurrentTab();
            activePanel = leftPanel;
        } else {
            // Fallback to previous behavior based on activePanel
            if (activePanel == leftPanel) {
                rightPanel.requestFocusOnCurrentTab();
                activePanel = rightPanel;
            } else {
                leftPanel.requestFocusOnCurrentTab();
                activePanel = leftPanel;
            }
        }

        // Update panel borders and force repaint so renderer can update focus colors
        updateActivePanelBorder();
        JTable lt = leftPanel.getFileTable();
        JTable rt = rightPanel.getFileTable();
        if (lt != null) lt.repaint();
        if (rt != null) rt.repaint();
        updateCommandLinePrompt();
    }

    /**
     * Public wrapper so child components (tabs) can request a panel switch.
     */
    public void switchPanelsFromChild() {
        switchPanels();
    }

    private void updateCommandLinePrompt() {
        if (cmdLabel == null) return;
        
        FilePanel active = activePanel;
        if (active == null) return;
        
        String path = active.getCurrentPath();
        cmdLabel.setText(path + ">");
    }
    
    /**
     * Attach TAB handling to switch panels
     */
    private void addTabKeyHandler(JTable table) {
        if (table == null) return;
        
        // Remove standard Swing TAB behavior
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "switchPanel");
        table.getActionMap().put("switchPanel", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                switchPanels();
            }
        });

        // Add Ctrl+Tab handling at the table level
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK), "nextTab");
        table.getActionMap().put("nextTab", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (activePanel != null) {
                    activePanel.nextTab();
                }
            }
        });
        
        // Add F8 for deleting with higher priority than default Swing actions
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), "deleteFiles");
        table.getActionMap().put("deleteFiles", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                deleteFiles();
            }
        });
        // Also map Delete key to deleteFiles on the table level so it works when table has focus
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "deleteFiles");
       // Also map Shift+Delete on table level
       table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
           .put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK), "deleteFiles");

        // Clipboard support (Ctrl+C, Ctrl+X, Ctrl+V)
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK), "clipboardCopy");
        table.getActionMap().put("clipboardCopy", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (activePanel != null && activePanel.getCurrentTab() != null) {
                    activePanel.getCurrentTab().copyToClipboard(false);
                }
            }
        });

        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK), "clipboardCut");
        table.getActionMap().put("clipboardCut", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (activePanel != null && activePanel.getCurrentTab() != null) {
                    activePanel.getCurrentTab().copyToClipboard(true);
                }
            }
        });

        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "clipboardPaste");
        table.getActionMap().put("clipboardPaste", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (activePanel != null && activePanel.getCurrentTab() != null) {
                    activePanel.getCurrentTab().pasteFromClipboard();
                }
            }
        });

        // Wildcard selection (Ctrl+A)
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), "wildcardSelect");
        table.getActionMap().put("wildcardSelect", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showWildcardSelectDialog();
            }
        });
    }

    /**
     * Automatically focus command line when user starts typing on a table
     */
    private void addCommandLineRedirect(JTable table) {
        // Use InputMap/ActionMap for Ctrl+Enter and Ctrl+Shift+Enter as KeyListener might be bypassed by JTable
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK), "copyNameToCmd");
        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), "copyPathToCmd");

        table.getActionMap().put("copyNameToCmd", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                copyFocusedToCommandLine(false);
            }
        });

        table.getActionMap().put("copyPathToCmd", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                copyFocusedToCommandLine(true);
            }
        });

        table.addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                char c = e.getKeyChar();
                // Transfer to command line only for printable characters and when no modifiers like Ctrl or Alt are pressed.
                // This prevents focus jump on shortcuts like Ctrl+C, Ctrl+V, etc.
                if (c != KeyEvent.CHAR_UNDEFINED && c >= 32 && c != 127 && !e.isControlDown() && !e.isAltDown()) {
                    commandLine.requestFocusInWindow();
                    String current = commandLine.getEditor().getItem().toString();
                    commandLine.getEditor().setItem(current + c);
                    e.consume();
                }
            }
        });
    }

    private void copyFocusedToCommandLine(boolean fullPath) {
        FileItem focused = activePanel.getFocusedItem();
        if (focused != null && !focused.getName().equals("..")) {
            String current = commandLine.getEditor().getItem().toString();
            String toAdd = fullPath ? focused.getFile().getAbsolutePath() : focused.getName();
            
            // If it contains spaces, wrap in quotes
            if (toAdd.contains(" ")) {
                toAdd = "\"" + toAdd + "\"";
            }
            
            if (!current.isEmpty() && !current.endsWith(" ")) {
                commandLine.getEditor().setItem(current + " " + toAdd);
            } else {
                commandLine.getEditor().setItem(current + toAdd);
            }
            commandLine.requestFocusInWindow();
        }
    }
    
    /**
     * Copy selected files to the opposite panel
     */
    private void copyFiles() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
            JOptionPane.showMessageDialog(this,
                "No files selected",
                "Copy",
                JOptionPane.INFORMATION_MESSAGE);
            requestFocusInActivePanel();
            return;
        }
        
        FilePanel targetPanel = (activePanel == leftPanel) ? rightPanel : leftPanel;
        File targetDir = targetPanel.getCurrentDirectory();
        
        int result = JOptionPane.showConfirmDialog(this,
                String.format("Copy %d items to:\n%s", selectedItems.size(), targetDir.getAbsolutePath()),
                "Copy",
                JOptionPane.OK_CANCEL_OPTION);
        
        if (result == JOptionPane.OK_OPTION) {
            performFileOperation((callback) -> {
                FileOperations.copy(selectedItems, targetDir, callback);
            }, "Copy completed", true, targetPanel);
        } else {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        }
    }
    
    /**
     * Move selected files to the opposite panel
     */
    private void moveFiles() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
            JOptionPane.showMessageDialog(this,
                "No files selected",
                "Move",
                JOptionPane.INFORMATION_MESSAGE);
            requestFocusInActivePanel();
            return;
        }
        
        FilePanel targetPanel = (activePanel == leftPanel) ? rightPanel : leftPanel;
        File targetDir = targetPanel.getCurrentDirectory();
        
        int result = JOptionPane.showConfirmDialog(this,
                String.format("Move %d items to:\n%s", selectedItems.size(), targetDir.getAbsolutePath()),
                "Move",
                JOptionPane.OK_CANCEL_OPTION);
        
        if (result == JOptionPane.OK_OPTION) {
            performFileOperation((callback) -> {
                FileOperations.move(selectedItems, targetDir, callback);
            }, "Move completed", false, activePanel, targetPanel);
        } else {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        }
    }
    
    /**
     * Delete selected files
     */
    private void deleteFiles() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
        JOptionPane.showMessageDialog(this,
            "No files selected",
            "Delete",
            JOptionPane.INFORMATION_MESSAGE);
            requestFocusInActivePanel();
            return;
        }
        // remember current selection index so we can restore selection after deletion
        final int rememberedIndex = (activePanel != null && activePanel.getCurrentTab() != null) ? 
            activePanel.getCurrentTab().getFocusedItemIndex() : -1;

    StringBuilder message = new StringBuilder("Really delete the following items?\n\n");
        for (FileItem item : selectedItems) {
                message.append(item.getName()).append("\n");
            if (message.length() > 500) {
                message.append("...");
                break;
            }
        }
        
        int result = JOptionPane.showConfirmDialog(this,
                message.toString(),
                "Delete",
                JOptionPane.YES_NO_OPTION,
                JOptionPane.WARNING_MESSAGE);
        
        if (result == JOptionPane.YES_OPTION) {
            performFileOperation((callback) -> {
                FileOperations.delete(selectedItems, callback);
            }, "Delete completed", false, activePanel);

            // After deletion and refresh, restore selection: move focus to the nearest higher item.
            SwingUtilities.invokeLater(() -> {
                try {
                    if (activePanel != null && activePanel.getCurrentTab() != null) {
                        // Use another invokeLater to ensure BRIEF mode layout is updated
                        SwingUtilities.invokeLater(() -> {
                            activePanel.getCurrentTab().selectItemByIndex(rememberedIndex - 1);
                        });
                    }
                } catch (Exception ignore) {}
            });
        } else {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        }
    }
    
    /**
     * Zip selected files
     */
    private void zipFiles() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
            JOptionPane.showMessageDialog(this,
                    "No files selected",
                    "Zip",
                    JOptionPane.INFORMATION_MESSAGE);
            requestFocusInActivePanel();
            return;
        }

        String defaultName;
        if (selectedItems.size() == 1) {
            defaultName = selectedItems.get(0).getName();
        } else {
            defaultName = activePanel.getCurrentDirectory().getName();
            if (defaultName == null || defaultName.isEmpty() || defaultName.equals("/") || defaultName.endsWith(":")) {
                defaultName = "archive";
            }
        }
        
        if (defaultName.contains(".")) {
            int lastDot = defaultName.lastIndexOf('.');
            if (lastDot > 0) {
                defaultName = defaultName.substring(0, lastDot);
            }
        }
        defaultName += ".zip";

        String zipName = JOptionPane.showInputDialog(this, "Enter zip filename:", defaultName);
        if (zipName == null || zipName.trim().isEmpty()) {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
            return;
        }

        if (!zipName.toLowerCase().endsWith(".zip")) {
            zipName += ".zip";
        }

        FilePanel targetPanel = (activePanel == leftPanel) ? rightPanel : leftPanel;
        File targetDir = targetPanel.getCurrentDirectory();
        File targetZip = new File(targetDir, zipName);
        
        if (targetZip.exists()) {
            int confirm = JOptionPane.showConfirmDialog(this, 
                "File already exists. Overwrite?", "Zip", JOptionPane.YES_NO_OPTION);
            if (confirm != JOptionPane.YES_OPTION) {
                if (activePanel != null && activePanel.getFileTable() != null) {
                    activePanel.getFileTable().requestFocusInWindow();
                }
                return;
            }
        }

        final File finalTargetZip = targetZip;
        performFileOperation((callback) -> {
            FileOperations.zip(selectedItems, finalTargetZip, callback);
        }, "Zipped into " + zipName, false, targetPanel, targetPanel);
    }

    /**
     * Unzip selected zip file
     */
    private void unzipFiles() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
            JOptionPane.showMessageDialog(this,
                    "No files selected",
                    "Unzip",
                    JOptionPane.INFORMATION_MESSAGE);
            requestFocusInActivePanel();
            return;
        }

        File zipFile = selectedItems.get(0).getFile();
        if (!zipFile.getName().toLowerCase().endsWith(".zip")) {
            JOptionPane.showMessageDialog(this,
                    "Selected file is not a ZIP archive",
                    "Unzip",
                    JOptionPane.ERROR_MESSAGE);
            requestFocusInActivePanel();
            return;
        }

        FilePanel targetPanel = (activePanel == leftPanel) ? rightPanel : leftPanel;
        File targetDir = targetPanel.getCurrentDirectory();

        int result = JOptionPane.showConfirmDialog(this,
                String.format("Unzip %s to:\n%s", zipFile.getName(), targetDir.getAbsolutePath()),
                "Unzip",
                JOptionPane.OK_CANCEL_OPTION);

        if (result == JOptionPane.OK_OPTION) {
            performFileOperation((callback) -> {
                FileOperations.unzip(zipFile, targetDir, callback);
            }, "Unzipped into " + targetDir.getName(), false, targetPanel);
        } else {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        }
    }
    
    /**
     * Rename selected file
     */
    private void renameFile() {
        // Start inline rename in the active panel (it will handle validation)
        if (activePanel != null) {
            try {
                activePanel.startInlineRename();
            } catch (Exception ex) {
                // Fallback to dialog-based rename if inline fails for some reason
                List<FileItem> selectedItems = activePanel.getSelectedItems();
                if (selectedItems.size() != 1) {
                    JOptionPane.showMessageDialog(this,
                            "Select one file to rename",
                            "Rename",
                            JOptionPane.INFORMATION_MESSAGE);
                    requestFocusInActivePanel();
                    return;
                }
                FileItem item = selectedItems.get(0);
                String newName = JOptionPane.showInputDialog(this,
                        "New name:",
                        item.getName());
                if (newName != null && !newName.trim().isEmpty() && !newName.equals(item.getName())) {
                    performFileOperation((callback) -> {
                        FileOperations.rename(item.getFile(), newName.trim());
                    }, "Rename completed", false, activePanel);
                } else {
                    if (activePanel != null && activePanel.getFileTable() != null) {
                        activePanel.getFileTable().requestFocusInWindow();
                    }
                }
            }
        }
    }
    
    /**
     * Create a new directory
     */
    private void createNewDirectory() {
        String dirNameInput = JOptionPane.showInputDialog(this,
            "New directory name:",
            "New directory");
            
        if (dirNameInput != null && !dirNameInput.trim().isEmpty()) {
            final String dirName = dirNameInput.trim();
            performFileOperation((callback) -> {
                FileOperations.createDirectory(activePanel.getCurrentDirectory(), dirName);
            }, "Directory created", false, () -> {
                if (activePanel != null && activePanel.getCurrentTab() != null) {
                    activePanel.getCurrentTab().selectItem(dirName);
                }
            }, activePanel);
        } else {
            if (activePanel != null && activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        }
    }
    
    /**
     * Show search dialog
     */
    private void showSearchDialog() {
        SearchDialog dialog = new SearchDialog(this, activePanel.getCurrentDirectory(), config);
        dialog.addWindowListener(new java.awt.event.WindowAdapter() {
            @Override
            public void windowClosed(java.awt.event.WindowEvent e) {
                requestFocusInActivePanel();
            }
        });
        dialog.setVisible(true);
    }

    /**
     * Show wildcard select dialog
     */
    public void showWildcardSelectDialog() {
        if (activePanel == null) return;
        
        WildcardSelectDialog dialog = new WildcardSelectDialog(this);
        dialog.setVisible(true);
        
        String pattern = dialog.getPattern();
        if (pattern != null && !pattern.isEmpty()) {
            activePanel.selectByWildcard(pattern);
        } else {
            // If cancelled, return focus to the active panel
            if (activePanel.getFileTable() != null) {
                activePanel.getFileTable().requestFocusInWindow();
            }
        }
    }

    /**
     * Show the given file's parent directory in the panel that currently has focus
     * and select the file in that panel.
     */
    public void showFileInFocusedPanel(File file) {
        if (file == null) return;
        // Determine which panel currently has focus
        java.awt.Component owner = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
        FilePanel target = null;
        if (owner != null) {
            Component p = owner;
            while (p != null) {
                if (p == leftPanel) {
                    target = leftPanel;
                    break;
                }
                if (p == rightPanel) {
                    target = rightPanel;
                    break;
                }
                p = p.getParent();
            }
        }
        if (target == null) target = activePanel != null ? activePanel : leftPanel;

        final FilePanel chosen = target;
        final File parentDir = file.getParentFile();
        if (parentDir == null) return;

        // Load directory and then select item by name on the EDT
        SwingUtilities.invokeLater(() -> {
            chosen.loadDirectory(parentDir);
            // mark this panel active and refresh borders
            activePanel = chosen;
            updateActivePanelBorder();
            // After loading, select the file name
            SwingUtilities.invokeLater(() -> {
                FilePanelTab tab = chosen.getCurrentTab();
                if (tab != null) {
                    tab.selectItem(file.getName());
                    tab.getFileTable().requestFocusInWindow();
                }
            });
        });
    }
    
    /**
     * Show file in internal viewer
     */
    private void viewFile() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
            return;
        }
        
        FileItem item = selectedItems.get(0);
        if (item.isDirectory() || item.getName().equals("..")) {
            return;
        }
        
        File file = item.getFile();
        
    // Removed previous 10 MB limit: allow opening large files in the viewer (paged hex will stream large binaries).
        
        FileEditor viewer = new FileEditor(this, file, config, true);
        viewer.addWindowListener(new java.awt.event.WindowAdapter() {
            @Override
            public void windowClosed(java.awt.event.WindowEvent e) {
                requestFocusInActivePanel();
            }
        });
        viewer.setVisible(true);
    }
    
    /**
     * Open file in internal editor
     */
    private void editFile() {
        List<FileItem> selectedItems = activePanel.getSelectedItems();
        if (selectedItems.isEmpty()) {
            return;
        }
        
        FileItem item = selectedItems.get(0);
        if (item.isDirectory() || item.getName().equals("..")) {
            return;
        }
        
        File file = item.getFile();
        
        // If an external editor is configured, try launching it with the file path
        String ext = config.getExternalEditorPath();
        if (ext != null && !ext.trim().isEmpty()) {
            try {
                java.util.List<String> cmd = new java.util.ArrayList<>();
                cmd.add(ext);
                cmd.add(file.getAbsolutePath());
                new ProcessBuilder(cmd).start();
                return;
            } catch (Exception ex) {
                // Fall back to internal editor if external fails
                JOptionPane.showMessageDialog(this,
                        "Could not start external editor: " + ex.getMessage() + "\nUsing internal editor.",
                        "Error", JOptionPane.ERROR_MESSAGE);
            }
        }

        // Removed previous 10 MB limit: allow opening large files in the editor. The editor may still choose hex/paged mode for large binaries.
        FileEditor editor = new FileEditor(this, file, config, false);
        editor.addWindowListener(new java.awt.event.WindowAdapter() {
            @Override
            public void windowClosed(java.awt.event.WindowEvent e) {
                requestFocusInActivePanel();
            }
        });
        editor.setVisible(true);
    }
    
    /**
     * Refresh both panels
     */
    private void refreshPanels() {
        // Refresh is now automatic upon changes
        // If manual refresh is needed, we can call loadDirectory
        if (leftPanel.getCurrentDirectory() != null) {
            leftPanel.loadDirectory(leftPanel.getCurrentDirectory());
        }
        if (rightPanel.getCurrentDirectory() != null) {
            rightPanel.loadDirectory(rightPanel.getCurrentDirectory());
        }
    }
    
    /**
     * Set the view mode for the active panel
     */
    private void setActiveViewMode(ViewMode mode) {
        if (activePanel != null) {
            activePanel.setViewMode(mode);
        }
    }

    /**
     * Show history of command line
     */
    private void showCommandLineHistory() {
        if (commandLine != null && commandLine.getItemCount() > 0) {
            commandLine.requestFocusInWindow();
            if (!commandLine.isPopupVisible()) {
                commandLine.setSelectedIndex(0);
                commandLine.showPopup();
            } else {
                int count = commandLine.getItemCount();
                int current = commandLine.getSelectedIndex();
                // If index is -1 or invalid, start from 0, otherwise go to next
                int nextIndex = (current < 0) ? 0 : (current + 1) % count;
                commandLine.setSelectedIndex(nextIndex);
            }
        }
    }
    
    /**
     * Add a new tab to the active panel
     */
    private void addNewTabToActivePanel() {
        if (activePanel != null) {
            File currentDir = activePanel.getCurrentDirectory();
            String path = currentDir != null ? currentDir.getAbsolutePath() : System.getProperty("user.home");
            activePanel.addNewTab(path);
        }
    }
    
    /**
     * Close the current tab in the active panel
     */
    private void closeCurrentTabInActivePanel() {
        if (activePanel != null) {
            activePanel.closeCurrentTab();
        }
    }
    
    /**
     * Open terminal in the current directory
     */
    private void openTerminal() {
        File currentDir = activePanel.getCurrentDirectory();
        if (currentDir == null) {
            currentDir = new File(System.getProperty("user.home"));
        }
        
        try {
            String osName = System.getProperty("os.name").toLowerCase();
            ProcessBuilder pb = null;
            
            if (osName.contains("win")) {
                // Windows
                pb = new ProcessBuilder("cmd.exe", "/c", "start", "cmd.exe");
            } else if (osName.contains("mac")) {
                // macOS
                pb = new ProcessBuilder("open", "-a", "Terminal", currentDir.getAbsolutePath());
            } else {
                // Linux and other Unix-like systems
                // Try common terminal emulators with working directory arguments
                String[] terminals = {"gnome-terminal", "konsole", "xfce4-terminal", "mate-terminal", "xterm"};
                
                for (String terminal : terminals) {
                    try {
                        Process p = Runtime.getRuntime().exec(new String[]{"which", terminal});
                        if (p.waitFor() == 0) {
                            if (terminal.equals("gnome-terminal") || terminal.equals("xfce4-terminal") || terminal.equals("mate-terminal")) {
                                pb = new ProcessBuilder(terminal, "--working-directory=" + currentDir.getAbsolutePath());
                            } else if (terminal.equals("konsole")) {
                                pb = new ProcessBuilder(terminal, "--workdir", currentDir.getAbsolutePath());
                            } else {
                                pb = new ProcessBuilder(terminal);
                            }
                            break;
                        }
                    } catch (Exception e) {
                        // Try next terminal
                    }
                }
                
                if (pb == null) {
                    // Fallback to xterm
                    pb = new ProcessBuilder("xterm");
                }
            }
            
            if (pb != null) {
                pb.directory(currentDir);
                pb.start();
            }
            
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this,
                    "Error opening terminal: " + e.getMessage(),
                    "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    private void executeCommand(String command) {
        if (command == null || command.trim().isEmpty()) {
            activePanel.getFileTable().requestFocusInWindow();
            return;
        }

        // Add to history
        addCommandToHistory(command.trim());
        
        // Final prompt update after command execution (path might have changed)
        updateCommandLinePrompt();

        // Execute natively, not via bash wrapper
        executeNative(command.trim(), null);

        // Clear after execution and return focus
        Component editorComp = commandLine.getEditor().getEditorComponent();
        if (editorComp instanceof JTextField) {
            ((JTextField) editorComp).setText("");
        } else {
            commandLine.setSelectedItem("");
        }
        activePanel.getFileTable().requestFocusInWindow();
    }

    private void executeNative(String command, String workingDir) {
        if (command == null || command.trim().isEmpty()) return;

        File currentDir;
        if (workingDir != null && !workingDir.trim().isEmpty()) {
            currentDir = new File(workingDir);
        } else {
            currentDir = activePanel.getCurrentDirectory();
        }
        
        if (currentDir == null || !currentDir.exists()) {
            currentDir = new File(System.getProperty("user.home"));
        }

        try {
            // Check if it's a file path that exists (and not a complex command)
            if (!command.contains(" ") || (command.startsWith("\"") && command.endsWith("\"") && !command.substring(1, command.length()-1).contains("\""))) {
                String path = command.startsWith("\"") ? command.substring(1, command.length()-1) : command;
                File file = new File(path);
                if (file.exists()) {
                    // Start executable files directly, otherwise use Desktop.open
                    if (file.canExecute() && !file.isDirectory()) {
                        new ProcessBuilder(file.getAbsolutePath()).directory(currentDir).start();
                        return;
                    }
                    if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
                        Desktop.getDesktop().open(file);
                        return;
                    }
                }
            }

            // Otherwise try running as a native process
            List<String> cmdList = parseCommand(command);
            try {
                new ProcessBuilder(cmdList).directory(currentDir).start();
            } catch (IOException ex) {
                // Fallback for Linux/macOS: try via shell
                String osName = System.getProperty("os.name").toLowerCase();
                if (osName.contains("linux") || osName.contains("mac")) {
                    new ProcessBuilder("sh", "-c", command).directory(currentDir).start();
                } else {
                    throw ex;
                }
            }
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this,
                    "Error executing native command: " + e.getMessage(),
                    "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    private List<String> parseCommand(String command) {
        List<String> list = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        boolean inQuotes = false;
        for (int i = 0; i < command.length(); i++) {
            char c = command.charAt(i);
            if (c == '\"') {
                inQuotes = !inQuotes;
            } else if (c == ' ' && !inQuotes) {
                if (sb.length() > 0) {
                    list.add(sb.toString());
                    sb.setLength(0);
                }
            } else {
                sb.append(c);
            }
        }
        if (sb.length() > 0) {
            list.add(sb.toString());
        }
        return list;
    }

    private void addCommandToHistory(String command) {
        if (command == null || command.isEmpty()) return;
        
        // Remove if already exists to move it to the top
        for (int i = 0; i < commandLine.getItemCount(); i++) {
            if (command.equals(commandLine.getItemAt(i))) {
                commandLine.removeItemAt(i);
                break;
            }
        }
        commandLine.insertItemAt(command, 0);
        // We don't necessarily want to select it here as it might interfere with the editor state
    }

    /**
     * Show About dialog
     */
    private void showAboutDialog() {
        JOptionPane.showMessageDialog(this,
        "KF File Manager 1.0\n\n" +
            "Two-panel file manager\n" +
            "Java 11\n\n" +
            "Keyboard shortcuts:\n" +
            "F5 - Copy\n" +
            "Alt+F5 - Zip\n" +
            "Alt+F9 - Unzip\n" +
            "F6 - Move\n" +
            "F7 - New directory\n" +
            "F8 - Delete\n" +
            "F9 - Open terminal\n" +
            "Shift+F6 - Rename\n" +
            "TAB - Switch panel\n" +
            "Ctrl+F - Search\n" +
            "Alt+O - Settings\n" +
            "Enter - Open directory\n" +
            "Backspace - Parent directory",
        "About",
                JOptionPane.INFORMATION_MESSAGE);
        requestFocusInActivePanel();
    }
    
    /**
     * Execute file operation with error handling
     */
    private void performFileOperation(FileOperation operation, String successMessage, boolean showBytes, FilePanel... panelsToRefresh) {
        performFileOperation(operation, successMessage, showBytes, null, panelsToRefresh);
    }

    /**
     * Execute file operation with error handling and a task to run after completion and refresh.
     */
    private void performFileOperation(FileOperation operation, String successMessage, boolean showBytes, Runnable postTask, FilePanel... panelsToRefresh) {
        ProgressDialog progressDialog = new ProgressDialog(this, "File Operation");
        progressDialog.setDisplayAsBytes(showBytes);
        
        FileOperations.ProgressCallback callback = new FileOperations.ProgressCallback() {
            @Override
            public void onProgress(long current, long total, String currentFile) {
                progressDialog.updateProgress(current, total, currentFile);
            }

            @Override
            public boolean isCancelled() {
                return progressDialog.isCancelled();
            }

            @Override
            public FileOperations.OverwriteResponse confirmOverwrite(File file) {
                final FileOperations.OverwriteResponse[] result = new FileOperations.OverwriteResponse[1];
                try {
                    SwingUtilities.invokeAndWait(() -> {
                        Object[] options = {"Yes", "Yes to All", "No", "No to All", "Cancel"};
                        int n = JOptionPane.showOptionDialog(progressDialog,
                                "File already exists: " + file.getName() + "\nOverwrite?",
                                "Overwrite Confirmation",
                                JOptionPane.DEFAULT_OPTION,
                                JOptionPane.QUESTION_MESSAGE,
                                null,
                                options,
                                options[0]);

                        switch (n) {
                            case 0: result[0] = FileOperations.OverwriteResponse.YES; break;
                            case 1: result[0] = FileOperations.OverwriteResponse.YES_TO_ALL; break;
                            case 2: result[0] = FileOperations.OverwriteResponse.NO; break;
                            case 3: result[0] = FileOperations.OverwriteResponse.NO_TO_ALL; break;
                            default: 
                                result[0] = FileOperations.OverwriteResponse.CANCEL;
                                progressDialog.cancel();
                                break;
                        }
                    });
                } catch (Exception e) {
                    result[0] = FileOperations.OverwriteResponse.CANCEL;
                }
                return result[0];
            }
        };

        // Run operation in a background thread
        new Thread(() -> {
            try {
                operation.execute(callback);
                
                SwingUtilities.invokeLater(() -> {
                    progressDialog.dispose();
                    for (FilePanel panel : panelsToRefresh) {
                        if (panel.getCurrentDirectory() != null) {
                            panel.loadDirectory(panel.getCurrentDirectory(), false, false);
                        }
                    }

                    if (postTask != null) {
                        SwingUtilities.invokeLater(postTask);
                    }
                    
                    if (activePanel != null && activePanel.getFileTable() != null) {
                        activePanel.getFileTable().requestFocusInWindow();
                    }
                    
                    if (callback.isCancelled()) {
                        JOptionPane.showMessageDialog(MainWindow.this, "Operation was cancelled by user.");
                        if (activePanel != null && activePanel.getFileTable() != null) {
                            activePanel.getFileTable().requestFocusInWindow();
                        }
                    }
                });
            } catch (Exception e) {
                SwingUtilities.invokeLater(() -> {
                    progressDialog.dispose();
                    JOptionPane.showMessageDialog(MainWindow.this, 
                            "Error: " + e.getMessage(), 
                            "Error", 
                            JOptionPane.ERROR_MESSAGE);
                    if (activePanel != null && activePanel.getFileTable() != null) {
                        activePanel.getFileTable().requestFocusInWindow();
                    }
                });
            }
        }).start();

        progressDialog.setVisible(true);
    }
    
    /**
     * Show drive selector for left panel
     */
    private void selectDriveForLeftPanel() {
        // Open the drive dropdown in the left panel
        if (leftPanel != null) leftPanel.showDrivePopup();
    }
    
    /**
     * Show drive selector for right panel
     */
    private void selectDriveForRightPanel() {
        // Open the drive dropdown in the right panel
        if (rightPanel != null) rightPanel.showDrivePopup();
    }
    
    /**
     * Save configuration and exit application
     */
    private void saveConfigAndExit() {
    // Save window state
        config.saveWindowState(this);
        
    // Save active panel
        if (activePanel == leftPanel) {
            config.setActivePanel("left");
        } else if (activePanel == rightPanel) {
            config.setActivePanel("right");
        }

    // Save current panel paths (for backward compatibility)
        if (leftPanel.getCurrentDirectory() != null) {
            config.setLeftPanelPath(leftPanel.getCurrentDirectory().getAbsolutePath());
        }
        if (rightPanel.getCurrentDirectory() != null) {
            config.setRightPanelPath(rightPanel.getCurrentDirectory().getAbsolutePath());
        }

    // Save open tabs + view modes and selected index for both panels
        try {
            java.util.List<String> leftPaths = leftPanel.getTabPaths();
            java.util.List<String> leftModes = leftPanel.getTabViewModes();
            java.util.List<String> leftFocused = leftPanel.getTabFocusedItems();
            int leftSelected = leftPanel.getSelectedTabIndex();
            config.saveLeftPanelTabs(leftPaths, leftModes, leftFocused, leftSelected);
        } catch (Exception ex) {
            // ignore
        }

        try {
            java.util.List<String> rightPaths = rightPanel.getTabPaths();
            java.util.List<String> rightModes = rightPanel.getTabViewModes();
            java.util.List<String> rightFocused = rightPanel.getTabFocusedItems();
            int rightSelected = rightPanel.getSelectedTabIndex();
            config.saveRightPanelTabs(rightPaths, rightModes, rightFocused, rightSelected);
        } catch (Exception ex) {
            // ignore
        }

        // Save command history
        java.util.List<String> cmdHistory = new java.util.ArrayList<>();
        for (int i = 0; i < commandLine.getItemCount(); i++) {
            cmdHistory.add(commandLine.getItemAt(i));
        }
        config.saveCommandLineHistory(cmdHistory);
        
        // Save configuration to file
        config.saveConfig();
        
    // Exit application
    System.exit(0);
    }
    
    @FunctionalInterface
    private interface FileOperation {
        void execute(FileOperations.ProgressCallback callback) throws Exception;
    }

    private void requestFocusInActivePanel() {
        if (activePanel != null && activePanel.getFileTable() != null) {
            activePanel.getFileTable().requestFocusInWindow();
        }
    }
}
