// RegistrationNamespaceSubPanel.java
//
// Created: October 2005
//
// This file is a component of the BioMoby project.
// Copyright Martin Senger (martin.senger@gmail.com).
//

package org.biomoby.service.dashboard;

import org.biomoby.shared.MobyException;
import org.biomoby.shared.MobyNamespace;

import org.tulsoft.shared.UUtils;
import org.tulsoft.tools.gui.SwingUtils;
import org.tulsoft.tools.gui.JTextFieldWithHistory;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.tree.DefaultMutableTreeNode;

import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

/**
 * A panel allowing to register a namespace in a Biomoby registry. <p>
 *
 * @author <A HREF="mailto:martin.senger@gmail.com">Martin Senger</A>
 * @version $Id: RegistrationNamespaceSubPanel.java,v 1.11 2009/02/06 09:17:14 groscurt Exp $
 */

public class RegistrationNamespaceSubPanel
    extends RegistrationPanel {

    // names of user preferences keys
    static final String COPY_BY_SELECT_NS = "ns-copy-by-select";
    static final String REG_NS_FROM_XML = "reg-ns-from-xml-file";

    // components that are used from more methods
    JTextFieldWithHistory nsName, nsAuth, nsEmail;
    JTextArea nsDescArea;
    JButton unregisButton;
    CustomNamespacesTree namespacesTree;

    /*********************************************************************
     * Default constructor.
     ********************************************************************/
    public RegistrationNamespaceSubPanel() {
	super();
    }

    /**************************************************************************
     * Panel to register a namespace.
     **************************************************************************/
    public JComponent getComponent (PropertyChannel propertyChannel,
				    CommonConsole console) {
 	setPropertyChannel (propertyChannel);
	registryModel = createRegistryModel();
	this.console = console;

	JPanel p = new JPanel (new GridBagLayout());

	// text fields to define new data type
   	JLabel labelNSName = new JLabel ("Namespace");
	nsName = createText (null, "namespaceName", DP_REG_NS_NAME);
   	JLabel labelNSAuth = new JLabel ("Authority (usually a domain name)");
	nsAuth = createText (null, "namespaceAuth", DP_REG_NS_AUTH);
   	JLabel labelNSEmail = new JLabel ("Contact email");
	nsEmail = createText (null, "namespaceEmail", DP_REG_NS_EMAIL);
	nsDescArea = new JTextArea();
	JPanel nsDesc = createCustomTextArea ("Description", null, "namespaceDesc", DP_REG_NS_DESC,
					      nsDescArea);

	JPanel defs = createTitledPanel ("New Namespace");
 	SwingUtils.addComponent (defs, labelNSName,  0, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
 	SwingUtils.addComponent (defs, nsName,       0, 1, 1, 1, HORI, NWEST, 1.0, 0.0);
 	SwingUtils.addComponent (defs, labelNSAuth,  0, 2, 1, 1, NONE, NWEST, 0.0, 0.0, BREATH_TOP);
 	SwingUtils.addComponent (defs, nsAuth,       0, 3, 1, 1, HORI, NWEST, 1.0, 0.0);
 	SwingUtils.addComponent (defs, labelNSEmail, 0, 4, 1, 1, NONE, NWEST, 0.0, 0.0, BREATH_TOP);
 	SwingUtils.addComponent (defs, nsEmail,      0, 5, 1, 1, HORI, NWEST, 1.0, 0.0);
 	SwingUtils.addComponent (defs, nsDesc,       0, 6, 1, 1, BOTH, NWEST, 1.0, 1.0, BREATH_TOP);

	// a tree with all already existing namespaces
	NamespacesBoard namespacesBoard =
	    new NamespacesBoard (registryModel,
				 console,
				 propertyChannel,
				 namespacesTree = new CustomNamespacesTree (registryModel, console));
	namespacesBoard.updateTree (CommonTree.SORTED_BY_NAME);

	// allow to copy selected namespace to the new one
	boolean usingCopyBySelect = getPrefValue (COPY_BY_SELECT_NS, false);
	copyBySelect =
	    createCheckBox ("Fill new Namespace when selected", usingCopyBySelect, KeyEvent.VK_C,
			    new ItemListener() {
				public void itemStateChanged (ItemEvent e) {
				    onCopyBySelectNS (e.getStateChange() == ItemEvent.SELECTED);
				}
			    });

	JPanel board = new JPanel (new GridBagLayout());
 	SwingUtils.addComponent (board, namespacesBoard, 0, 0, 1, 1, BOTH, NWEST, 1.0, 1.0);
 	SwingUtils.addComponent (board, copyBySelect,    0, 1, 1, 1, NONE, NWEST, 0.0, 0.0);

	// split tree board and text fields 
	JSplitPane split = hSplit (defs, board, 0.5);

	// buttons
	registerButton = createRegisterButton
	    (" Register Namespace ",
	     "Register a new namespace in a Biomoby registry",
	     KeyEvent.VK_N);
	showXMLButton = createShowXMLButton
	    ("Create and show XML for registering this namespace",
	     KeyEvent.VK_X);
	fromXMLButton = createFromXMLButton
	    ("Register this namespace from a raw XML in a file",
	     "registerNamespace",
	     KeyEvent.VK_F,
	     REG_NS_FROM_XML,
	     createXMLChooser (REG_NS_FROM_XML));
	unregisButton =
	    createButton (" Unregister Namespace ",
			  "Remove an existing namespace from a Biomoby registry",
			  KeyEvent.VK_U,
			  new ActionListener() {
			      public void actionPerformed (ActionEvent e) {
			    	  namespacesTree.onUnregisterNamespace();
			      }
			  });
	unregisButton.setIcon (unregisterIcon);
	unregisButton.setDisabledIcon (unregisterIconDis);
	unregisButton.setEnabled( false );

	// put it together
 	SwingUtils.addComponent (p, split,          0, 0, 4, 1, BOTH, NWEST, 1.0, 1.0);
 	SwingUtils.addComponent (p, registerButton, 0, 1, 1, 1, NONE, WEST,  0.0, 0.0);
 	SwingUtils.addComponent (p, showXMLButton,  1, 1, 1, 1, NONE, WEST,  0.0, 0.0);
 	SwingUtils.addComponent (p, fromXMLButton,  2, 1, 1, 1, NONE, WEST,  0.0, 0.0);
 	SwingUtils.addComponent (p, unregisButton,  3, 1, 1, 1, NONE, NEAST, 0.0, 0.0);
	return p;
    }

    /**************************************************************************
     *
     **************************************************************************/
    protected void onCopyBySelectNS (boolean enabled) {
	setPrefValue (COPY_BY_SELECT_NS, enabled);
    }

    /**************************************************************************
     *
     **************************************************************************/
    public void checkAndRegister (boolean realRegistration)
	throws MobyException {

	String value = nsName.getText();
	if (UUtils.isEmpty (value))
	    throw new MobyException ("Namespace name is still missing. Please fill it first.");

	MobyNamespace namespace = new MobyNamespace (value);

	value = nsAuth.getText();
	if (UUtils.isEmpty (value))
	    throw new MobyException ("Every namespace must have an authority.\n" +
				     "Fill it, or (if such authority already exists)\n" +
				     "select it from the namespace tree sorted by authority.");
	namespace.setAuthority (value);

	value = nsEmail.getText();
	if (UUtils.isEmpty (value))
	    throw new MobyException ("Every namespace must have a contact person.\n" +
				     "Please fill in an email address.");
	namespace.setEmailContact (value);

	value = nsDescArea.getText();
	if (UUtils.isEmpty (value))
	    throw new MobyException ("Every namespace must have a description.\n" +
				     "Please fill in the most detailed one.");
	namespace.setDescription (value);

	if (realRegistration) {
	    console.setText ("Namespace to be registered:\n" +
			     "---------------------------\n" +
			     namespace.toString());
	    registryModel.registerNamespace (namespace);
	    console.setText ("\nRegistration successful!\n\n");
	    updateCache();

	} else {
	    String xml = registryModel.getRegisterNamespaceXML (namespace);
	    console.setText ("\n" + xml + "\n");
	}
    }

    /**************************************************************************
     * This is separated here because the registration from XML is
     * done in the super-class (RegistrationPanel), but the update
     * must be done here, in sub-class.
     **************************************************************************/
    protected void updateCache()
	throws MobyException {
	registryModel.updateNamespacesCache();
    }

    /**************************************************************************
     *
     * Customized tree of namespaces - has different popup menus etc...
     *
     **************************************************************************/
    protected class CustomNamespacesTree
	extends NamespacesTree {

	// action commands for popup menu items
	protected final static String AC_UNREG  = "ac-unreg";

	/*********************************************************************
	 * Construtor
	 ********************************************************************/
	public CustomNamespacesTree (RegistryModel model,
				     CommonConsole console) {
	    super (model, console);
	}

	/*********************************************************************
	 *
	 ********************************************************************/
	protected void createPopups (String title) {
 	    super.createPopups (title);
 	    removeFromPopups (AC_RELOAD);
 	    removeSeparatorAfter (AC_COLLAPSE);

	    popup.addSeparator();
 	    popup.add
 		(createMenuItem (new AbstractAction ("Unregister") {
 			public void actionPerformed (ActionEvent e) {
			    onUnregisterNamespace();
 			}
 		    }, AC_UNREG, unregisterIcon, unregisterIconDis));
	}

	/*********************************************************************
	 *
	 ********************************************************************/
	protected void setEnabledPopup (boolean enabled) {
	    super.setEnabledPopup (enabled);
	    selected (null);
	}

	/*********************************************************************
	 * Called when a tree node is selected, or deselected (in
	 * which case 'node' is null). If a namespace is selected,
	 * enable the 'unregister' popup menu item, and possibly copy
	 * this namespace values to the text fields defining a new
	 * namespace.
	 ********************************************************************/
	protected void selected (DefaultMutableTreeNode node) {

	    if (node == null) {
		setEnabledPopupItem (AC_UNREG, false);
		return;
	    }
	    final CommonNode nodeObject = (CommonNode)node.getUserObject();
	    final boolean isNamespace = (nodeObject.getType() == CommonNode.NODE_NAMESPACE);
	    final boolean isAuthority = (nodeObject.getType() == CommonNode.NODE_AUTHORITY);
	    setEnabledPopupItem (AC_UNREG, isNamespace);

	    final SwingWorker worker = new SwingWorker() {
		    MobyNamespace namespace = null;
		    public Object construct() {
			try {
			    if (isNamespace)
				namespace = registryModel.getNamespace (nodeObject.getValue());
			    unregisButton.setEnabled( isNamespace );
			} catch (MobyException e) {
			    error (NAMESPACES_ACCESS_ERROR, e);
			}
			return namespace;  // not used here
		    }

		    // runs on the event-dispatching thread.
		    public void finished() {
			if (namespace != null) {
			    console.setText (namespace.toString() + "\n");
			    if (copyBySelect.isSelected()) {
				nsName.setText (namespace.getName());
				nsAuth.setText (namespace.getAuthority());
				nsEmail.setText (namespace.getEmailContact());
				nsDescArea.setText (namespace.getDescription());
			    }
			} else if (isAuthority) {
			    if (copyBySelect.isSelected())
				nsAuth.setText (nodeObject.getValue());
			}
		    }
		};
	    worker.start(); 
	}

	/*********************************************************************
	 *
	 ********************************************************************/
	protected void onUnregisterNamespace() {
	    DefaultMutableTreeNode node =
		(DefaultMutableTreeNode)getLastSelectedPathComponent();
	    if (node == null) return;   // nothing selected
	    CommonNode nodeObject = (CommonNode)node.getUserObject();
	    final String name = nodeObject.getValue();

	    if (! confirm ("Are you sure you wish to unregister\n" +
			   "namespace: " + name + " ?"))
		return;

	    final SwingWorker worker = new SwingWorker() {
		    MobyException exception = null;
		    public Object construct() {
			try {
				unregisButton.setEnabled( false );
			    MobyNamespace namespace = new MobyNamespace (name);
			    console.setText ("Namespace to be unregistered: " + name + "\n");
			    registryModel.unRegisterNamespace (namespace);
			    console.setText ("\nUnregistration successful!\n\n");
			    updateCache();

			} catch (MobyException e) {
			    exception = e;
			}
			return null;  // not used here
		    }

		    // runs on the event-dispatching thread.
		    public void finished() {
			if (exception != null)
			    error ("An error occured when trying to unregister a namespace.\n\n",
				   exception);
		    }
		};
	    worker.start(); 
	}
    }
}
