// $Id: events.java,v 1.17 2002/11/14 22:50:43 mdean Exp $


/**
 * select among multiple configurations of publishers and subscribers for genealogy event data
 */
class events
{
    static int generation = 0;

    static class ClassSelectorNode
	extends javax.swing.tree.DefaultMutableTreeNode
    {
	static java.util.Hashtable nodes = new java.util.Hashtable();

	ClassSelectorNode getNode(com.hp.hpl.mesa.rdf.jena.model.Resource cl)
	    throws com.hp.hpl.mesa.rdf.jena.model.RDFException
	{
	    ClassSelectorNode retval = (ClassSelectorNode) nodes.get(cl);
	    if (retval == null)
		{
		    retval = new ClassSelectorNode(cl);
		}
	    return retval;
	}

	ClassSelectorNode(com.hp.hpl.mesa.rdf.jena.model.Resource cl)
	    throws com.hp.hpl.mesa.rdf.jena.model.RDFException
	{
	    nodes.put(cl, this);
	    userObject = cl;
	    com.hp.hpl.mesa.rdf.jena.model.Model model = cl.getModel();

	    parent = null;
	    com.hp.hpl.mesa.rdf.jena.model.NodeIterator parents = model.listObjectsOfProperty(cl, com.hp.hpl.mesa.rdf.jena.vocabulary.RDFS.subClassOf);
	    while (parents.hasNext())
		{
		    // XXX - filter out Restrictions?

		    parent = getNode((com.hp.hpl.mesa.rdf.jena.model.Resource) parents.next());
		}

	    allowsChildren = true;
	    children = new java.util.Vector();
	    com.hp.hpl.mesa.rdf.jena.model.ResIterator kids = model.listSubjectsWithProperty(com.hp.hpl.mesa.rdf.jena.vocabulary.RDFS.subClassOf,
												       cl);
	    while (kids.hasNext())
		{
		    children.add(getNode(kids.next()));
		}
	}
    }

    static class ClassSelector
    {
	com.hp.hpl.mesa.rdf.jena.model.Resource selection = null;
	com.hp.hpl.mesa.rdf.jena.model.Resource root;

	javax.swing.JPanel panel = null;
	javax.swing.JLabel label = null;
	javax.swing.JTree tree = null;
	javax.swing.JButton clear = null;
	java.awt.TextArea text = null;

	ClassSelector(String label,
		      com.hp.hpl.mesa.rdf.jena.model.Resource root)
	    throws com.hp.hpl.mesa.rdf.jena.model.RDFException
	{
	    this.root = root;
	    tree = new javax.swing.JTree(new ClassSelectorNode(root));

	    clear = new javax.swing.JButton("None");
	    clear.addActionListener(new java.awt.event.ActionListener()
		{
		    public void actionPerformed(java.awt.event.ActionEvent e)
		    {
			tree.clearSelection();
		    }
		});

	    text = new java.awt.TextArea();
	    text.setEditable(false);

	    panel = new javax.swing.JPanel();
	    panel.add(new javax.swing.JLabel(label));
	    panel.add(tree);
	    panel.add(clear);
	    panel.add(text);
	}

	/**
	 * returns selected class or null if no class has been selected
	 */
	com.hp.hpl.mesa.rdf.jena.model.Resource getSelectedClass()
	{
	    javax.swing.tree.TreePath path = tree.getSelectionPath();
	    if (path == null)
		return null;
	    return (com.hp.hpl.mesa.rdf.jena.model.Resource) ((ClassSelectorNode) path.getLastPathComponent()).getUserObject();
	}

	/**
	 * subclass will normally specialize
	 */
	void run()
	    throws Exception
	{
	    text.setText("");
	}

	/**
	 * subclass will normally specialize
	 */
	void stop()
	    throws Exception
	{
	}
    }

