package com.wiley.compbooks.brose.chapter11.event;

/**
 * Chapter 11, printer server and implementation
 */

import org.omg.CosEventChannelAdmin.*;
import org.omg.CosEventComm.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.Any;
import org.omg.CORBA.ORB;
import org.omg.PortableServer.*;

import java.util.Hashtable;
import com.wiley.compbooks.brose.chapter11.office.*;
import com.wiley.compbooks.brose.chapter11.office.PrinterPackage.*;

class PrinterImpl 
    extends PrinterPOA
    implements PushSupplierOperations
{
    private EventChannel channel;
    private SupplierAdmin supplierAdmin ;
    private ProxyPushConsumer proxyPushConsumer;
    private ORB orb;
    private POA poa;

    private Hashtable queue;
    private int jobId;
    private int printIdx;
    private boolean offline;
    private boolean disconnected;
    private PrintThread printThread;

    static class JobInfo
    {
	public int jobId;
	public String userId;
	public String text;

	public JobInfo(int jobId, String userId, String text)
	{
	    this.jobId = jobId;
	    this.userId = userId;
	    this.text = text;
	}
    }


    /** Inner class PrintThread ( member class)
	simulates the actual "printing" in a separate thread
     */

    class PrintThread extends Thread
    {
	public PrintThread()
	{
	    start();
	}

	/**
	 * convenience method that does the synchronization
	 */
	
	public synchronized void tell()
	{
	    super.notify();
	}

	public void run()
	{
	    while( true )
	    {
		// wait until there are jobs waiting
		while( printIdx >= jobId || offline )
		{
		    try
		    {
			synchronized( this )
			{ 
			    wait(); 
			}
		    }
		    catch( InterruptedException ie )
		    {}
		}

		// "print"
		JobInfo job = (JobInfo)queue.remove( new Integer( printIdx ));
		if( job != null && generateEvents() )
		{		
		    System.out.println("--Printing Job # " + job.jobId + " --\n" + job.text + "\n--END JOB---");
		    // create an event and  push it
		    Any printEvent = orb.create_any();
		    PrinterStatus status = new PrinterStatus();
		    status.the_job( Status.PRINTED, new Job( job.jobId, job.userId ) );
		    PrinterStatusHelper.insert( printEvent, status );
		    try
		    {
			proxyPushConsumer.push(printEvent);
		    }
		    catch( org.omg.CosEventComm.Disconnected d )
		    {
			// ignore
		    }
		}
		// update internal printing position
		printIdx++;
		try
		{
		    Thread.sleep(2000);
		}
		catch( Exception e )
		{
		    // ignore
		}
	    }
	}
    }


    public PrinterImpl(EventChannel e, ORB orb, POA poa)
    {
	// set the ORb and event channel
	this.orb = orb;
	this.poa = poa;
	channel = e;
    }

    public void connect()
    {
	PushSupplierPOATie thisTie = new PushSupplierPOATie( this );

	// get admin interface and proxy consumer
	supplierAdmin = channel.for_suppliers();
	proxyPushConsumer = supplierAdmin.obtain_push_consumer();

	// connect the push supplier
	try
	{
	    proxyPushConsumer.connect_push_supplier( PushSupplierHelper.narrow( poa.servant_to_reference( thisTie )));
	}
	catch( Exception e )
	{
	    e.printStackTrace();
	}
	// initialize "queue" and start printer thread
	queue = new Hashtable();	
	printThread = new PrintThread();
    }

    /**
     * Enter a job in the printer queue
     */

    public synchronized int print( String text, String uid) 
	throws OffLine
    {
	if( offline )
	    throw new OffLine();
            
	queue.put( new Integer(jobId), new JobInfo( jobId, uid, text ));
	printThread.tell();
	return jobId++;
    }

    /**
     * Remove a job in the printer queue
     */

    public void cancel(int id, String uid ) 
	throws UnknownJobID, AlreadyPrinted
    {
	if( id > jobId || id < 0) 
	    throw new UnknownJobID();

	if( id < printIdx )
	    throw new AlreadyPrinted();

	JobInfo job = (JobInfo)queue.remove( new Integer( id ));
	if( job != null )
	{
	    if( !job.userId.equals( uid ))
		throw new org.omg.CORBA.NO_PERMISSION();

	    System.out.println("--CANCELLED JOB #" + id  + "--");

	    if( generateEvents() )
	    {
		Any cancelEvent = orb.create_any();
		PrinterStatus status = new PrinterStatus();
		status.the_job( Status.CANCELLED, new Job( id, job.userId ) );
		PrinterStatusHelper.insert( cancelEvent, status );
		try
		{
		    proxyPushConsumer.push( cancelEvent );
		}
		catch( org.omg.CosEventComm.Disconnected d )
		{
		    // ignore
		}
	    }
	}
    }

    /**
     * Sets the printer online/offline
     */

    public void setOffLine(boolean flag)
    {
	offline = flag;
	if( !offline )
	    printThread.tell();

	if( generateEvents() )
	{
	    Any lineEvent = orb.create_any();
	    PrinterStatus status = new PrinterStatus();
	    status.dummy( ( offline ? Status.OFFLINE : Status.ONLINE ), false );
	    PrinterStatusHelper.insert( lineEvent, status );
	    try
	    {
		proxyPushConsumer.push( lineEvent );
	    }
	    catch( org.omg.CosEventComm.Disconnected d )
	    {
		// ignore
	    }
	}
    }

    /**
     * Potentially release resources
     */

    public void disconnect_push_supplier() 
    {
	disconnected = true;
    }

    boolean generateEvents()
    {
	return !disconnected;
    }

    /**
     * main
     */

    static public void main (String argv[]) 
    {
	org.omg.CosEventChannelAdmin.EventChannel e = null;
	org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(argv, null);

	try 
	{
	    // initialize POA, get naming and event service references
	    POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA") );

	    NamingContextExt nc = 
		NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));	
	    e = EventChannelHelper.narrow(nc.resolve(nc.to_name("office_event.channel")));

	    // create a Printer object, implicitly activate it and advertise its presence
	    PrinterImpl printer = new PrinterImpl( e, orb, poa );
	    printer.connect();

	    org.omg.CORBA.Object printerObj = poa.servant_to_reference( printer );
	    nc.bind( nc.to_name("Printer"), printerObj);

	    // wait for requests
	    poa.the_POAManager().activate();
	    orb.run();
	} 
	catch (Exception ex) 
	{ 
	    ex.printStackTrace();
	}	
    }
}


