/*
 * -----------------------------------------------------------------------------
 *
 * <p><b>License and Copyright: </b>The contents of this file are subject
 * to the Educational Community License (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy
 * of the License at <a href="http://www.opensource.org/licenses/ecl1.txt">
 * http://www.opensource.org/licenses/ecl1.txt.</a></p>
 *
 * <p>Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.</p>
 *
 * <p>The entire file consists of original code.  Copyright &copy;
 * 2002-2007 by The Rector and Visitors of the University of Virginia and
 * Cornell University.  All rights reserved.</p>
 *
 * -----------------------------------------------------------------------------
 */


/* Based on Fedora's Client LoginDialog.java code */
/* Modified to work with Greenstone: 22 Jan 2008 */

package org.greenstone.gatherer.gui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.io.File;
import java.io.IOException;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import java.awt.Rectangle;
import java.awt.Toolkit;
import org.greenstone.gatherer.Dictionary;



import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.Configuration;

public class FedoraLogin extends JDialog 
{
    private static final int MAX_ITEMS = 5;

    private static final java.awt.Color LIGHT = new java.awt.Color(244, 244, 224);
    private static final java.awt.Color TRANSPARENT = new java.awt.Color(0,0,0,0);

    private JPanel m_infoPane;
    private JComboBox m_serverComboBox;
    private JComboBox m_protocolComboBox;
    private JComboBox m_usernameComboBox;
    private JPasswordField m_passwordField;

    private String m_lastUsername="fedoraAdmin";
    private String m_lastServer="localhost:8080";
    private String m_lastProtocol="http";
    private String m_lastPassword="";

    private boolean login_requested = false;

