package ca.ucalgary.seahawk.gui;

import ca.ucalgary.seahawk.util.*;

import org.biomoby.registry.meta.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * A dialog box that allows the Seahawk user to configure
 * options such as cache expiry and location, as well as
 * the MOBY registry to use, and whether referrer headers
 * should be sent to services.  Settings are saved to a file
 * by the SeahawkOptions class.
 */
public class SeahawkOptionsGUI extends JDialog implements ActionListener, WindowListener{
    public static final String CUSTOM_REGISTRY_SYNONYM = "Custom (set your own location & namespace)";
    public static final String CUSTOM_REGISTRY_DESC = "User-defined MOBY Central registry.";
    public static final String DIALOG_TITLE = "Seahawk User Preferences";
    public static final boolean DIALOG_MODAL = true;

    private static final int MAX_DESC_WIDTH = 60;

    private JComboBox registryComboBox;
    private JTextPane registryDescTextArea;
    private JTextField registryEndpoint;
    private JTextField registryNamespace;

    private JButton deleteCacheButton;
    private JButton selectCacheDirButton;
    private JTextField converterHostTextField;
    private JTextField converterPortTextField;
    private JTextField cacheExpiryTextField;
    private JFileChooser cacheDirFileChooser;

    private JCheckBox sendReferrerCheckBox;

    private JButton resetButton;
    private JButton okButton;
    private JButton cancelButton;

    private RegistriesList registriesList;
    private String[] registryNames;
    private String lastCustomEndpoint = "http://yourdomain.org/cgi-bin/MOBY-Central.pl";
    private String lastCustomNamespace = "http://yourdomain.org/MOBY/Central";

