/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.ui.dialog.pref.general;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.Comparator;
import java.util.Locale;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.SwingConstants;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.JTextComponent;

import com.mucommander.commons.runtime.OsFamily;
import com.mucommander.commons.util.LocaleUtils;
import com.mucommander.commons.util.ui.layout.SpringUtilities;
import com.mucommander.commons.util.ui.layout.XBoxPanel;
import com.mucommander.commons.util.ui.layout.YBoxPanel;
import com.mucommander.commons.util.ui.spinner.IntEditor;
import com.mucommander.conf.MuConfigurations;
import com.mucommander.conf.MuPreference;
import com.mucommander.conf.MuPreferences;
import com.mucommander.text.Translator;
import com.mucommander.ui.dialog.pref.PreferencesDialog;
import com.mucommander.ui.dialog.pref.PreferencesPanel;
import com.mucommander.ui.dialog.pref.component.PrefCheckBox;
import com.mucommander.ui.dialog.pref.component.PrefComboBox;
import com.mucommander.ui.dialog.pref.component.PrefFilePathField;
import com.mucommander.ui.dialog.pref.component.PrefRadioButton;
import com.mucommander.ui.dialog.pref.component.PrefSpinner;
import com.mucommander.ui.main.WindowManager;


/**
 * 'Folders' preferences panel.
 *
 * @author Maxence Bernard, Mariuz Jakubowski
 */
class FoldersPanel extends PreferencesPanel implements ItemListener, KeyListener, ActionListener {

    // Startup folders
    private PrefRadioButton lastFoldersRadioButton;
    private PrefRadioButton customFoldersRadioButton;
    
    private PrefFilePathFieldWithDefaultValue leftCustomFolderTextField;
    private JButton leftCustomFolderButton;

    private PrefFilePathFieldWithDefaultValue rightCustomFolderTextField;
    private JButton rightCustomFolderButton;

    // Show hidden files?
    private PrefCheckBox showHiddenFilesCheckBox;

    // Show Mac OS X .DS_Store?
    private PrefCheckBox showDSStoreFilesCheckBox;

    // Show system folders ?
    private PrefCheckBox showSystemFoldersCheckBox;

    // Display compact file size ?
    private PrefCheckBox compactSizeCheckBox;

    // Follow symlinks when changing directory ?
    private PrefCheckBox followSymlinksCheckBox;
    
    // Always show single tab's header ?
    private PrefCheckBox showTabHeaderCheckBox;

    // Timeout for quick searches
    private PrefSpinner quickSearchTimeoutSpinner;

    // Locale that is used to sort by filenames
    private PrefComboBox<Locale> localeComboBox;

    // Show parent folder (as "..") in the file table
    private PrefCheckBox showParentFolderCheckBox;

