/***********************************************************************************
 *
 * THIS SOFTWARE IS COPYRIGHT STEVEN MORRIS 2002. ALL RIGHTS RESERVED
 *
 * THIS SOFTWARE IS FREE FOR NON COMMERCIAL USE FOR THE PURPOSE OF LEARNING MHP.  ANY
 * USE FOR OTHER PURPOSES IS PROHIBITED UNLESS WRITTEN AGREEMENT IS OBTAINED.
 *
 * DISTRIBUTION OF THIS CODE IS PROHIBITED
 */

// Import the standard package that we need to be an Xlet
import javax.tv.xlet.*;

// The service selection API is contained in the
// javax.tv.service.selection.* package.  We also need the JavaTV locators
// in order to use it.
import javax.tv.locator.*;
import javax.tv.service.selection.*;

/**
 * This Xlet uses the JavaTV service selection API to select different
 * services and control them.
 *
 * Since this is quite a complex Xlet that may take a while to finish
 * doing its work, we do most of the work in a separate thread.  For
 * this reason, the Xlet implements the Runnable interface.  It also
 * implements the javax.tv.service.selection.ServiceContextListener
 * interface to receive events related to the service context that
 * it's manipulating.
 */
public class ServiceSelectionExample implements Xlet, Runnable, ServiceContextListener {

	// A private variable to store our Xlet context.  In this case, we do actually
	// use this, showing why it's a good idea to keep a reference to the Xlet context.
	private XletContext context;

	// A private variable that keeps a reference to the thread that
	// will do all of the work.
	private Thread myWorkerThread;

	/**************************************************************************
	 *
	 * The methods below this line are the standard Xlet methods.  These mostly do
	 * nothing surprising,since we do most of the work in a separate thread.
	 */

	/**
	 * A default constructor, included for completeness.
	 */
	public ServiceSelectionExample()
	{
	}

	/**
	 * Initialise the Xlet.  In this case, we have no special initialisation
	 * that we need to do.
	 */
	public void initXlet(javax.tv.xlet.XletContext context) throws javax.tv.xlet.XletStateChangeException
	{
		// We keep a reference to our Xlet context because we will need it later.
		this.context = context;
	}

	/**
	 * Start the Xlet.  This is where we actually start doing useful work.
	 */
	public void startXlet() throws javax.tv.xlet.XletStateChangeException
	{
		// startXlet() should not block for too long, and waiting for the SI queries
		// to complete is definitely too long.  To solve this, we start another
		// thread to do the work for this.
		myWorkerThread = new Thread(this);
		myWorkerThread.start();
	}

	/**
	 * Pause the Xlet.
	 */
	public void pauseXlet()
	{
		// We do nothing, which is not exactly a good idea, but we won't
		// use many resources and so this should be OK.

	}

	/**
	 * Destroy the Xlet.
	 */
	public void destroyXlet(boolean unconditional) throws javax.tv.xlet.XletStateChangeException
	{
		//tell the user that the method has been called
		System.out.println("destroyXlet() called");

		if (unconditional) {
			// We have been ordered to terminate, so we obey the order politely and release any
			//scarce resources that we are holding.
		}
		else {
			// We have had a polite request to die, so we can refuse this request if we want.
			throw new XletStateChangeException("Please don't let me die!");
		}
	}


	/**************************************************************************
	 *
	 * The methods below this point are the ones which actually do all of the work
	 * selecting the new service
	 */

	/**
	 * Select a new service, which may or may not be on the same transport
	 * stream.
	 */
	public void run() {
		// Before we can do anything, we need to create a service context.
		// We don't use our own service context, because it we selected a
		// new service in our own service context, this application would get
		// killed unless it was also signalled on the new service.  See annex
		// Z of the MHP 1.1 specification for a detailed discussion of the
		// relationship between services, service contexts and the application
		// lifecycle.

		// Get a reference to the service context factory.
		ServiceContextFactory factory = ServiceContextFactory.getInstance();

		// Create a new service context.
		ServiceContext sc;
		try {
			sc = factory.createServiceContext();
		}
		catch (InsufficientResourcesException ire) {
			// We don't have enough free resources to create a new service
			// context.  This may not mean exactly what you think.  See
			// Annex Z of the MHP 1.1 specification for more information
			System.out.println("Not enough resources to create a new service context.  Exiting...");
			return;
		}

		// We add ourselves as an event listener for the service context,
		// so that we can be notified of state changes.
		sc.addListener(this);


		// Now that we have a service context we're ready to select a
		// new service.  First, we create a javax.tv.locator.Locator
		// for the service that we want to select
		Locator locator = null;

		// JavaTV Locators are created by a LocatorFactory
		LocatorFactory locatorFactory = LocatorFactory.getInstance();
		// Now create the Locator.  This can throw an exception,
		// so we enclose it in a 'try' block.
		try {
			locator = locatorFactory.createLocator("dvb://122.100.3");
		}
		catch (MalformedLocatorException mle) {
			// This should never happen, because the locator above is well-formed
			System.out.println("Can't create the locator object - malformed locator");
			mle.printStackTrace();
		}

		// We need to pass the select() method either an array of Locators or a
		// Service object.  In this case, we will use the array of locators
		Locator[] locators = new Locator[1];
		locators[0] = locator;

		// After creating the locator, we can use it to select the service.
		// This can also fail in a number of ways, so again we enclose it
		// in a 'try' block.
		try {
			sc.select(locators);
		}
		catch (javax.tv.locator.InvalidLocatorException ile) {
			// Print an error message
			System.out.println("Invalid Locator when selecting the new service");
			ile.printStackTrace();
		}
		catch (InvalidServiceComponentException isce) {
			// Print an error message
			System.out.println("Invalid service component when selecting the new service");
			isce.printStackTrace();
		}
	}

	/**
	 * This method is inherited from javax.tv.service.selection.ServiceContextListener.
	 * It gets called whenever a service context that we're registered as a listener
	 * for generates an event.
	 */
	public void receiveServiceContextEvent(ServiceContextEvent event) {
		// In this case, we just print the event on the debug output
		System.out.println("Service context has generated an event - " + event);
	}

}