    /**
     * @param owner the frame that launches this dialog
     */
    public SeahawkOptionsGUI(Frame owner){
        super(owner, DIALOG_TITLE, DIALOG_MODAL);

        // Custom handling of window close operation
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(this);

        // The dreaded GridBagLayout...
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        setLayout(gridbag);
	
        registriesList = new RegistriesList();
        Registry[] registries = registriesList.getAll();
        registryNames = new String[registries.length+1];
        int i = 0;
        for(; i < registries.length; i++){
            registryNames[i] = registries[i].getSynonym();
        }
        registryNames[i] = CUSTOM_REGISTRY_SYNONYM;

        c.insets = new Insets(3,2,3,2);
        //........................................
        
        c.gridwidth = 2;
        c.fill = GridBagConstraints.HORIZONTAL;
        JLabel label = new JLabel("Registry server:", JLabel.RIGHT);
        add(label, c);
        
        registryComboBox = new JComboBox(registryNames);
        registryComboBox.addActionListener(this);
        add(registryComboBox, c);

        c.gridheight = 4;
        c.fill = GridBagConstraints.NONE;
        JPanel bPanel = new JPanel(new GridLayout(3, 0, 3, 3));
          resetButton = new JButton("Restore program defaults");
	  resetButton.addActionListener(this);
        
          cancelButton = new JButton("Cancel");
	  cancelButton.addActionListener(this);

          okButton = new JButton("Apply changes");
          okButton.addActionListener(this);

          bPanel.add(resetButton);
          bPanel.add(cancelButton);
          bPanel.add(okButton);
        
        add(bPanel, c);

        //........................................
        c.gridx = 0;
        c.gridy = 1;
        c.gridheight = 1;
        c.fill = GridBagConstraints.BOTH;
        label = new JLabel("Description:", JLabel.RIGHT);
        label.setVerticalAlignment(JLabel.TOP);
        add(label, c);
        
        c.gridx = 2;
        c.weightx = 1.0;
        c.weighty = 1.0;
        registryDescTextArea = new JTextPane();
        registryDescTextArea.setEditable(false);
        add(registryDescTextArea, c);
        
        //........................................
        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 0.0;
        c.weighty = 0.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        label = new JLabel("Location:", JLabel.RIGHT);
        add(label, c);

        c.gridx = 2;
        registryEndpoint = new JTextField(50);
        registryEndpoint.setEditable(false);
        add(registryEndpoint, c);

        //........................................
        
        c.gridx = 0;
        c.gridy = 3;
        label = new JLabel("Namespace:", JLabel.RIGHT);
        add(label, c);

        c.gridx = 2;
        registryNamespace = new JTextField(50);
        registryNamespace.setEditable(false);
        add(registryNamespace, c);

        //........................................
	c.gridx = 0;
        c.gridy = 4;
        c.gridwidth = 5;
        JSeparator line = new JSeparator();
        add(line, c);

        //........................................
	c.gridx = 0;
        c.gridy = 5;
        c.gridwidth = 2;
        label = new JLabel("OpenOffice Service Host & Port:", JLabel.RIGHT);
	label.setToolTipText(HTMLUtils.htmlifyToolTipText("Used to convert Word " +
				       "documents and other formats for display " +
				       "in Seahawk.  If left blank, no conversions " +
				       "will be performed, restricting data import.", 50));
        add(label, c);

        c.gridx = 2;
        c.gridwidth = 1;
        converterHostTextField = new JTextField(12);
	converterHostTextField.setMinimumSize(converterHostTextField.getPreferredSize()); // prevents text collapsing
        add(converterHostTextField, c);	
        c.gridx = 3;
        c.gridwidth = 1;
        c.fill = GridBagConstraints.NONE;	
	c.anchor = GridBagConstraints.WEST;
        converterPortTextField = new JTextField(5);
        converterPortTextField.setMinimumSize(converterPortTextField.getPreferredSize()); // prevents text collapsing
        add(converterPortTextField, c);	
	c.fill = GridBagConstraints.HORIZONTAL;

        //........................................
        c.gridx = 0;
        c.gridy = 6;
        c.gridwidth = 5;
        line = new JSeparator();
        add(line, c);

        //........................................
        c.gridx = 0;
        c.gridy = 7;
        c.gridwidth = 4;
        c.gridheight = 1;
        sendReferrerCheckBox = new JCheckBox("Send referrer data to services");
        add(sendReferrerCheckBox, c);

        //........................................
        c.gridy = 8;
        c.gridwidth = 5;
        line = new JSeparator();
        add(line, c);

        //........................................
        c.gridy = 9;
        c.gridwidth = 2;
        label = new JLabel("Cache expiry (in hours)", JLabel.RIGHT);
        add(label, c);

        c.gridx = 2;
        c.gridwidth = 1;
	c.fill = GridBagConstraints.NONE;
        cacheExpiryTextField = new JTextField(3);
        cacheExpiryTextField.setMinimumSize(cacheExpiryTextField.getPreferredSize()); // prevents text collapsing
        add(cacheExpiryTextField, c);

        c.gridx = 3;
        c.gridwidth = 2;
        c.fill = GridBagConstraints.NONE;
        JPanel cbPanel = new JPanel(new GridLayout(1,0,4,2));

          deleteCacheButton = new JButton("Delete cached items");
          deleteCacheButton.addActionListener(this);
        
          selectCacheDirButton = new JButton("Change cache directory");
          selectCacheDirButton.addActionListener(this);

	  cbPanel.add(deleteCacheButton);
	  cbPanel.add(selectCacheDirButton);
        
	  cacheDirFileChooser = new JFileChooser();
	  cacheDirFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

	add(cbPanel, c);
        
	setCurrentValues();
	pack();
    }

    public void actionPerformed(ActionEvent event){
	Object source = event.getSource();
	if(source == cancelButton){
	    setCurrentValues();
	    setVisible(false);
	}
	else if(source == okButton){
	    saveSettings();
	}
	else if(source == resetButton){
	    setDefaultValues();
	}
	else if(source == selectCacheDirButton){
	    int choice = cacheDirFileChooser.showOpenDialog(this);
	    if (choice != JFileChooser.APPROVE_OPTION) {
		cacheDirFileChooser.setSelectedFile(SeahawkOptions.getTempDir());
	    }
	}
	else if(source == deleteCacheButton){
	    RegistryCache.deleteAllCacheFiles();
	    // Can we also delete the in-memory cache somehow?
	}
	else if(source == registryComboBox){
	    updateRegistryFields();
	}
    }
    
