<html>
<xmp>
/**
 * Title: Abstract ContextApp
 * Copyright: Copyright (c) 2003
 * Company:   University of Oregon Computer Science Dept.
 *
 * @author Jason Prideaux
 * @version 1.0
 *
 */

package cat.app;

import cat.context.*;
import cat.sig.*;
import cat.sig.event.*;
import cat.hist.*;
import cat.sensor.core.*;
import cat.sensor.util.*;
import cat.sensor.event.*;
import cat.sensor.soft.*;
import cat.util.*;
import cat.transport.*;
import transport.protocol.*;

import java.util.*;
import java.io.*;
import java.net.*;


/* ===================================================================== */
/** This class is a template for building applications using CAT.
 *  It implements a few useful features (which can be overwritten) and
 *  defines what needs to be implemented.
 *
 *
 */
public abstract class ContextApp implements SigListener, SensorListener{

	//Keep a queue of sensor events, for processing in a thread.
	//Prevents this app from being a bottleneck in event passing.
	private Vector eventsQueue = new Vector();

	//The ContextManager for this wearable application.
	private ContextManager manager;



    /* ===================================================================== */
    /** This constructor will start the application.  It will call a setup
     *  method which signs us up as a listener to sensors.  And the rest is
     *  up to whoever extends this class.
     *
     *  @param  id   The unique of this agent/device.
     *  @param  port   The port number that CAT will use to communicate.
     *
     */
	public ContextApp(String id, int port){

		//set up the port and name of this agent.
		iSIM.PORT = port;
		iSIM.ID = id;

		//set up the app.
		appSetUp();

		//process sensor events.
		new Thread(this).start();

	} //Constructor



    /* ===================================================================== */
    /** This method sets up the basic information for the application.
     *  It creates the ContextManagaer, and signs up us as a sensor listener
     *  to all sensors. (note: soft sensors have not been created yet, that
     *  is up to whoever extends this class)
     *
     */
	final public void appSetUp(){

		System.out.println("Creating the ContextManager.");

		//Create the Context-Aware Toolkit.
		manager = new ContextManager();


		//Want to listen to our available local sensors.
		System.out.println("Retrieving our available local sensors.");
		Sensor[] sensors = manager.getSensorManager().getAllSensors();
		for(int i=0; i<sensors.length; i++ ){
			System.out.println("  Sensor ID: "+sensors[i].getID()+",  Type: "+sensors[i].getSensorType() );
			sensors[i].addSensorListener(this);
		}

		//Want to listen to SIG Events.
		manager.getSigManager().addSigListener(this);


	} //method: appSetUp



    /* ===================================================================== */
    /** This method can be used to send p2p communication.  Can send any
     *  serializable object to the given peer/agent.
     *
     *  @param  agent   The agent to send a message/object to.
     *  @param  obj   The object to send to the agent.
     *
     */
	final public static void p2pSend( Agent agent, Object obj ){

		Message msg = new UnicastMessage(""+SensorOntology.P2PDATA, agent.getPort(), iSIM.PORT );
		((UnicastMessage)msg).setContent( obj );
		msg.setTo(agent.getPort());
		msg.setSender(new Agent(iSIM.ID, iSIM.PORT ));
		//use transport to send it.
		//we don't care how it gets sent.
		new Transport().sendMessage(msg);

	} //method: p2pSend




