package com.wiley.compbooks.brose.chapter10.office;

import org.omg.CORBA.*;
import java.util.*;
import java.io.*;
import com.wiley.compbooks.brose.chapter10.office.MeetingPackage.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POAPackage.*;

/**
 * MeetingImpl.java
 *
 * @author Gerald Brose *
 * (C) John Wiley, Inc. 2000
 */


class MeetingDefaultServant
    extends MeetingPOA
    implements java.io.Serializable
{
    private ORB orb;
    private POA poa;
    private Calendar calendar;
    private DateComparator dateComparator;

    /** constructor */

    MeetingDefaultServant(ORB orb, POA poa)
    {
	this.orb = orb;
	this.poa = poa;
	calendar = Calendar.getInstance();
	dateComparator = new DateComparator();
    }

    public static class MeetingState
	implements java.io.Serializable
    {
	/* transient data */
	transient boolean dirty = false;
	transient DigitalSecretary[] participants;
	transient DigitalSecretary organizer;
	transient Room location;
	transient byte[] oid;

	/** the actual state */
	String purpose;
	String[] participantIORs;
	String organizerIOR;
	String locationIOR;
	Date when;
	boolean cancelled = false;
	String cancelReason = null;
    }

    public void createState( byte [] oid,
			     String purpose,
			     Room location,
			     Date time,
			     DigitalSecretary organizer,
			     DigitalSecretary[] participants )
    {
	try
	{
	    String filename = ".meeting_" + new String( oid );
	    ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream ( filename ));

	    MeetingState state = new MeetingState();
	    state.purpose = purpose;
	    state.when = time;
	    state.participantIORs = new String[participants.length];
	    state.oid = oid;

	    for( int i = 0; i < participants.length; i++ )
	    {
		state.participantIORs[i] = orb.object_to_string(participants[i]);
	    }
	    state.organizerIOR = orb.object_to_string(organizer );
	    state.locationIOR =  orb.object_to_string(location );

	    out.writeObject( state );
	}
	catch( Exception e )
	{
	    e.printStackTrace();
	    throw new RuntimeException( e.getMessage());
	}
    }

    private synchronized MeetingState restoreState()
    {
	try
	{
	    byte [] oid = _object_id();
	    String filename = ".meeting_" + new String( oid );
	    ObjectInputStream in = new ObjectInputStream( new FileInputStream ( filename ));
	    MeetingState state = (MeetingState)in.readObject();

	    state.participants = new DigitalSecretary[ state.participantIORs.length];

	    for( int i = 0; i < state.participantIORs.length; i++ )
	    {
		state.participants[i] =
		    DigitalSecretaryHelper.narrow(orb.string_to_object(state.participantIORs[i]));
	    }

	    state.organizer = DigitalSecretaryHelper.narrow(orb.string_to_object(state.organizerIOR));
	    state.location = RoomHelper.narrow( orb.string_to_object(state.locationIOR));
	    state.oid = oid;

	    in.close();

	    return state;
	}
	catch( Exception e )
	{
	    e.printStackTrace();
	    throw new RuntimeException( e.getMessage());
	}
    }

    /**
     * Release the state for this object.
     */

    private synchronized void releaseState(MeetingState state)
    {
	if( doRelease( state ))
	{
	    try
	    {
		if( state.dirty )
		{
		    // save state
		    System.out.println("Saving servant state...");
		    String filename = ".meeting_" + new String( state.oid );
		    ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream ( filename ));

		    state.participantIORs = new String[state.participants.length];
		    for( int i = 0; i < state.participantIORs.length; i++ )
		    {
			state.participantIORs[i] = orb.object_to_string(state.participants[i]);
		    }
		    state.organizerIOR = orb.object_to_string(state.organizer );
		    state.locationIOR =  orb.object_to_string(state.location );

		    out.writeObject( state );
		}
				// schedule for deactivation
		System.out.println("deactivating it...");
		try
		{
		    poa.deactivate_object( state.oid );
		}
		catch( ObjectNotActive ona )
		{
		    // never mind...
		}
	    }
	    catch( Exception e )
	    {
		e.printStackTrace();
		throw new RuntimeException( e.getMessage());
	    }
	}
	else
	    System.out.println("Keeping servant active");
    }

    /**
     * The implementation of this method determines whether the
     * the current object should be deactivated or not.
     * Here, we decide depending on the actual meeting date.
     * Historical objects are always deactivated.
     */

    private boolean doRelease( MeetingState state )
    {
	calendar.setTime( new java.util.Date());
	int i = dateComparator.compare( state.when,
					new Date( calendar.get( Calendar.YEAR ),
						  Month.from_int( calendar.get( Calendar.MONTH )),
						  (short)calendar.get( Calendar.DAY_OF_MONTH ),
						  (short)calendar.get( Calendar.HOUR_OF_DAY )));
	return ( i < 0 );
    }

    public String purpose()
    {
	MeetingState state = restoreState();
	String result = state.purpose;
	releaseState(state);
	return result;
    }

    public DigitalSecretary organizer()
    {
	MeetingState state = restoreState();
	DigitalSecretary _organizer = state.organizer;
	releaseState(state);
	return _organizer;
    }

    public Room location()
    {
	MeetingState state = restoreState();
	Room _location = state.location;
	releaseState(state);
	return _location;
    }

    public Date when()
    {
	MeetingState state = restoreState();
	Date when_ = state.when;
	releaseState(state);
	return when_;
    }

    public DigitalSecretary[] getParticipants()
	throws MeetingCancelled
    {
	MeetingState state = restoreState();

	if( state.cancelled )
	{
	    releaseState(state);
	    throw new MeetingCancelled(state.cancelReason);
	}
	DigitalSecretary[] result = state.participants;
	releaseState(state);
	return result;
    }

    public void addParticipant( DigitalSecretary participant )
	throws MeetingCancelled
    {
	MeetingState state = restoreState();
	if( state.cancelled )
	{
	    releaseState(state);
	    throw new MeetingCancelled(state.cancelReason);
	}

	DigitalSecretary[] result = new DigitalSecretary[ state.participants.length + 1];
	System.arraycopy( state.participants, 0, result, 0, state.participants.length );
	result[ result.length-1] = participant;
	state.participants = result;
	state.dirty = true;
	releaseState(state);
    }

    public void removeParticipant( DigitalSecretary participant )
	throws MeetingCancelled
    {
	MeetingState state = restoreState();
	if( state.cancelled )
	{
	    releaseState(state);
	    throw new MeetingCancelled( state.cancelReason);
	}

	boolean found = false;
	for( int i = 0; i < state.participants.length; i++ )
	{
	    if( state.participants[i].equals( participant ))
	    {
		found = true;
		break;
	    }
	}

	if( ! found )
	{
	    releaseState(state);
	    return;
	}

	DigitalSecretary[] result = new DigitalSecretary[ state.participants.length - 1];
	int j =0;
	for( int i = 0; i < state.participants.length; i++ )
	{
	    if( ! state.participants[i].equals( participant ))
	    {
		result[j++] = state.participants[i];
	    }
	}

	state.participants = result;
	state.dirty = true;
	releaseState(state);
    }

    public void cancel(String reason)
	throws MeetingCancelled
    {
	MeetingState state = restoreState();

	if( state.cancelled )
	{
	    releaseState(state);
	    throw new MeetingCancelled(state.cancelReason);
	}

	state.cancelReason = reason;
	notifyParticipants("Meeting " + state.purpose + " has been cancelled.");

	state.dirty = true;
	releaseState(state);
    }

    public void relocate(Room to, Date when)
	throws MeetingCancelled, SlotAlreadyTaken
    {
	MeetingState state = restoreState();

	if( state.cancelled )
	{
	    releaseState(state);
	    throw new MeetingCancelled(state.cancelReason);
	}

	try
	{
	    to.book( state.when, state.organizer.name() );
	    state.location.cancelBooking( state.when );
	    state.when = when;
	    state.location = to;
	    notifyParticipants("Meeting " + state.purpose + " has been relocated.");
	    state.dirty = true;
	}
	catch( Exception e )
	{
	    e.printStackTrace(); // should not happen...
	}

	releaseState(state);
    }

    void notifyParticipants(String message)
    {
	MeetingState state = restoreState();

	for( int i = 0; i < state.participants.length; i++ )
	{
	    state.participants[i].notifyUser( message );
	}
	releaseState( state );
    }

}