    /**
     * Use the values defined by SeahawkOptions.
     */
    private void setCurrentValues(){
	Registry currentRegistry = SeahawkOptions.getRegistry();
	if(currentRegistry == null){
	    registryComboBox.setSelectedItem(RegistriesList.DEFAULT_REGISTRY_SYNONYM);
	}
	else if(currentRegistry.getSynonym().equals(SeahawkOptions.CUSTOM_REGISTRY_SYNONYM)){
	    registryComboBox.setSelectedItem(CUSTOM_REGISTRY_SYNONYM);  // a bit more verbose than SO.CRS
	    lastCustomEndpoint = currentRegistry.getEndpoint();
	    lastCustomNamespace = currentRegistry.getNamespace();
	}
	else{
	    registryComboBox.setSelectedItem(currentRegistry.getSynonym());
	}
	updateRegistryFields(); 
	converterHostTextField.setText(""+SeahawkOptions.getDocConverterHost());
	converterPortTextField.setText(""+SeahawkOptions.getDocConverterPort());
	sendReferrerCheckBox.setSelected(SeahawkOptions.getSendReferrerPolicy());
	cacheExpiryTextField.setText(""+SeahawkOptions.getCacheExpiry());
	cacheDirFileChooser.setSelectedFile(SeahawkOptions.getTempDir());
    }

    private void saveSettings(){
	double expiry = SeahawkOptions.getCacheExpiry();
	try{
	    expiry = Double.parseDouble(cacheExpiryTextField.getText());
	} catch(Exception e){
	    JOptionPane.showMessageDialog(null, 
					  "The cache expiry field is not a number,\n"+
					  "please correct it to continue", 
					  "Invalid Number Formatting", 
					  JOptionPane.ERROR_MESSAGE);
	    return;
	}

	int port = SeahawkOptions.getDocConverterPort();
	try{
	    port = Integer.parseInt(converterPortTextField.getText());
	    SeahawkOptions.setDocConverterPort(port);
	} catch(Exception e){
	    JOptionPane.showMessageDialog(null, 
					  "The converter port field is not an positive integer,\n"+
					  "please correct it to continue", 
					  "Invalid Integer Formatting", 
					  JOptionPane.ERROR_MESSAGE);
	    return;
	}

	Registry previousRegistry = SeahawkOptions.getRegistry();
	String selectedRegistryName = registryComboBox.getSelectedItem().toString();
	if(selectedRegistryName.equals(CUSTOM_REGISTRY_SYNONYM)){
	    SeahawkOptions.setRegistry(new Registry(SeahawkOptions.CUSTOM_REGISTRY_SYNONYM,
						    registryEndpoint.getText(),
						    registryNamespace.getText()));
	}
	else{
	    try{
		SeahawkOptions.setRegistry(registriesList.get(selectedRegistryName));
	    } catch(Exception e){
		e.printStackTrace();
		JOptionPane.showMessageDialog(null, 
					      "The registry information could not be saved properly,\n" +
					      "see the console for more details.  Aborting save.", 
					      "Registry Specification Error", 
					      JOptionPane.ERROR_MESSAGE);
		return;
	    }
	}

	// Update SeahawkOptions with the values from the form
	try{
	    SeahawkOptions.setTempDir(cacheDirFileChooser.getSelectedFile());
	} catch(Exception e){
	    e.printStackTrace();
	    JOptionPane.showMessageDialog(null, 
					  "Please select another directory for the cache.\n"+
					  "The specified cache directory ("+
					  cacheDirFileChooser.getSelectedFile()+
					  ") is not suitable:\n"+
					  e.getMessage(), 
					  "Cache Specification Error", 
					  JOptionPane.ERROR_MESSAGE);
	    SeahawkOptions.setRegistry(previousRegistry); // Roll back the change
	    return;
	}

	SeahawkOptions.setDocConverterHost(converterHostTextField.getText().trim());
	SeahawkOptions.setSendReferrerPolicy(sendReferrerCheckBox.isSelected());
	SeahawkOptions.setCacheExpiry(expiry);

	// Saves the new values to a file, so they will perpetuate themselves between sessions
	if(!SeahawkOptions.saveSettings()){
	    JOptionPane.showMessageDialog(null, 
					  "Sorry, but there was an error saving your preferences.\n"+
					  "Please see the console for more details.", 
					  "Error Saving Preferences", 
					  JOptionPane.ERROR_MESSAGE);
	}
	setVisible(false);
    }

