/* ***************************************************************************+
 * ITX package (cnrg.itx) for telephony application programming.              *
 * Copyright (c) 1999  Cornell University, Ithaca NY.                         *
 * A copy of the license is distributed with this package.  Look in the docs  *
 * directory, filename GPL.  Contact information: bergmark@cs.cornell.edu     *
 ******************************************************************************/


package cnrg.itx.datax;

import java.util.*;

/**
 * Abstract class for creating and managing an endpoint of communication. Each Connection 
 * has two different "channels" an input channel and an output channel.  Both channels collect
 * data from sources and pipe it to destinations.  The input channel is intended to "collect" data
 * while the output channel is intended to distribute data.  However, it is possible not to follow these
 * conventions with the system still functioning.  We distinguish between input and output for naming
 * reasons only -- they have the same functionality.  
 */
public abstract class Connection extends Observable implements Statistics, Runnable, Properties
{
	private static final int SLEEPTIME = 2000;
	
	Stats s;
	Channel input, output;
	Thread t;
	boolean running;
	
	/**
	 * Creates a connection.
	 * @param input The channel which is inteded to collect data.
	 * @param output The channel which distributes data.
	 */
	public Connection(Channel input, Channel output)
	{	
		s = new Stats();
		this.input = input;
		this.output = output;
	}
	
	/**
	 * Interface to get the input channel.
	 * @return Channel the input channel
	 */
	public Channel getInputChannel()
	{
		return input;  
	}
	
	/**
	 * Interface to get the output channel.
	 * @return Channel the output channel
	 */
	public Channel getOutputChannel()
	{
		return output;
	}
	
	/**
	 * Sets a new input channel.  <p> The old channel should be closed to free its resources.
	 * @param c The new input channel
	 */
	public void setInputChannel(Channel c)
	{
		input = c;
	}
	
	/**
	 * Sets a new output channel.
	 * The old channel should be closed to free its resources.
	 * 
	 * @param c The new output channel
	 */
	public void setOutputChannel(Channel c)
	{
		output = c;
	}
	
	/**
	 * Interface to get the statistics for the connection.
	 * @return Stats the statistics corresponding to the channel
	 */
	public Stats getStatistics()
	{
		return s;
	}

	/**
	 * Gets the properties for this audioConnection
	 * @return the ProertiesCollection
	 */
	public PropertiesCollection getProperties()
	{
		PropertiesCollection pc = new PropertiesCollection();
		
		try
		{
			if (input != null)
				pc = input.getProperties();
			if (output != null)
				pc = pc.merge( output.getProperties() );
		}
		catch (DataException e)
		{
			e.printStackTrace();
		}
		
		return pc;
	}

	/**
	 * Sets the properties for this audioConnection
	 */
	public void setProperties(PropertiesCollection pc)
	{
		if (input != null)
			input.setProperties(pc);
		if (output != null)
			output.setProperties(pc);
	}

	/**
	 * Interface to set the given properties collection into the device. WOrks under the 
	 * assumption that this is the properties collection of the peer.
	 */
	public void setPeerProperties(PropertiesCollection pc) throws DataException
	{
		if (input!= null)
			input.setPeerProperties(pc);
		if (output != null)
			output.setPeerProperties(pc);
	}
	
	/**
	 * Method to implement the connection thread.
	 */
	public void run()
	{
		while (running)
		{
			s = new Stats();
			if (input != null)
				s = s.merge( input.getStatistics() );
			if (output != null)
				s = s.merge(output.getStatistics());
			setChanged();
			notifyObservers(s);

			try
			{ 
				Thread.currentThread().sleep(SLEEPTIME);
			}
			catch (InterruptedException ie)
			{
				ie.printStackTrace();
			}
		}
	}

	/**
	 * Begins sending data in the channel.  Calls open on both channels, which will call
	 * open on all devices attached to the channel.
	 * @exception DataException thrown after reopening a device that has already been closed. 
	 */
	public void open() throws DataException
	{
		if (input != null)
			input.open();
		if (output != null)
			output.open();
		
		t = new Thread(this);
		t.setDaemon(true);
		running = true;
		t.start();
	}
	
	/**
	 * Stops sending data in the channel.  Calls close on both channels, which closes all
	 * devices attached to the channel.  Note: Once a devide has been closed, it cannot be
	 * reopened.  
	 */
	public void close()
	{
		if (output != null)
			output.close();
		if (input != null)
			input.close();
		
		running = false;
	}
}
