/*
 * 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.auth;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mucommander.auth.CredentialsManager;
import com.mucommander.auth.CredentialsMapping;
import com.mucommander.commons.collections.AlteredVector;
import com.mucommander.commons.file.Credentials;
import com.mucommander.commons.util.ui.dialog.FocusDialog;
import com.mucommander.commons.util.ui.helper.MnemonicHelper;
import com.mucommander.commons.util.ui.layout.XAlignedComponentPanel;
import com.mucommander.commons.util.ui.layout.XBoxPanel;
import com.mucommander.commons.util.ui.layout.YBoxPanel;
import com.mucommander.text.Translator;
import com.mucommander.ui.action.ActionProperties;
import com.mucommander.ui.action.impl.EditCredentialsAction;
import com.mucommander.ui.list.DynamicList;
import com.mucommander.ui.list.SortableListPanel;
import com.mucommander.ui.main.MainFrame;


/**
 * This dialog contains a list of all persistent credentials and allows the user to edit, remove, go to and reorder them.
 *
 * <p>If the contents of this list is modified, credentials will be saved to disk when this dialog is disposed.</p>
 *
 * @author Maxence Bernard
 */
public class EditCredentialsDialog extends FocusDialog implements ActionListener, ListSelectionListener {
	private static final Logger LOGGER = LoggerFactory.getLogger(EditCredentialsDialog.class);
	
    private MainFrame mainFrame;

    private JButton removeButton;
    private JButton goToButton;
    private JButton closeButton;

    private JTextField loginField;
    private JPasswordField passwordField;

    private AlteredVector<CredentialsMapping> credentials;
    private DynamicList<CredentialsMapping> credentialsList;

    private CredentialsMapping lastSelectedItem;

    // Dialog's size has to be at least 400x300
    private final static Dimension MINIMUM_DIALOG_DIMENSION = new Dimension(440,330);

    // Dialog's size has to be at most 600x400
    private final static Dimension MAXIMUM_DIALOG_DIMENSION = new Dimension(600,400);



    public EditCredentialsDialog(MainFrame mainFrame) {
        super(mainFrame, ActionProperties.getActionLabel(EditCredentialsAction.Descriptor.ACTION_ID), mainFrame);

        this.mainFrame = mainFrame;

        Container contentPane = getContentPane();

        // Retrieve persistent credentials list
        this.credentials = CredentialsManager.getPersistentCredentialMappings();

        // Create the sortable credentials list panel
        SortableListPanel<CredentialsMapping> listPanel = new SortableListPanel<CredentialsMapping>(credentials);
        this.credentialsList = listPanel.getDynamicList();
        this.lastSelectedItem = (CredentialsMapping) credentialsList.getSelectedValue();

        contentPane.add(listPanel, BorderLayout.CENTER);

        // Text fields panel
        XAlignedComponentPanel compPanel = new XAlignedComponentPanel();

        // Add login field
        this.loginField = new JTextField();
        compPanel.addRow(Translator.get("login")+":", loginField, 5);

        // Add password field
        this.passwordField = new JPasswordField();
        compPanel.addRow(Translator.get("password")+":", passwordField, 10);

        YBoxPanel yPanel = new YBoxPanel(10);
        yPanel.add(compPanel);

        XBoxPanel buttonsPanel = new XBoxPanel();
        MnemonicHelper mnemonicHelper = new MnemonicHelper();

        // Remove button
        removeButton = new JButton(credentialsList.getRemoveAction());
        removeButton.setMnemonic(mnemonicHelper.getMnemonic(removeButton));

        buttonsPanel.add(removeButton);

        // Go to button
        goToButton = new JButton(Translator.get("go_to"));
        goToButton.setMnemonic(mnemonicHelper.getMnemonic(goToButton));
        goToButton.addActionListener(this);

        buttonsPanel.add(goToButton);

        // Button that closes the window
        closeButton = new JButton(Translator.get("close"));
        closeButton.setMnemonic(mnemonicHelper.getMnemonic(closeButton));
        closeButton.addActionListener(this);

        buttonsPanel.add(Box.createHorizontalGlue());
        buttonsPanel.add(closeButton);

        yPanel.add(buttonsPanel);

        contentPane.add(yPanel, BorderLayout.SOUTH);

        // Set initial text components and buttons' enabled state
        updateComponents();

        // Listen to selection changes to reflect the change
        credentialsList.addListSelectionListener(this);

        // table will receive initial focus
        setInitialFocusComponent(credentialsList);
		
        // Selects 'Done' button when enter is pressed
        getRootPane().setDefaultButton(closeButton);

        // Packs dialog
        setMinimumSize(MINIMUM_DIALOG_DIMENSION);
        setMaximumSize(MAXIMUM_DIALOG_DIMENSION);

        // Call dispose() on close and write credentials file
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        showDialog();
    }