    public FoldersPanel(PreferencesDialog parent) {
        super(parent, Translator.get("prefs_dialog.folders_tab"));

        setLayout(new BorderLayout());


        // Startup folders panel
        YBoxPanel startupFolderPanel = new YBoxPanel();
        startupFolderPanel.setBorder(BorderFactory.createTitledBorder(Translator.get("prefs_dialog.startup_folders")));

        // Last folders or custom folders selections
        lastFoldersRadioButton = new PrefRadioButton(Translator.get("prefs_dialog.last_folder")) {
            public boolean hasChanged() {
                return !(isSelected() ?
                        MuPreferences.STARTUP_FOLDERS_LAST	: MuPreferences.STARTUP_FOLDERS_CUSTOM).equals(
                                MuConfigurations.getPreferences().getVariable(MuPreference.STARTUP_FOLDERS));
            }
        };
        customFoldersRadioButton = new PrefRadioButton(Translator.get("prefs_dialog.custom_folder")) {
            public boolean hasChanged() {
                return !(isSelected() ?
                        MuPreferences.STARTUP_FOLDERS_CUSTOM : MuPreferences.STARTUP_FOLDERS_LAST).equals(
                                MuConfigurations.getPreferences().getVariable(MuPreference.STARTUP_FOLDERS));
            }
        };
        startupFolderPanel.add(lastFoldersRadioButton);
        startupFolderPanel.addSpace(5);
        startupFolderPanel.add(customFoldersRadioButton);

        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(lastFoldersRadioButton);
        buttonGroup.add(customFoldersRadioButton);

        customFoldersRadioButton.addItemListener(this);
        
        // Custom folders specification
        JLabel leftFolderLabel = new JLabel(Translator.get("prefs_dialog.left_folder"));
        leftFolderLabel.setAlignmentX(LEFT_ALIGNMENT);

        JLabel rightFolderLabel = new JLabel(Translator.get("prefs_dialog.right_folder"));
        rightFolderLabel.setAlignmentX(LEFT_ALIGNMENT);

        // Panel that contains the text field and button for specifying custom left folder
        XBoxPanel leftCustomFolderSpecifyingPanel = new XBoxPanel(5);
        leftCustomFolderSpecifyingPanel.setAlignmentX(LEFT_ALIGNMENT);
        
        // Create a path field with auto-completion capabilities
        leftCustomFolderTextField = new PrefFilePathFieldWithDefaultValue(true);
        leftCustomFolderTextField.addKeyListener(this);
        leftCustomFolderSpecifyingPanel.add(leftCustomFolderTextField);

        leftCustomFolderButton = new JButton("...");
        leftCustomFolderButton.addActionListener(this);
        leftCustomFolderSpecifyingPanel.add(leftCustomFolderButton);

        // Panel that contains the text field and button for specifying custom right folder
        XBoxPanel rightCustomFolderSpecifyingPanel = new XBoxPanel(5);
        rightCustomFolderSpecifyingPanel.setAlignmentX(LEFT_ALIGNMENT);

        // Create a path field with auto-completion capabilities
        rightCustomFolderTextField = new PrefFilePathFieldWithDefaultValue(false);
        rightCustomFolderTextField.addKeyListener(this);
        rightCustomFolderSpecifyingPanel.add(rightCustomFolderTextField);

        rightCustomFolderButton = new JButton("...");
        rightCustomFolderButton.addActionListener(this);
        rightCustomFolderSpecifyingPanel.add(rightCustomFolderButton);
        
        JPanel container = new JPanel(new SpringLayout());
        container.add(leftFolderLabel);
        container.add(leftCustomFolderSpecifyingPanel);
        container.add(rightFolderLabel);
        container.add(rightCustomFolderSpecifyingPanel);
        
        //Lay out the panel.
        SpringUtilities.makeCompactGrid(container,
                                        2, 2, //rows, cols
                                        20, 6,        //initX, initY
                                        6, 6);       //xPad, yPad
        
        startupFolderPanel.add(container);
        
        if(MuConfigurations.getPreferences().getVariable(MuPreference.STARTUP_FOLDERS, "").equals(MuPreferences.STARTUP_FOLDERS_LAST)) {
            lastFoldersRadioButton.setSelected(true);
            setCustomFolderComponentsEnabled(false);
        }
        else
            customFoldersRadioButton.setSelected(true);
        
        // --------------------------------------------------------------------------------------------------------------

        YBoxPanel northPanel = new YBoxPanel();
        northPanel.add(startupFolderPanel);
        northPanel.addSpace(5);

        JPanel localePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        localePanel.setBorder(BorderFactory.createTitledBorder(Translator.get("prefs_dialog.filename_language")));
        Function<Locale, String> localeToDisplayName = locale -> {
            var languageTag = locale.toLanguageTag();
            var language = Translator.get(languageTag);
            // if there's no translation for the language tag, use the "display string" of the locale
            if (languageTag.equals(language))
                language = locale.getDisplayLanguage();
            return String.format("%s (%s)", language, languageTag);
        };
        var locales = Stream.of(Locale.getAvailableLocales())
                .filter(locale -> !locale.getLanguage().isEmpty())
                .sorted(Comparator.comparing(localeToDisplayName))
                .toArray(Locale[]::new);
        Supplier<String> currentLanguageTag = () -> MuConfigurations.getPreferences().getVariable(MuPreference.FILENAME_LOCALE, Locale.getDefault().toLanguageTag());
        localeComboBox = new PrefComboBox<>() {
            public boolean hasChanged() {
                return !locales[getSelectedIndex()].toLanguageTag().equals(currentLanguageTag.get());
            }
        };
        localeComboBox.setRenderer(new BasicComboBoxRenderer() {
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

                Locale locale = (Locale)value;
                label.setText(localeToDisplayName.apply(locale));

                return label;
            }
        });
        localeComboBox.addDialogListener(parent);
        Stream.of(locales).forEach(localeComboBox::addItem);
        localeComboBox.setSelectedItem(LocaleUtils.forLanguageTag(currentLanguageTag.get()));