    /* ===================================================================== */
    /** This method gets SIG events, it can process most of the information
     *  itself, and then it merely asks the extending application if it would
     *  like to join a SIG, or to inform it of an agent that joined.
     *
     *  @param  ce  The incoming SIG event
     *
     */
    final public void handleSigEvent(SigEvent ce){

		//get request to join a SIG.
		if( ce instanceof SigRequestEvent ){

			System.out.println("The ContextApp got a SigRequest");

			//get the sig name, sensors, and from who
			Vector wanted = ((SigRequestEvent)ce).getWantedSensors();
			Vector sharing = ((SigRequestEvent)ce).getGivingSensors();
			String s = ce.getSig();
			Vector a = ((SigRequestEvent)ce).getAgents();

			if( joinSig(s, a, wanted, sharing) && getContextManager().getSigManager().findSig(s) == null){

				System.out.println("   We will join the SIG.  Sending a SigResponse.");

				//find our sensors that we'll share
				Vector toshare = new Vector();
				Vector all = new Vector();
				Sensor[] sensors = manager.getSensorManager().getAllSensors();
				for(int j=0; j<wanted.size(); j++ ){
					for(int i=0; i<sensors.length; i++ ){
						if( ((Integer)(wanted.elementAt(j))).intValue() == sensors[i].getSensorType() ){
							toshare.add( sensors[i] );
							all.add( sensors[i] );
						}
					}
				}

				//create sensors for what sig is sharing
				//Vector sharing = s.getAllSensors();
				for(int j=0; j<sharing.size(); j++ ){
					//Sensor sr = (Sensor)(sharing.elementAt(j));
					LeafSensor ls = new LeafSensor("remote_"+s+"_"+((Integer)(sharing.elementAt(j))).intValue() );
					ls.setRemote(true);
					ls.setSensorType( ((Integer)(sharing.elementAt(j))).intValue() );
					manager.getSensorManager().addLeafSensor(ls.getID(), ls);
					all.add(ls);
					ls.addSensorListener(this);
					ls.addSensorListener(manager.getHistoryManager());
				}

				//sigmanager.join
				manager.getSigManager().joinSig(s,toshare, all, a, 0, 0);

			}else{
				System.out.println("   We will not join the SIG.");
			}

		//got a response from a SIG request we sent.
		}else if( ce instanceof SigResponseEvent ){

			//This could all be done somewhere else.  The app

			System.out.println("The ContextApp got a SigResponse");

			//get the sig name, sensors, and from who
			Vector giving = ((SigResponseEvent)ce).getGivingSensors();
			String s = ((SigResponseEvent)ce).getSig();
			Agent a = ce.getAgent();

			Sig sig = manager.getSigManager().findSig(s);

			if( sig.getAgents().size() == 0 ){

				//now create sensors for ones that sig is sharing
				// and add sensors to the sensorManager and SIG
				for(int j=0; j<giving.size(); j++ ){
					LeafSensor ls = new LeafSensor("remote_"+s+"_"+((Integer)(giving.elementAt(j))).intValue());
					ls.setRemote(true);
					ls.setSensorType( ((Integer)(giving.elementAt(j))).intValue() );
					manager.getSensorManager().addLeafSensor(ls.getID(), ls);
					ls.addSensorListener(this);
					ls.addSensorListener(manager.getHistoryManager());
				}
			}
			if( a.getId() != iSIM.ID ){
				sig.addAgent(a);
			}

			joinedSig(s,a);


		}


	} //method: handleSigEvent



    /* ===================================================================== */
    /** This method tells the ContextApp, that the app wants to join the given
     *  SIG.  This abstract class can then handle the details of joining the SIG.
     *
     *  @param  s   SIG name.
     *  @param  a   Agents in SIG.
     *  @param  wanted   Sensors wanted for SIG.
     *  @param  sharing   Sensors giving to SIG.
     *
     */
	final public void willJoinSig(String s, Vector a, Vector wanted, Vector sharing ){

		if( getContextManager().getSigManager().findSig(s) == null){

			System.out.println("   We will join the SIG.  Sending a SigResponse.");

			//find our sensors that we'll share
			Vector toshare = new Vector();
			Vector all = new Vector();
			Sensor[] sensors = manager.getSensorManager().getAllSensors();
			for(int j=0; j<wanted.size(); j++ ){
				for(int i=0; i<sensors.length; i++ ){
					if( ((Integer)(wanted.elementAt(j))).intValue() == sensors[i].getSensorType() ){
						toshare.add( sensors[i] );
						all.add( sensors[i] );
					}
				}
			}

			//create sensors for what sig is sharing
			//Vector sharing = s.getAllSensors();
			for(int j=0; j<sharing.size(); j++ ){
				//Sensor sr = (Sensor)(sharing.elementAt(j));
				LeafSensor ls = new LeafSensor("remote_"+s+"_"+((Integer)(sharing.elementAt(j))).intValue() );
				ls.setRemote(true);
				ls.setSensorType( ((Integer)(sharing.elementAt(j))).intValue() );
				manager.getSensorManager().addLeafSensor(ls.getID(), ls);
				all.add(ls);
				ls.addSensorListener(this);
				ls.addSensorListener(manager.getHistoryManager());
			}

			//sigmanager.join
			manager.getSigManager().joinSig(s,toshare, all, a, 0, 0);
		}

	} //method: willJoinSig


// Hook methods

    /* ===================================================================== */
    /** This method gets called when app receives a SIG join request.
	 *
	 */
	public abstract boolean joinSig(String name, Vector agents, Vector wanted_sensors, Vector sharing_sensors);

    /* ===================================================================== */
    /** This method gets called when an agent joined a SIG that you part of.
	 *
	 */
	public abstract void joinedSig(String name, Agent agent);


    /* ===================================================================== */
    /** This method is for handling sensor events. (agent discovery is a
     *  sensor event)
	 *
	 */
  	public abstract void run();







	//Being an Observer, we must implement this method
	//to get events from those that we observe.
    /* ===================================================================== */
    /** Get sensor events in here.
	 *
	 */
	final public void update(Object event){
		synchronized(eventsQueue){
			eventsQueue.add(event);
		}
	} //method: update


    /* ===================================================================== */
    /** Returns the Context Manager for the app to use.
	 *
	 */
	final public ContextManager getContextManager(){
		return manager;
	}

    /* ===================================================================== */
    /** Returns a list of non-processed sensor events.
	 *
	 */
	final public Vector getSensorQueue(){
		return eventsQueue;
	}


} //class: ContextApp
</xmp>
</html>