/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.gui.widgets;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.mail.MessagingException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JProgressBar;
import javax.swing.border.TitledBorder;

import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.commons.codec.binary.Base64;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.validation.util.DefaultValidationResultModel;


/**
 * a Dialog to change the password. the password is written back to the
 * passwordhash-object encoded with base64 and MD5
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @Version
 * <br>$Log: PasswordChangeDialog.java,v $
 * <br>Revision 1.9  2009-05-07 08:26:45  hermen
 * <br>handle password changing without restarting
 * <br>
 * <br>Revision 1.8  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.7  2008-07-07 12:07:57  heinemann
 * <br>complete - # 156: Add feedback to password dialog for superman mode
 * <br>http://santec.tudor.lu/trac/gecamed/ticket/156
 * <br>
 * <br>Revision 1.6  2008-04-14 15:29:10  mack
 * <br>Added getUnencodedPassword method
 * <br>
 * <br>Revision 1.5  2008-01-15 09:29:38  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 * <br>Revision 1.4  2008-01-07 13:02:50  hermen
 * <br>replaces mail.jar for base64 encoding for License reasons
 * <br>
 * <br>Revision 1.3  2007-12-06 14:46:44  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 *
 */
public class PasswordChangeDialog extends JDialog implements ActionListener, KeyListener
{
    //~ Static fields/initializers =============================================

    private static final long serialVersionUID = 1L;
    /**
     * the minimum length of a password
     */
    private static final int PW_LENGTH = 5;

    //~ Instance fields ========================================================

    private JButton cancelButton;
    private JButton okButton;
    private JProgressBar statusLabel;
    private JPasswordField pw1;
    private JPasswordField pw2;
    private PanelBuilder builder;
    private String passHash;
	private Color fgColor;
	private DefaultValidationResultModel validationResultModel;

    //~ Constructors ===========================================================
    /**
     * Creates a new PasswordChangeDialog.
     *
     * @param mainFrame a reference to the mainFrame of the Program to obtain all child-objects from
     * @param passHash the Password-Object, where the new pass is written to
     *
     * @throws HeadlessException
     */
    public PasswordChangeDialog(MainFrame mainFrame)
        throws HeadlessException
    {
        super(mainFrame);
        this.setModal(true);

        // create components
        this.setTitle(Translatrix.getTranslationString("core.changePassword")); 
        this.pw1 = new JPasswordField();
        this.pw1.addKeyListener(this);
        this.pw2 = new JPasswordField();
        this.pw2.addKeyListener(this);
        this.pw2.addActionListener(this);
        this.cancelButton = new JButton(Translatrix.getTranslationString("core.cancel")); 
        this.cancelButton.addActionListener(this);
        this.okButton = new JButton(Translatrix.getTranslationString("core.ok")); 
        this.okButton.addActionListener(this);
        this.statusLabel = new JProgressBar();
        this.fgColor = this.statusLabel.getForeground();
        this.statusLabel.setMaximum(PW_LENGTH);

        // create Layout
        FormLayout layout = new FormLayout("25dlu:grow, 2dlu, pref:grow, 2dlu, 75dlu:grow", 
                "pref, 2dlu, pref, 2dlu, pref, 2dlu, pref"); 
        builder = new PanelBuilder(layout);
        builder.setBorder(new TitledBorder(Translatrix.getTranslationString("core.changePassword"))); 
        CellConstraints cc = new CellConstraints();

        // add elements to Layout
        builder.add(new JLabel(GECAMedModule.getIcon(GECAMedIconNames.PASSWORD)),            cc.xywh(1, 1, 1, 3));
        builder.add(new JLabel(Translatrix.getTranslationString("core.newPassword")), cc.xyw(3, 1, 1)); 
        builder.add(this.pw1, cc.xyw(5, 1, 1));
        builder.add(new JLabel(Translatrix.getTranslationString("core.verify")), cc.xyw(3, 3, 1)); 
        builder.add(this.pw2, cc.xyw(5, 3, 1));
        builder.add(this.cancelButton, cc.xyw(1, 5, 3));
        builder.add(this.okButton, cc.xyw(5, 5, 1));
        builder.add(this.statusLabel, cc.xyw(1, 7, 5));
        
        
        // show the Dialog
        this.getContentPane().add(builder.getPanel());
        this.pack();
    }