    private void setDefaultValues(){
	// Restore the program defaults in subsequent JVM's by deleting the preferences file! 
	java.io.File defaultsFile = SeahawkOptions.getDefaultsFile();
	if(defaultsFile.exists()){
	    defaultsFile.delete();
	}

	// We also need to reset the current session...TODO
	JOptionPane.showMessageDialog(null, 
				      "Please restart Seahawk to continue with the default settings.", 
				      "Custom Settings Deleted", 
				      JOptionPane.INFORMATION_MESSAGE);
	setVisible(false);
    }

    private void updateRegistryFields(){
	// Update all the registry fields if the name switches
	String selectedRegistryName = registryComboBox.getSelectedItem().toString();
	if(selectedRegistryName.equals(CUSTOM_REGISTRY_SYNONYM)){
	    registryDescTextArea.setText(CUSTOM_REGISTRY_DESC);
	    registryEndpoint.setEditable(true);
	    registryEndpoint.setText(lastCustomEndpoint);
	    registryNamespace.setEditable(true);
	    registryNamespace.setText(lastCustomNamespace);
	    pack();
	    return;
	}
	if(registryDescTextArea.getText().equals(CUSTOM_REGISTRY_DESC)){
	    lastCustomEndpoint = registryEndpoint.getText();
	    lastCustomNamespace = registryNamespace.getText();
	}
	
	try{
	    Registry selectedRegistry = registriesList.get(registryComboBox.getSelectedItem().toString());
	    String desc = selectedRegistry.getDescription();
	    String newDesc = "";
	    if(desc != null){
		int charCount = 0;
		int curLine = 0;
		for(String word: desc.split(" ")){
		    charCount += word.length() + 1;
		    if(charCount/MAX_DESC_WIDTH > curLine){
			curLine++;
			charCount--;
			newDesc += "\n";
		    }
		    newDesc += word + " ";
		}
	    }		
	    registryDescTextArea.setText(newDesc);
	    registryEndpoint.setText(selectedRegistry.getEndpoint());
	    registryEndpoint.setEditable(false);
	    registryNamespace.setText(selectedRegistry.getNamespace());
	    registryNamespace.setEditable(false);
	} catch(Exception e){
	    e.printStackTrace();
	    return;
	}

	pack();
    }


    public void windowActivated(WindowEvent e){}
    public void windowClosed(WindowEvent e){}
    /**
     * Invoked when the user attempts to close the window from the window's system menu.
     * Implemented as resetting the fields to their original values, and closing the
     * window (same effect as the "Cancel" button).
     */
    public void windowClosing(WindowEvent e){
	setCurrentValues();
	setVisible(false);
    }
    public void windowDeactivated(WindowEvent e){}
    public void windowDeiconified(WindowEvent e){}
    public void windowIconified(WindowEvent e){}
    public void windowOpened(WindowEvent e){}
    
    public static void main(String[] args)
    {
      JFrame frame = new JFrame("test");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      
      SeahawkOptionsGUI dialog = new SeahawkOptionsGUI(frame);
      
      frame.pack();
      frame.setVisible(true);
      
      dialog.setVisible(true);
    }
}
