/* Author: Jeff Dalton <J.Dalton@ed.ac.uk> &
 *         Stephen Potter <stephenp@aiai.ed.ac.uk>
 *         Added new agent names by Austin Tate
 * Last Updated: 6-Dec-2001 by Austin Tate
 * Copyright: (c) 2001, AIAI, University of Edinburgh
 */

package ix.akt;

import java.io.*;
//import java.awt.*;

// Imports for using JDOM
// import java.io.IOException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import org.w3c.dom.*;
import com.sun.xml.tree.*;
import sunlabs.brazil.util.http.*;
import java.util.*;
import java.net.*;

import ix.icore.Issue;
import ix.icore.BasicIssue;

import ix.iface.util.XML;

import ix.util.lisp.*;		// only for Issue -> XML
import ix.util.*;

import aktbus.core.*;



/*
 * A class that encapsulates the knowledge needed for
 * sending and receiving messages on the AKT Knowledge Bus.
 */

public class AktCommunicationStrategy 
//implements IPC.CommunicationStrategy, MessageListener {
implements IPC.CommunicationStrategy {

    public String ipcName = "ipcName unknown";

    public IPC.MessageListener ipcListener = null;

    public AktCommunicationStrategy() {
    }


    /**
     * Sends an Issue or Report to a named AKT agent by converting
     * it to XML and calling the <code>sendText</code> method.
     */
    public void sendObject(Object destination, Object contents) {
	String toName = (String)destination;
	sendText(toName, XML.objectToXML(contents));
    }

    /**
     * Sends a string to a named AKT agent.
     */
    public void sendText(String toName, String contents) {

	AktMessage mess;
	String hostname = null;
	String recp = null;

	InetAddress intadd;

	try{
	  // retrieves name of local host - assumes that recipient is also running
	  // on same host (!). This should be modified at some stage....
	  intadd = InetAddress.getLocalHost();

	  // getHostName returns string in the form, for example, of "kerrera.aiai.ed.ac.uk"
	  // (need to check what this returns on PCs...)
	  hostname = intadd.getHostName();
	  //Debug.noteln(hostname);
	}
	catch (Exception e){
	  Debug.noteException(e);
	}

	
	// prefix hostname to convert to valid URI, and append appropriate port number:
	if(toName.equalsIgnoreCase("IDEEL")) recp = "http://"+hostname + ":5040";
	if(toName.equalsIgnoreCase("ILEED")) recp = "http://"+hostname + ":5050";
	if(toName.equalsIgnoreCase("ITEST")) recp = "http://"+hostname + ":5060";
	if(toName.equalsIgnoreCase("I-Sample")) recp = "http://"+hostname + ":5062";
	if(toName.equalsIgnoreCase("Supervisor")) recp = "http://"+hostname + ":5070";
	if(toName.equalsIgnoreCase("Operator")) recp = "http://"+hostname + ":5072";
	if(toName.equalsIgnoreCase("Edinburgh")) recp = "http://"+hostname + ":5080";
	if(toName.equalsIgnoreCase("Aberdeen")) recp = "http://"+hostname + ":5082";
	if(toName.equalsIgnoreCase("Panel-1")) recp = "http://"+hostname + ":5091";
	if(toName.equalsIgnoreCase("Panel-2")) recp = "http://"+hostname + ":5092";
	if(toName.equalsIgnoreCase("Panel-3")) recp = "http://"+hostname + ":5093";
	if(toName.equalsIgnoreCase("Panel-4")) recp = "http://"+hostname + ":5094";
	if(toName.equalsIgnoreCase("Panel-5")) recp = "http://"+hostname + ":5095";
	if(toName.equalsIgnoreCase("Panel-6")) recp = "http://"+hostname + ":5096";
	if(toName.equalsIgnoreCase("Panel-7")) recp = "http://"+hostname + ":5097";
	if(toName.equalsIgnoreCase("Panel-8")) recp = "http://"+hostname + ":5098";
	if(toName.equalsIgnoreCase("Panel-9")) recp = "http://"+hostname + ":509";

	Debug.noteln("Sending to : "+recp+" ("+toName+")");
	Debug.noteln("Contents : "+contents);
	
	try{
	  // create a new AktMessage, and then send it (to recp).
	  // following should add message under "aktbus:hasSimpleMessage" node:
	  mess = new AktMessage(new URL("http://"+hostname),new URL(recp),contents);

	  // and send mess to recp:
	  mess.send();
	}
	catch (Exception e) {
	  Debug.noteException(e);
	}
    }



    /**
     * Initiate an AktBus receiver object and set up for receiving messages.
     */
    public void setupServer(Object destination, 
			    IPC.MessageListener listener) {

	ipcName = (String)destination;   // our name for registering as agent
	ipcListener = listener;

	int port=8888; // dummy default

	final AktMessage mess;
	final Object contents;

	//	Debug.noteln("\nipcName = "+ipcName);

	// get number of the appropriate port to open for each application:
	if(ipcName.equalsIgnoreCase("IDEEL")) port = 5040;
	if(ipcName.equalsIgnoreCase("ILEED")) port = 5050;
	if(ipcName.equalsIgnoreCase("ITEST")) port = 5060;
	if(ipcName.equalsIgnoreCase("I-Sample")) port = 5062;
	if(ipcName.equalsIgnoreCase("Supervisor")) port = 5070;
	if(ipcName.equalsIgnoreCase("Operator")) port = 5072;
	if(ipcName.equalsIgnoreCase("Edinburgh")) port = 5080;
	if(ipcName.equalsIgnoreCase("Aberdeen")) port = 5082;
	if(ipcName.equalsIgnoreCase("Panel-1")) port = 5091;
	if(ipcName.equalsIgnoreCase("Panel-2")) port = 5092;
	if(ipcName.equalsIgnoreCase("Panel-3")) port = 5093;
	if(ipcName.equalsIgnoreCase("Panel-4")) port = 5094;
	if(ipcName.equalsIgnoreCase("Panel-5")) port = 5095;
	if(ipcName.equalsIgnoreCase("Panel-6")) port = 5096;
	if(ipcName.equalsIgnoreCase("Panel-7")) port = 5097;
	if(ipcName.equalsIgnoreCase("Panel-8")) port = 5098;
	if(ipcName.equalsIgnoreCase("Panel-9")) port = 5099;

	try{

	  AktBusReceiver rec = new AktBusReceiver(port);
	  receiveMessage(rec);
	
	}
	catch (Exception e){
	    Debug.noteException(e);
	}
    
    }


  /* get_subtree_text recursively extracts all the text existing in the subtree below
   * node n, and returns it, as a single string.
   * Note: since this requires quite detailed knowledge of the construction of AKTBUS
   * messages, it would seem to be at too low a level (and relies on this construction
   * staying constant). This method was originally (in the AKTBUS package) implemented
   * using "&lt;" and "&gt;" rather than "<" and ">" - but this had to be altered, as
   * the BasicInputMessage constructor requires its XML content to be expressed in the
   * form of a simple String.
   */

  String get_subtree_text(org.w3c.dom.Node n)
  {
    Node child;
    String result="";
    
    child=n.getFirstChild();		//get 1st child of Node n
    while (child!=null)
      {
	if (child.getNodeType() == ElementNode.TEXT_NODE)
	  {
	    result += child.getNodeValue();
	  }
	else	{
	  // Adds less than and greater than to the elements
	  result +=
	    "<" + child.getNodeName() + ">"
			+ get_subtree_text(child)
		+ "</" + child.getNodeName() + ">";
	}
	child = child.getNextSibling();
      }
    return result;
  }

  
  /* receiveMessage threads a receiver which waits for a message to be received,
   * then extracts the content, converts this content into an xml object, and 
   * notifies the ipcListener with this object, before looping to receive the
   * next message.
   */

  void receiveMessage(final AktBusReceiver rec){
    new Thread(){
      public void run(){
	while(true) {
	  try{

	    // receives a message, extracts simple content (NOTE:CHECK THIS AGAINST FUTURE
	    // AKTBUS RELEASES!), then converts it into the expected XML object.

	    AktMessage newm = rec.receive_message();
	    //newm.ugly_write(); // lets us see what's been received.

	    // following lines require too deep a knowledge of AKTBUS internals...
	    // expect this to change....:

	    org.w3c.dom.Node nd = newm.find_element_node("aktbus:hasSimpleMessage");
	    if(nd==null){

	      // if the hasSimpleMessage node does not exist, then try hasContent node?
	      // Note: this is not guaranteed to work in every eventuality, especially
	      // if messages are generated external to I-X (ie not using the sendText method
	      // above).

	      Debug.noteln("Non-IX generated message received (has no hasSimpleMessage node)");
	      nd = newm.find_element_node("aktbus:hasContent");
	    }

	    // flatten message content into single string:
	    String mcontent = get_subtree_text(nd); 

	    // output message details.
	    Debug.noteln("\nSender: "+newm.get_sender());
	    Debug.noteln("Receiver: "+newm.get_receiver());
	    Debug.noteln("Incoming message: "+mcontent);

	    // create an XML object from the message content
	    Object contents = XML.objectFromXML(mcontent);

	    // and notify the ipcListener:
	    ipcListener.messageReceived(new IPC.BasicInputMessage(contents));

	  }
	  catch (Exception e){

	    Debug.noteException(e);

	  }
	}
      }
    }.start();
  }

 
}