    //~ Methods ================================================================

    /* (non-Javadoc)
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource().equals(this.cancelButton)) {
            // on cancel --> dispose
            this.setStatus("", fgColor);
            this.dispose();
        } else if ((e.getSource().equals(this.okButton)) || (e.getSource().equals(this.pw2))){
            // on ok --> if passwords match --> save
            String pwStr1 = new String(pw1.getPassword());
            String pwStr2 = new String(pw2.getPassword());
            if (pwStr1.equals(pwStr2)) {
                if (pwStr1.length() >= PW_LENGTH) {
                    try {
                        this.passHash = PasswordChangeDialog.getEncodedHash(pwStr1);
                        this.setStatus(Translatrix.getTranslationString("core.passwordChanged"), Color.GREEN);
                        this.setStatus("", fgColor);
                        this.dispose();
                    } catch (Exception e1) {
                        this.setStatus(e1.getMessage());
                    }
                } else {
                    this.setStatus(Translatrix.getTranslationString("core.newPasswordToShort")); 
                }
            } else {
                this.setStatus(Translatrix.getTranslationString("core.passwordsDontMatch")); 
            }
        }
    }

    /**
     * just for testing.....
     *
     * @param args
     */
    public static void main(String[] args)
    {
        new PasswordChangeDialog(null); 
    }

    /**
     * returns the clearText String encoded with base64 and MD5
     *
     * @param clearText the String to be encoded
     *
     * @return the encoded String
     *
     * @throws MessagingException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    public static String getEncodedHash(String clearText)
        throws NoSuchAlgorithmException, IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
//        OutputStream out = MimeUtility.encode(baos, "base64"); 
        MessageDigest md = MessageDigest.getInstance("MD5"); 
        if (clearText == null) {
            clearText = ""; 
        }
        byte[] in = clearText.getBytes();
        byte[] digested = md.digest(in);
        baos.write(digested);
        baos.close();
        
        byte[] bArr = Base64.encodeBase64(baos.toByteArray());
        
        return new String(bArr, "ISO-8859-1"); 
    }

    /**
     * shows the string in the statusfield in RED
     *
     * @param str the string to be shown
     */
    private void setStatus(String str)
    {
        this.setStatus(str, Color.RED);
    }

    /**
     * shows the str in the statusfield ind color c
     *
     * @param str the string to be shown
     * @param c the color of the string
     */
    private void setStatus(String str, Color c)
    {
        this.statusLabel.setForeground(c);
        this.statusLabel.setStringPainted(true);
        this.statusLabel.setString(str);
    }

    /**
     * shows the Dialog centered on the Screen.
     */
    public String getPassword()
    {
    	this.setStatus("", fgColor);
    	this.pw1.setText("");
    	this.pw2.setText("");
    	this.statusLabel.setMaximum(PW_LENGTH);
    	this.statusLabel.setValue(0);
    	this.passHash = null;
        Dimension f = this.getSize();
    	Point d = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint(); 
		this.setLocation(d.x - (f.width/ 2), d.y - (f.height / 2));
		this.pw1.requestFocus();
        MainFrame.showDialogCentered(this);
        return this.passHash;
    }

    public String getUnencodedPassword()
    {
    if (this.passHash != null) 
    	return String.valueOf(this.pw1.getPassword());		
    else return null;
    }
    
	public void keyTyped(KeyEvent e) {
	}
	public void keyPressed(KeyEvent e) {
	}
	public void keyReleased(KeyEvent e) {
	    	this.setStatus("", fgColor);
		if (e.getSource().equals(pw1)) {
			statusLabel.setValue(pw1.getPassword().length);
			
		} else if (e.getSource().equals(pw2)) {
			this.statusLabel.setMaximum(pw1.getPassword().length);
			statusLabel.setValue(pw2.getPassword().length);
		} 
	}
}
