
/**
 * NextScreenImpl.java
 *
 *
 * Created: Mon Dec 17 09:11:07 2001
 *
 * @author <a href="mailto: "Jan Newmarch</a>
 * @version
 */

package saver;

import saver.drawable.Drawable;

import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.lookup.LookupCache;
import net.jini.core.lookup.ServiceItem;
import net.jini.lease.LeaseRenewalManager;
import java.rmi.RemoteException;
import net.jini.core.lookup.ServiceID;
import java.util.TreeMap;
import java.util.SortedMap;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.lookup.ServiceItemFilter;

public class NextScreenImpl implements NextScreen, 
				       DiscoveryListener, 
				       ServiceDiscoveryListener {

    protected LookupCache cache;
    protected ComparableID compServiceID;
    protected ServiceID serviceID;
    protected ScreenImpl thisScreen;

    private class ComparableID implements Comparable  {
	long hi;
	long lo;

	ComparableID(ServiceID id) {
	    hi = id.getMostSignificantBits();
	    lo = id.getLeastSignificantBits();
	}

	public int compareTo(Object obj) {
	    ComparableID id = (ComparableID) obj;
	    long hisHi = id.hi;
	    long hisLo = id.lo;
	    if (hi > hisHi) {
		return 1;
	    } else if (hi < hisHi) {
		return -1;
	    } else {
		if (lo > hisLo) {
		    return 1;
		} else if (lo < hisLo) {
		    return -1;
		} else {
		    return 0;
		}
	    }
	}
    }

    public NextScreenImpl() {
	
        ServiceDiscoveryManager clientMgr = null;


        try {
            LookupDiscoveryManager mgr =
                new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
                                           null /* unicast locators */,
                                           this /* DiscoveryListener */);
            clientMgr = new ServiceDiscoveryManager(mgr, 
                                                new LeaseRenewalManager());
        } catch(Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
  
        Class [] classes = new Class[] {Screen.class};
        ServiceTemplate template = new ServiceTemplate(null, 
						       classes, 
                                                       null);

        try {
            cache = clientMgr.createLookupCache(template, 
                                                null, /* no filter */ 
                                                this /* listener */);
        } catch(Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

    }

    public void setThisScreen(ScreenImpl scr) {
	thisScreen = scr;
    }

    public void setServiceID(ServiceID id) {
	serviceID = id;
	compServiceID = new ComparableID(id);
    }

     public void migrate(Drawable d) {
	 if (Saver.debug) System.out.println("migrating " + d);

	 if (! registered()) {
	     addToThisScreen(d);
	     return;
	 }

	 ServiceItem[] screens = cache.lookup(null, Integer.MAX_VALUE);

	 if(emptyCache(screens)) {
	     addToThisScreen(d);
	     return;
	 }

	 if (inCache(screens)) {
	     if (migrateToNext(screens, d)) {
	     } else {
		 addToThisScreen(d);
	     }
	 } else {
	     if (migrateToRandom(screens, d)) {
	     } else {
		 addToThisScreen(d);
	     }
	 }
	 return;
    }

    /**
     * We are registered with a LUS
     */
    public boolean registered() {
	return serviceID != null;
    }

    protected boolean emptyCache(ServiceItem[] screens) {
	if (screens == null || screens.length == 0) {
	    return true;
	}
	return false;
    }

    protected boolean inCache(ServiceItem[] screens) {
	for (int n = 0; n < screens.length; n++) {
	    if (screens[n].serviceID.equals(serviceID)) {
		return true;
	    }
	}
	return false;
    }

    public void addToThisScreen(Drawable d) {
	// Just cycle locally
	// we have to make a copy of the object
	// so we can have the original and the moved one
	// both on screen
	Drawable copy = d.copy();
	thisScreen.add(copy);
	if (Saver.debug) 
	    System.out.println("Local add of " + d + " by copy " + copy);
    }

    public boolean migrateToRandom(ServiceItem[] screens, Drawable d) {
	if (Saver.debug) System.out.println("Migrating to random");
	// well, not really random -
	// just take the first one that works
	// could be improved to the first random one that works
	for (int n = 0; n < screens.length; n++) {
	    try {
		((Screen) (screens[n].service)).add(d);
		if (Saver.debug)
		    System.out.println("Migrated to serviceID " + 
				       screens[n].serviceID);
		// no exception thrown - success
		return true;
	    } catch(RemoteException e) {
		if (Saver.debug) {
		    System.out.println("Service no good " +
				       screens[n].serviceID);
		    e.printStackTrace();
		}
		// ignore this one if it has gone away
	    }
	}
	return false;
    }

    public boolean migrateToNext(ServiceItem[] screens, Drawable d) {
	if (Saver.debug)
	    System.out.println("Migrating to next");	
	// sort the ids into order and work through them until
	// we find one that will accept the object
	TreeMap map = new TreeMap();
	for (int n = 0; n < screens.length; n++) {
	    ComparableID key = new ComparableID(screens[n].serviceID);
	    map.put(key, screens[n]);
	}
	// Collection values = map.values();

	// all elmts >= compServiceID
	SortedMap tail = map.tailMap(compServiceID);
	ServiceItem thisScreen = (ServiceItem) tail.remove(tail.firstKey());
	while ( ! tail.isEmpty()) {
	    ServiceItem item = (ServiceItem) tail.remove(tail.firstKey());
	    try {
		((Screen) (item.service)).add(d);
		System.out.println("Migrated to serviceID " + item.serviceID);
		// no exception thrown - success
		return true;
	    } catch(RemoteException e) {
		if (Saver.debug) {
		    System.out.println("Service no good " +
				       item.serviceID);
		    e.printStackTrace();
		}
		// ignore this one if it has gone away
	    }
	}

	// Hmm, none of the higher ones worked, start at the bottom
	// SortedMap head = map.headMap(serviceID);
	while ( ! map.isEmpty()) {
	    ServiceItem item = (ServiceItem) map.remove(map.firstKey());
	    if (item == null || item.service == null) {
		continue;
	    }
	    try {
		((Screen) (item.service)).add(d);
		if (Saver.debug)
		    System.out.println("Migrated to serviceID " + 
				       item.serviceID);
		// no exception thrown - success
		return true;
	    } catch(RemoteException e) {
		if (Saver.debug) {
		    System.out.println("Service no good " +
				       item.serviceID);
		    e.printStackTrace();
		}
		// ignore this one if it has gone away
	    }
	}
	return false;
    }

    public void discovered(DiscoveryEvent e) {
	if (Saver.debug) System.out.println("Registrar discovered");
    }

    public void discarded(DiscoveryEvent e) {
	if (Saver.debug) System.out.println("Registrar discarded");
    }

    public void serviceAdded(ServiceDiscoveryEvent event) {
	if (Saver.debug) 
	    System.out.println("Service added to cache " + 
			       event.getPostEventServiceItem().serviceID);
    }

    public void serviceRemoved(ServiceDiscoveryEvent event) {
    }

    public void serviceChanged(ServiceDiscoveryEvent event) {
    }
}// NextScreenImpl