        localePanel.add(localeComboBox);
        northPanel.add(localePanel);
        northPanel.addSpace(10);

        showHiddenFilesCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.show_hidden_files"), () -> MuConfigurations.getPreferences().getVariable(
                MuPreference.SHOW_HIDDEN_FILES,
                MuPreferences.DEFAULT_SHOW_HIDDEN_FILES));
        showHiddenFilesCheckBox.addDialogListener(parent);
        northPanel.add(showHiddenFilesCheckBox);

        // Mac OS X-only options
        if(OsFamily.MAC_OS.isCurrent()) {
            // Monitor showHiddenFilesCheckBox state to disable 'show .DS_Store files' option
            // when 'Show hidden files' is disabled, as .DS_Store files are hidden files
            showHiddenFilesCheckBox.addItemListener(this);

            showDSStoreFilesCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.show_ds_store_files"), () -> MuConfigurations.getPreferences().getVariable(
                    MuPreference.SHOW_DS_STORE_FILES,
                    MuPreferences.DEFAULT_SHOW_DS_STORE_FILES));
            showDSStoreFilesCheckBox.setEnabled(showHiddenFilesCheckBox.isSelected());
            showDSStoreFilesCheckBox.addDialogListener(parent);
            // Shift the check box to the right to indicate that it is a sub-option
            northPanel.add(showDSStoreFilesCheckBox, 20);
        }

        showParentFolderCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.show_parent"), () -> MuConfigurations.getPreferences().getVariable(
                MuPreference.SHOW_PARENT_FOLDER,
                MuPreferences.DEFAULT_SHOW_PARENT_FOLDER));
        showParentFolderCheckBox.addDialogListener(parent);
        northPanel.add(showParentFolderCheckBox);

        if (OsFamily.MAC_OS.isCurrent() || OsFamily.WINDOWS.isCurrent()) {
            showSystemFoldersCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.show_system_folders"), () -> MuConfigurations.getPreferences().getVariable(
                    MuPreference.SHOW_SYSTEM_FOLDERS,
                    MuPreferences.DEFAULT_SHOW_SYSTEM_FOLDERS));
            showSystemFoldersCheckBox.addDialogListener(parent);
            northPanel.add(showSystemFoldersCheckBox);
        }

        compactSizeCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.compact_file_size"), () -> MuConfigurations.getPreferences().getVariable(
                MuPreference.DISPLAY_COMPACT_FILE_SIZE,
                MuPreferences.DEFAULT_DISPLAY_COMPACT_FILE_SIZE));
        compactSizeCheckBox.addDialogListener(parent);
        northPanel.add(compactSizeCheckBox);

        followSymlinksCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.follow_symlinks_when_cd"), () -> MuConfigurations.getPreferences().getVariable(
                MuPreference.CD_FOLLOWS_SYMLINKS,
                MuPreferences.DEFAULT_CD_FOLLOWS_SYMLINKS));
        followSymlinksCheckBox.addDialogListener(parent);
        northPanel.add(followSymlinksCheckBox);

        showTabHeaderCheckBox = new PrefCheckBox(Translator.get("prefs_dialog.show_tab_header"), () -> MuConfigurations.getPreferences().getVariable(
                MuPreference.SHOW_TAB_HEADER,
                MuPreferences.DEFAULT_SHOW_TAB_HEADER));
        showTabHeaderCheckBox.addDialogListener(parent);
        northPanel.add(showTabHeaderCheckBox);

        JPanel quickSearchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        quickSearchPanel.setBorder(BorderFactory.createEmptyBorder());
        quickSearchTimeoutSpinner = new PrefSpinner(0, 999, 1, () -> MuConfigurations.getPreferences().getVariable(
                MuPreference.QUICK_SEARCH_TIMEOUT,
                MuPreferences.DEFAULT_QUICK_SEARCH_TIMEOUT));
        quickSearchTimeoutSpinner.addDialogListener(parent);
        quickSearchTimeoutSpinner.setEditor(new IntEditor(quickSearchTimeoutSpinner, "###", Translator.get("prefs_dialog.no_quick_search_timeout"), SwingConstants.TRAILING));
        quickSearchPanel.add(new JLabel(Translator.get("prefs_dialog.quick_search_timeout_sec")));
        quickSearchPanel.add(quickSearchTimeoutSpinner);
        northPanel.add(quickSearchPanel);

        add(northPanel, BorderLayout.NORTH);
        
        lastFoldersRadioButton.addDialogListener(parent);
        customFoldersRadioButton.addDialogListener(parent);
        rightCustomFolderTextField.addDialogListener(parent);
        leftCustomFolderTextField.addDialogListener(parent);
    }

    private void setCustomFolderComponentsEnabled(boolean enabled) {
        leftCustomFolderTextField.setEnabled(enabled);
        leftCustomFolderButton.setEnabled(enabled);
        rightCustomFolderTextField.setEnabled(enabled);
        rightCustomFolderButton.setEnabled(enabled);
    }


    /////////////////////////////////////
    // PreferencesPanel implementation //
    /////////////////////////////////////

    @Override
    protected void commit() {
        MuConfigurations.getPreferences().setVariable(MuPreference.STARTUP_FOLDERS, lastFoldersRadioButton.isSelected() ? MuPreferences.STARTUP_FOLDERS_LAST : MuPreferences.STARTUP_FOLDERS_CUSTOM);
        MuConfigurations.getPreferences().setVariable(MuPreference.LEFT_CUSTOM_FOLDER, leftCustomFolderTextField.getFilePath());
        MuConfigurations.getPreferences().setVariable(MuPreference.RIGHT_CUSTOM_FOLDER, rightCustomFolderTextField.getFilePath());
        MuConfigurations.getPreferences().setVariable(MuPreference.DISPLAY_COMPACT_FILE_SIZE, compactSizeCheckBox.isSelected());
        MuConfigurations.getPreferences().setVariable(MuPreference.CD_FOLLOWS_SYMLINKS, followSymlinksCheckBox.isSelected());
        MuConfigurations.getPreferences().setVariable(MuPreference.SHOW_TAB_HEADER, showTabHeaderCheckBox.isSelected());
        MuConfigurations.getPreferences().setVariable(MuPreference.QUICK_SEARCH_TIMEOUT, (int) quickSearchTimeoutSpinner.getValue());
        MuConfigurations.getPreferences().setVariable(MuPreference.FILENAME_LOCALE, localeComboBox.getSelectedItem().toLanguageTag());
        MuConfigurations.getPreferences().setVariable(MuPreference.SHOW_PARENT_FOLDER, showParentFolderCheckBox.isSelected());

        // If one of the show/hide file filters have changed, refresh current folders of current MainFrame
        boolean refreshFolders = MuConfigurations.getPreferences().setVariable(MuPreference.SHOW_HIDDEN_FILES, showHiddenFilesCheckBox.isSelected());

        if(OsFamily.MAC_OS.isCurrent()) {
            refreshFolders |= MuConfigurations.getPreferences().setVariable(MuPreference.SHOW_DS_STORE_FILES, showDSStoreFilesCheckBox.isSelected());
        }

        if(OsFamily.MAC_OS.isCurrent() || OsFamily.WINDOWS.isCurrent()) {
            refreshFolders |= MuConfigurations.getPreferences().setVariable(MuPreference.SHOW_SYSTEM_FOLDERS, showSystemFoldersCheckBox.isSelected());
        }

        if(refreshFolders)
            WindowManager.tryRefreshCurrentFolders();
    }


    /////////////////////////////////
    // ItemListener implementation //
    /////////////////////////////////

    public void itemStateChanged(ItemEvent event) {
        Object source = event.getSource();

        // Disable 'show .DS_Store files' option when 'Show hidden files' is disabled, as .DS_Store files are hidden files
        if(source==showHiddenFilesCheckBox) {
            showDSStoreFilesCheckBox.setEnabled(showHiddenFilesCheckBox.isSelected());
        }
        else if(source==customFoldersRadioButton) {
            setCustomFolderComponentsEnabled(customFoldersRadioButton.isSelected());
        }
    }


    ////////////////////////////////
    // KeyListener implementation //
    ////////////////////////////////

    /**
     * Catches key events to automagically select custom folder radio button if it was not already selected.
     */
    public void keyTyped(KeyEvent e) {
        Object source = e.getSource();

        if(source==leftCustomFolderTextField || source==rightCustomFolderTextField) {
            if(!customFoldersRadioButton.isSelected())
                customFoldersRadioButton.setSelected(true);
        }
    }

    public void keyPressed(KeyEvent e) {
    }

    public void keyReleased(KeyEvent e) {
    }


    ///////////////////////////////////
    // ActionListener implementation //
    ///////////////////////////////////

    /**
     * Opens dialog for selecting starting folder.
     */
    public void actionPerformed(ActionEvent e) {
        Object source = e.getSource();

        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        chooser.setDialogTitle(Translator.get("choose_folder"));
        chooser.setDialogType(JFileChooser.OPEN_DIALOG);
        if(chooser.showDialog(parent, Translator.get("choose")) == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            if (source==leftCustomFolderButton) {
                leftCustomFolderTextField.setText(file.getPath());
                if(!customFoldersRadioButton.isSelected())
                    customFoldersRadioButton.setSelected(true);
            }
            else if (source==rightCustomFolderButton) {
                rightCustomFolderTextField.setText(file.getPath());
                if(!customFoldersRadioButton.isSelected())
                    customFoldersRadioButton.setSelected(true);
            }
        }
    }
    
    public class PrefFilePathFieldWithDefaultValue extends PrefFilePathField {

        private boolean isLeft;
        private final String HOME_FOLDER_PATH = System.getProperty("user.home");

        public PrefFilePathFieldWithDefaultValue(boolean isLeft) {
            super(isLeft ? MuConfigurations.getPreferences().getVariable(MuPreference.LEFT_CUSTOM_FOLDER, "") : MuConfigurations.getPreferences().getVariable(MuPreference.RIGHT_CUSTOM_FOLDER, ""));
            this.isLeft = isLeft;

//    		setUI(new HintTextFieldUI(HOME_FOLDER_PATH, true));
        }

        public boolean hasChanged() {
            return isLeft ?
                    !getText().equals(MuConfigurations.getPreferences().getVariable(MuPreference.LEFT_CUSTOM_FOLDER)) :
                    !getText().equals(MuConfigurations.getPreferences().getVariable(MuPreference.RIGHT_CUSTOM_FOLDER));
        }

        public String getFilePath() {
            String text = super.getText();

            return text.trim().isEmpty() ? HOME_FOLDER_PATH : text;
        }

        private class HintTextFieldUI extends BasicTextFieldUI implements FocusListener {

            private String hint;
            private boolean hideOnFocus;
            private Color color;

            public Color getColor() {
                return color;
            }

            public void setColor(Color color) {
                this.color = color;
                repaint();
            }

            private void repaint() {
                if(getComponent() != null) {
                    getComponent().repaint();
                }
            }

            public boolean isHideOnFocus() {
                return hideOnFocus;
            }

            public void setHideOnFocus(boolean hideOnFocus) {
                this.hideOnFocus = hideOnFocus;
                repaint();
            }

            public String getHint() {
                return hint;
            }

            public void setHint(String hint) {
                this.hint = hint;
                repaint();
            }
            public HintTextFieldUI(String hint) {
                this(hint,false);
            }

            public HintTextFieldUI(String hint, boolean hideOnFocus) {
                this(hint,hideOnFocus, Color.gray);
            }

            public HintTextFieldUI(String hint, boolean hideOnFocus, Color color) {
                this.hint = hint;
                this.hideOnFocus = hideOnFocus;
                this.color = color;
            }

            @Override
            protected void paintSafely(Graphics g) {
                super.paintSafely(g);
                JTextComponent comp = getComponent();
                if(hint!=null && comp.getText().length() == 0 && (!(hideOnFocus && comp.hasFocus()))){
                    if(color != null) {
                        g.setColor(color);
                    } else {
                        g.setColor(comp.getForeground().brighter().brighter().brighter());
                    }
                    int padding = (comp.getHeight() - comp.getFont().getSize())/2;
                    g.drawString(hint, 3, comp.getHeight()-padding-1);
                }
            }

            public void focusGained(FocusEvent e) {
                if(hideOnFocus) repaint();

            }

            public void focusLost(FocusEvent e) {
                if(hideOnFocus) repaint();
            }

            @Override
            protected void installListeners() {
                super.installListeners();
                getComponent().addFocusListener(this);
            }
            @Override
            protected void uninstallListeners() {
                super.uninstallListeners();
                getComponent().removeFocusListener(this);
            }
        }
    }
}