    static class Publisher
	extends ClassSelector
    {
	Publisher(String label,
		  com.hp.hpl.mesa.rdf.jena.model.Resource root)
	    throws com.hp.hpl.mesa.rdf.jena.model.RDFException
	{
	    super(label, root);
	}

	org.daml.jbi.PubAdapter pubAdapter;
	PublisherThread publisherThread;
	java.sql.ResultSet results;

	class PublisherThread
	    extends Thread
	{
	    int startGeneration = generation;
	    public void run()
	    {
		try {
		    while ((generation == startGeneration)
			   && (results.next()))
			{
			    String pin = results.getString("pin");
			    String from_year = results.getString("from_year");
			    int ev_type = results.getInt("ev_type");

			    // determine type
			    com.hp.hpl.mesa.rdf.jena.model.Resource type = null;
			    switch (ev_type)
				{
				case 1:
				    type = gedcom.Birth;
				    break;
				case 2:
				    type = gedcom.Marriage;
				    break;
				case 3:
				    type = gedcom.Death;
				    break;
				default:
				    break;
				}
			    if ((type == null)
				|| (pubAdapter.pubUIDs.get(type) == null))	// XXX
				continue;

			    // create event
			    org.daml.jbi.Message event = new org.daml.jbi.Message(type);
			    event.add(gedcom.date,
				      from_year);
			    event.add(com.hp.hpl.mesa.rdf.jena.vocabulary.RDFS.label, // XXX
				      pin);
			    
			    // indicate publication
			    text.append(events.toString(event) + "\n");
			    
			    // publish event
			    try {
				pubAdapter.publish(event);
			    } catch (mil.af.rl.pubAdapter.ThrottleException e) {
				System.err.println(e); // XXX
			    }
			    
			    // delay
			    sleep(1000L);
			}
		} catch (Exception e) {
		    System.err.println(e); // XXX
		    e.printStackTrace();
		}
	    }
	}

	void run()
	    throws Exception
	{
	    // clear output
	    text.setText("");

	    com.hp.hpl.mesa.rdf.jena.model.Resource cl = getSelectedClass();
	    if (cl == null)
		return;

	    pubAdapter = new org.daml.jbi.PubAdapter();
	    org.daml.jbi.Message meta = new org.daml.jbi.Message(cl);
	    pubAdapter.registerPublisher(meta,
					 "g1", null, 10);

	    text.append("registered\n");

	    // open database (move to PublisherThread?) 
	    Class.forName("com.mysql.jdbc.Driver").newInstance();
	    java.sql.Connection connection = java.sql.DriverManager.getConnection("jdbc:mysql:///prf");
	    final java.sql.Statement statement = connection.createStatement();
	    results = statement.executeQuery("select pin, from_year, from_month, from_day, ev_type from prf12 where from_year >= 1900 order by from_year");

	    publisherThread = new PublisherThread();
	    publisherThread.start();
	}

	void stop()
	    throws Exception
	{
	    if (pubAdapter != null)
		{
		    publisherThread.join();
	    
		    pubAdapter.unregisterAll();
		}
	}
    }

    static class Subscriber
	extends ClassSelector
    {
	Subscriber(String label,
		   com.hp.hpl.mesa.rdf.jena.model.Resource root)
	    throws com.hp.hpl.mesa.rdf.jena.model.RDFException
	{
	    super(label, root);
	}

	org.daml.jbi.SubAdapter subAdapter;
	java.util.Vector threads = new java.util.Vector();

	class SubscriberThread
	    extends Thread
	{
	    Object subUID;
	    int startGeneration = generation;

	    SubscriberThread(Object subUID)
	    {
		this.subUID = subUID;
	    }

	    public void run()
	    {
		while (generation == startGeneration)
		    try {
			java.io.Serializable container = subAdapter.subAdapter.getDelivery(subUID);
			if (container != null)
			    {
				byte [] meta = subAdapter.subAdapter.getMetadata(container);
				org.daml.jbi.Message message = new org.daml.jbi.Message(subAdapter.subAdapter.getPayload(container));
				
				text.append(events.toString(message) + "\n");
			    }

			sleep(500L);
		    } catch (Exception e) {
			System.err.println(e);
			e.printStackTrace();
		    }
	    }
	}
	
	void run()
	    throws Exception
	{
	    text.setText("");

	    com.hp.hpl.mesa.rdf.jena.model.Resource cl = getSelectedClass();

	    if (cl == null)
		return;
	    subAdapter = new org.daml.jbi.SubAdapter();
	    org.daml.jbi.Message meta = new org.daml.jbi.Message(getSelectedClass());
	    subAdapter.registerSubscriber(meta,
					  "g1", 10,
					  new mil.af.rl.subAdapter.SubNotificationListener()
					  {
					      public synchronized void notifyArrival(Object subUID)
					      {
						  SubscriberThread subscriberThread = new SubscriberThread(subUID);
						  subscriberThread.start();
						  threads.add(subscriberThread);
					      }
					      
					      public synchronized void notifyLoss(Object subUID)
					      {
						  text.append("lost message\n");
					      }
					  },
					  500);
	    text.append("registered\n");
	}

	void stop()
	    throws Exception
	{
	    if (subAdapter != null)
		{
		    subAdapter.unregisterAll();
	    
		    // XXX - join threads
		}
	}
    }