    public FedoraLogin(String title, boolean can_cancel) 
    {
	super(Gatherer.g_man, "Login", true);
        this.setComponentOrientation(Dictionary.getOrientation());
	this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        
	JLabel serverLabel=new JLabel("Fedora Server");        
	JLabel protocolLabel=new JLabel("Protocol");
        JLabel usernameLabel=new JLabel("Username");
        JLabel passwordLabel=new JLabel("Password");

        serverLabel.setComponentOrientation(Dictionary.getOrientation());
        protocolLabel.setComponentOrientation(Dictionary.getOrientation());
        usernameLabel.setComponentOrientation(Dictionary.getOrientation());
        passwordLabel.setComponentOrientation(Dictionary.getOrientation());
        
        m_serverComboBox=new JComboBox(new String[]{m_lastServer});
        m_serverComboBox.setComponentOrientation(Dictionary.getOrientation());
        m_serverComboBox.setEditable(true);
	m_protocolComboBox=new JComboBox(new String[]{m_lastProtocol, "https"}); // http and https
        m_protocolComboBox.setComponentOrientation(Dictionary.getOrientation());        
	m_protocolComboBox.setEditable(true);
        m_usernameComboBox=new JComboBox(new String[]{m_lastUsername});
        m_usernameComboBox.setComponentOrientation(Dictionary.getOrientation());
        m_usernameComboBox.setEditable(true);
        m_passwordField=new JPasswordField();

        setComboBoxValues();

	
        LoginAction loginAction=new LoginAction(this);
        JButton loginButton=new JButton(loginAction);

        loginAction.setButton(loginButton);
        loginButton.setEnabled(false);
	this.getRootPane().setDefaultButton(loginButton);

        m_passwordField.getDocument().addDocumentListener(
                new PasswordChangeListener(loginButton, m_passwordField));
        m_passwordField.setAction(loginAction);

        JPanel inputPane=new JPanel();
        inputPane.setComponentOrientation(Dictionary.getOrientation());
        inputPane.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createCompoundBorder(
                    BorderFactory.createEmptyBorder(6, 6, 6, 6),
                    BorderFactory.createEtchedBorder()
                ),
                BorderFactory.createEmptyBorder(6,6,6,6)
                ));
        GridBagLayout gridBag=new GridBagLayout();
        inputPane.setLayout(gridBag);
        addLabelValueRows(new JLabel[] {serverLabel, protocolLabel, usernameLabel, passwordLabel}, 
                new JComponent[] {m_serverComboBox, m_protocolComboBox, m_usernameComboBox, m_passwordField}, 
                gridBag, inputPane);
	
	// handling Closing and cancelling events
	this.addWindowListener(new WindowAdapter() {
		public void windowClosing(WindowEvent e){
		    // this is important, if we want to stop looping on displaying the dialog:
		    login_requested = false; 
		    //dispose(); // defaultCloseOperation of this dialog already set to dispose it
		}
	    });

        JButton cancelButton=new JButton(new AbstractAction() {
        	private static final long serialVersionUID = 1L;
            public void actionPerformed(ActionEvent evt) {
		// this is important, if we want to stop looping on displaying the dialog:
		login_requested = false;
                dispose();
            }
        });

	cancelButton.setText("Exit"); // if haven't logged in yet

        JPanel buttonPane=new JPanel();
        buttonPane.setComponentOrientation(Dictionary.getOrientation());
        buttonPane.add(loginButton);
        buttonPane.add(cancelButton);
        Container contentPane=getContentPane();
        contentPane.setComponentOrientation(Dictionary.getOrientation());

        contentPane.setLayout(new BorderLayout());
	m_infoPane = new JPanel();
        m_infoPane.setComponentOrientation(Dictionary.getOrientation());
	m_infoPane.setBackground(TRANSPARENT);
	contentPane.add(m_infoPane, BorderLayout.NORTH);
        contentPane.add(inputPane, BorderLayout.CENTER);
        contentPane.add(buttonPane, BorderLayout.SOUTH);
        addWindowListener(new WindowAdapter() {
            public void windowOpened(WindowEvent evt) {
                m_passwordField.requestFocus();
            }
        });
        pack();

	// Position
	Dimension size = getSize();
	if (Gatherer.g_man != null) {
	    Rectangle frame_bounds = Gatherer.g_man.getBounds();
	    setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
	}
	else {
	    Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
	    setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
	}


        setVisible(true);
    }


    public void saveProperties(JComboBox control, String affectedProperty, String editedValue) {
	//String editedValue = (String)control.getSelectedItem();
	// Edited value is the value in the combobox editfield, it has not yet been added to the combobox
	control.insertItemAt(editedValue, 0); 
	
	if(editedValue == null) {
	    editedValue = (String)control.getItemAt(0); // else reuse the first default
	} 
	Configuration.setString(affectedProperty, true, editedValue);
	
	// Add the values in the combobox as well.
	int value_count = 1;
	for(int i = 0; value_count < MAX_ITEMS && i < control.getItemCount(); i++, value_count++) {
	    String value = (String)control.getItemAt(i);
	    if(value == null || value.equals(editedValue) || value.equals("")) { 
		// skip duplicates of just-edited comboBox value and empty values
		value_count--;
	    } else { 
		Configuration.setString(affectedProperty+value_count, true, value);
	    } // else retain old value for this affectedProperty (e.g. general.gliserver_url)
	}
    }

    private void setComboBoxValues() {
        // All comboboxes are of the same size
        Dimension newSize=new Dimension(m_serverComboBox.getPreferredSize().width+20, m_serverComboBox.getPreferredSize().height);
	m_passwordField.setPreferredSize(newSize);
      
	setComboBoxValues(m_serverComboBox, "fedora.server", newSize);
	setComboBoxValues(m_protocolComboBox, "fedora.protocol", newSize);
	setComboBoxValues(m_usernameComboBox, "fedora.username", newSize);
	newSize = null;
    }
    
    private void setComboBoxValues(JComboBox control, String affectedProperty, Dimension newSize) {
	// get values from Configuration file, if none, it will go back to using defaultvalues
	// put them into the combobox
	
	String value = Configuration.getString(affectedProperty, true);
	boolean finished = value.equals("");
	if(!finished) {
	    control.removeAllItems(); // remove defaults, since config file now contains init values
	    control.addItem(value);
	} // else value and combobox already contains the defaults
	   	    
	
	for(int i = 1; !finished; i++) {
	    value = Configuration.getString(affectedProperty+i, true);
	    if(value.equals("")) {
		finished = true;
	    }
	    else { // value is not empty
		control.addItem(value);
	    }
	}
	
	// setting the size of the dropdown control
	control.setPreferredSize(newSize);
    }

    public void addLabelValueRows(JLabel[] labels, JComponent[] values,
				  GridBagLayout gridBag, Container container) {
        GridBagConstraints c=new GridBagConstraints();
        c.insets=new Insets(0, 6, 6, 6);
        for (int i=0; i<labels.length; i++) {
            c.anchor=GridBagConstraints.EAST;
            c.gridwidth=GridBagConstraints.RELATIVE; //next-to-last
            c.fill=GridBagConstraints.NONE;      //reset to default
            c.weightx=0.0;                       //reset to default
            gridBag.setConstraints(labels[i], c);
            container.add(labels[i]);
	    
            c.gridwidth=GridBagConstraints.REMAINDER;     //end row
            if (!(values[i] instanceof JComboBox)) {
                c.fill=GridBagConstraints.HORIZONTAL;
            } else {
                c.anchor=GridBagConstraints.WEST;
            }
            c.weightx=1.0;
            gridBag.setConstraints(values[i], c);
            container.add(values[i]);
        }
	
    }

    /** Displays the given errormessage (which is split over several lines)
	* in the FedoraLogin itself 
	* @param errorLines: the lines of the error message, where each line
	* will be presented in its own JLabel in the infoPane.
     */
    public void setErrorMessage(String[] errorLines) {
	m_infoPane.removeAll();
	m_infoPane.setBackground(LIGHT);
	// n rows and 1 column
	m_infoPane.setLayout(new java.awt.GridLayout(errorLines.length, 1));
	for(int i = 0; i < errorLines.length; i++) {
	    JLabel line = new JLabel("  " + errorLines[i] + "  ");
	    m_infoPane.add(line);
	}
	
	// Adjust and resize this dialog to take into account the 
	// recently added components (labels)
	m_infoPane.validate();	
	this.pack();
    }

    public class PasswordChangeListener
            implements DocumentListener {

        private JButton m_loginButton;
        private JPasswordField m_passField;

        public PasswordChangeListener(JButton loginButton, JPasswordField pf) {
            m_loginButton=loginButton;
            m_passField=pf;
        }

        public void changedUpdate(DocumentEvent e) {
            dataChanged();
        }

        public void insertUpdate(DocumentEvent e) {
            dataChanged();
        }

        public void removeUpdate(DocumentEvent e) {
            dataChanged();
        }
	
        public void dataChanged() {
	    if (m_passField.getPassword().length == 0) { 
                m_loginButton.setEnabled(false);
            } else {
                m_loginButton.setEnabled(true);
            }
        }

    }
    
    
      public class LoginAction
	  extends AbstractAction {

	  FedoraLogin m_loginDialog;
	  JButton m_button;
	  
	  public LoginAction(FedoraLogin loginDialog) {
	      super("Login");
	      m_loginDialog=loginDialog;
	  }
	  
	  public void setButton(JButton button) {
            m_button=button;
	  }
	  
	  public void actionPerformed(ActionEvent evt) {
	     
	    
            if (m_button.isEnabled()) {

                try {
                    // pull out values and do a quick syntax check
                    String hostPort=(String) m_serverComboBox.getSelectedItem();
                    int colonPos=hostPort.indexOf(":");
                    if (colonPos==-1) {
                        throw new IOException("Server must be specified as host:port");
                    }
                    String[] s=hostPort.split(":");
                    String host=s[0];
                    if (host.length()==0) {
                        throw new IOException("No server name provided.");
                    }
                    int port=0;
                    try {
                        port=Integer.parseInt(s[1]);
                    } catch (NumberFormatException nfe) {
                        throw new IOException("Server port must be an integer.");
                    }
		    String protocol=(String) m_protocolComboBox.getSelectedItem();
		    if (protocol.equals("")) {
			throw new IOException("No protocol provided.");
		    }

                    String username=(String) m_usernameComboBox.getSelectedItem();
                    if (username.equals("")) {
                        throw new IOException("No username provided.");
                    }
                    String pass = new String(m_passwordField.getPassword());


                    // all looks ok...just save stuff and exit now
		    m_lastServer=host + ":" + port;
                    m_lastProtocol=protocol;
                    m_lastUsername=username;
		    m_lastPassword=pass;

		    m_loginDialog.saveProperties(m_serverComboBox, "fedora.server", m_lastServer);
		    m_loginDialog.saveProperties(m_protocolComboBox, "fedora.protocol", m_lastProtocol);
		    m_loginDialog.saveProperties(m_usernameComboBox, "fedora.username", m_lastUsername);
		   
		    // now save these values to the Configuration file
		    Configuration.save();                   

		    login_requested = true;

		    // hiding it instead of disposing it on OK-press allows us to loop
		    // on displaying the dialog in case there's more checking to do
		    m_loginDialog.setVisible(false);

                } catch (Exception e) {
                    //String msg = e.getMessage();
		    //System.err.println(msg);

		    e.printStackTrace();

                }
            }
	  }    
      }

    public boolean loginRequested()
    {
	return login_requested;
    }

    public String getLibraryURL()
    {
	String libraryUrl = m_lastProtocol + "://" + m_lastServer + "/fedora/search";

	return libraryUrl;
    }

    public String getHostname()
    {
	String[] s=m_lastServer.split(":");
	String host=s[0];
	return host;
    }
    public String getPort()
    {
	String[] s=m_lastServer.split(":");

	String port = s[1];
	return port;
    }

    public String getUsername()
    {
	return m_lastUsername;
    }

    public String getPassword()
    {
	return m_lastPassword;
    }

    public String getProtocol()
    {
	return m_lastProtocol;
    }


}