    /**
     * Updates text fields and buttons' enabled state based on the current selection. Should be called
     * whenever the list selection has changed.
     */
    private void updateComponents() {
        String loginValue = null;
        String passwordValue = null;

        boolean componentsEnabled = false;

        if(!credentialsList.isSelectionEmpty() && credentials.size()>0) {
            componentsEnabled = true;

            CredentialsMapping credentialsMapping = (CredentialsMapping) credentialsList.getSelectedValue();
            Credentials credentials = credentialsMapping.getCredentials();
            loginValue = credentials.getLogin();
            passwordValue = credentials.getPassword();
        }

        loginField.setText(loginValue);
        loginField.setEnabled(componentsEnabled);

        passwordField.setText(passwordValue);
        passwordField.setEnabled(componentsEnabled);

        removeButton.setEnabled(componentsEnabled);
    }


    /**
     * Updates the value of the item that was being editing. Should be called whenever the list selection has changed.
     */
    private void modifyCredentials() {
        // Make sure that the item still exists (could have been removed) before trying to modify its value
        int itemIndex = credentials.indexOf(lastSelectedItem);
        if(lastSelectedItem!=null && itemIndex!=-1) {
            credentials.setElementAt(new CredentialsMapping(new Credentials(loginField.getText(), new String(passwordField.getPassword())), lastSelectedItem.getRealm(), true), itemIndex);
        }
        
        this.lastSelectedItem = (CredentialsMapping) credentialsList.getSelectedValue();
    }


    /**
     * Overrides dispose() to write credentials if needed (if at least one item has been changed).
     */
    @Override
    public void dispose() {
        super.dispose();

        // Write credentials file to disk, only if changes were made
        try {CredentialsManager.writeCredentials(false);}
        // We should probably pop an error dialog here...
        catch(Exception e) {}
    }


    ///////////////////////////
    // ActionListener method //
    ///////////////////////////

    public void actionPerformed(ActionEvent e) {
        Object source = e.getSource();

        // Dispose the dialog (credentials save is performed in dispose())
        if (source== closeButton)  {
            // Commit current credentials modifications.
            // Note: if the dialog is cancelled, current modifications will be cancelled (i.e. not committed) 
            modifyCredentials();
            
            dispose();
        }
        else if(source==goToButton) {
            // Dispose dialog first
            dispose();
            // Go to credentials' realm location
            mainFrame.getActivePanel().tryChangeCurrentFolder(((CredentialsMapping)credentialsList.getSelectedValue()).getRealm());
        }
    }


    ///////////////////////////////////
    // ListSelectionListener methods //
    ///////////////////////////////////

    public void valueChanged(ListSelectionEvent e) {
        LOGGER.trace("called, e.getValueIsAdjusting="+e.getValueIsAdjusting()+" getSelectedIndex="+ credentialsList.getSelectedIndex());

        if(e.getValueIsAdjusting())
            return;

        // Commit current credentials modifications
        modifyCredentials();

        // Update components to reflect the new selection
        updateComponents();
    }

}