    /**
     * read the specified local file as if it came from the specified URI
     */
    public static void readLocal(com.hp.hpl.mesa.rdf.jena.model.Model model,
				 String path,
				 String base)
	throws java.io.IOException,
	com.hp.hpl.mesa.rdf.jena.model.RDFException
    {
	java.io.FileInputStream stream = new java.io.FileInputStream(path);
	model.read(new java.io.InputStreamReader(stream),
		   base);
	stream.close();
    }

    /**
     * print Event message
     */
    static String toString(org.daml.jbi.Message message)
	throws com.hp.hpl.mesa.rdf.jena.model.RDFException
    {
	String type = message.getType().toString();
	int hash = type.indexOf('#');
	if (hash != (-1))
	    type = type.substring(hash + 1);
	return message.getProperty(com.hp.hpl.mesa.rdf.jena.vocabulary.RDFS.label).getLiteral() + " " + type + " " + message.getProperty(gedcom.date).getLiteral();
    }

    public static void main(String args[])
	throws Exception
    {
	com.hp.hpl.mesa.rdf.jena.model.Model model = new com.hp.hpl.mesa.rdf.jena.mem.ModelMem();

	readLocal(model, "../gedcom/gedcom.daml", "http://www.daml.org/2001/01/gedcom/gedcom");
	model.read("file:gedcom-jbi.daml");
	readLocal(model, "jbi-ont.daml", "http://www.daml.org/2002/11/jbi/jbi-ont");

	com.hp.hpl.mesa.rdf.jena.model.Resource root = model.createResource(org.daml.jbi.jbi_ont.InfoObject.toString());

	javax.swing.JFrame frame = new javax.swing.JFrame("DAML JBI TIE Events Demo");
	frame.setSize(900, 600);
	java.awt.Container pane = frame.getContentPane();
	pane.setLayout(new java.awt.GridLayout(2, 2, 5, 5));
	final Publisher pub1 = new Publisher("Publisher 1",
					     root);
	final Publisher pub2 = new Publisher("Publisher 2",
					     root);
	final Subscriber sub1 = new Subscriber("Subscriber 1",
					       root);
	final Subscriber sub2 = new Subscriber("Subscriber 2",
					       root);
	pane.add(pub1.panel);
	pane.add(sub1.panel);
	pane.add(pub2.panel);
	pane.add(sub2.panel);

	final java.util.Vector panes = new java.util.Vector();
	panes.add(pub1);
	panes.add(pub2);
	panes.add(sub1);
	panes.add(sub2);

	javax.swing.JMenuBar menuBar = new javax.swing.JMenuBar();
	frame.setJMenuBar(menuBar);
	javax.swing.JMenuItem run = new javax.swing.JMenuItem("Run");
	menuBar.add(run);
	run.addActionListener(new java.awt.event.ActionListener()
	    {
		public void actionPerformed(java.awt.event.ActionEvent e)
		{
		    try {
			java.util.Iterator iterator = panes.iterator();
			while (iterator.hasNext())
			    {
				ClassSelector selector = (ClassSelector) iterator.next();
				selector.run();
			    }
		    } catch (Exception ex) {
			System.err.println(ex); // XXX
			ex.printStackTrace();
		    }
		}
	    });
	javax.swing.JMenuItem stop = new javax.swing.JMenuItem("Stop");
	menuBar.add(stop);
	stop.addActionListener(new java.awt.event.ActionListener()
	    {
		public void actionPerformed(java.awt.event.ActionEvent e)
		{
		    // cause PublisherThread's to finish
		    generation++;

		    // run stop methods
		    java.util.Iterator iterator = panes.iterator();
		    while (iterator.hasNext())
			{
			    ClassSelector selector = (ClassSelector) iterator.next();
			    try {
				selector.stop();
			    } catch (Exception ex) {
				System.err.println(ex);
				ex.printStackTrace();
			    }
			}
		}
	    });
	javax.swing.JMenuItem exit = new javax.swing.JMenuItem("Exit");
	menuBar.add(exit);
	exit.addActionListener(new java.awt.event.ActionListener()
	    {
		public void actionPerformed(java.awt.event.ActionEvent e)
		{
		    System.exit(0);
		}
	    });

	frame.setVisible(true);
    }
}
