/** HelloWorld_EventNotification_Version2
 *
 *       The HyperCast HelloWorld programs demonstrate various aspects of
 *       the HyperCast API.  The programs create an overlay socket that
 *       sends and receives messages.
 *
 *       The HelloWorld_EventNotification_Version2 program is a variation of  
 *       HelloWorld_EventNotification. 
 *       The version in this file overwrites the method handler() of the 
 *       NotificationHandler class. This effectively disables the built-in 
 *       event notification process of invoking event specific handler methods 
 *       and unblocking of threads waiting on events. 
 *       
 *       This file corresponds to an example program in the HyperCast API description
 *       (see Chapter:  "API - Advanced", Section: "3"). 
 *
 * @author HyperCast Team, University of Toronto
 * @version 2006
 *
 */

import java.util.*;
import java.io.*;
import java.net.*;
import hypercast.*;
import hypercast.events.*;

public class HelloWorld_EventNotification_Version2 implements I_ReceiveCallback
{ 
	//   The string we send 
	String MyString = new String("Hello World");
	
	//   The overlay socket 
	I_OverlaySocket MySocket = null;
	
	//   The configuration object
	HyperCastConfig ConfObj = null;
	
	public static void main(String[] args)
	{
		// Create a new HelloWorld_CallBack instance
		HelloWorld_EventNotification_Version2 hw = new HelloWorld_EventNotification_Version2();
	}
	
	public void ReceiveCallback(I_OverlayMessage msg)
	{
		// Extract the payload and the logical address of the source
		byte[] data = msg.getPayload();
		String Src = msg.getSourceAddress().toString();
		
		
		// Print the payload (Skip messages sent by this program) 
		if(!msg.getSourceAddress().equals(MySocket.getLogicalAddress()))
			System.out.println("Received <"+ new String(data) + "> from logical address: " + Src + ".");
	}
	
	public HelloWorld_EventNotification_Version2 ()
	{
		//  Create a configuration object
		ConfObj = HyperCastConfig.createConfig("hypercast.xml");
		
		// Create a notification handler 
		//CallBack_notification_handler cnh = new CallBack_notification_handler();
		MyNotificationHandler_Version2 cnh = new MyNotificationHandler_Version2();
		
		// Create an overlay socket with the notification handler (second parameter)
		MySocket=ConfObj.createOverlaySocket(this, cnh);
		
		// Join the overlay network 
		MySocket.joinOverlay();
			
		// Wait until node is stable (using custom method) 
		cnh.MyWaitUntilStable();
			
		// Print the logical address */ 
		String MyLogicalAddress = MySocket.getLogicalAddress().toString();
		System.out.println("Logical address is " + MyLogicalAddress + ".");
				
		for(int i=1;i<10;i++) {

			// wait 5 seconds   
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {} 
			
			// Convert the string to a byte array and create a message 
			byte[] MyData = MyString.getBytes(); 
			I_OverlayMessage MyMessage = MySocket.createMessage(MyData);
			
			/* The message is sent to all members of the overlay */
			MySocket.sendToAll(MyMessage);
			System.out.println(" message sent to other members ...");
		}
		MySocket.closeSocket();
	}
	
}

/*
 * MyNotificaton_handler overwrites the handler() method of the NotificationHandler 
 * and supplies custom methods for blocking on events and for event handling methods
 */
class MyNotificationHandler_Version2 extends NotificationHandler 
{
	/** The object used for synchronization that makes application to wait
	 * for the overlay socket to be stable before sending and receiving data.
	 */
	Object StableGuard;
	
	// Flag indicating if overlay socket is stable 
	boolean isSTABLE = false;
	
	MyNotificationHandler_Version2() {
		super();
		
		// Create synchronization object 
		StableGuard = new Object();
	}
	
	// A custom  method to wait on the event "NODE_ISSTABLE" 
	public void MyWaitUntilStable() {
		synchronized (StableGuard)
		{
			while (!isSTABLE) {
				try 
				{
					System.out.println("CustomEventHandler: Wait for the overlay socket to become stable.");
					StableGuard.wait();
					System.out.println("CustomEventHandler: Unblock  (Node is stable).");
				}
				catch (InterruptedException ie) 
				{
				}
			}
		}
	}
	
	
	// This overwrites the handler() method of the NotificationHandler class
	// The handler() method is a dispatcher that invokes all event handling methods
	// The following handler only handles four events. 
	public void handler (NOTIFICATION_EVENT obj)
	{
		if (obj instanceof NODE_ISSTABLE) {
			NODE_ISSTABLE ne = (NODE_ISSTABLE) obj;
			System.out.println("CustomEventHandler: event NODE_ISSTABLE happens!");
			
			synchronized (StableGuard) 
			{
				isSTABLE = true;
				StableGuard.notify();
			}
		}
		else if (obj instanceof NODE_LEAVEOVERLAY) {
			NODE_LEAVEOVERLAY ne = (NODE_LEAVEOVERLAY) obj;
			System.out.println("CustomEventHandler: event NODE_LEAVEGROUP happens!");
		}
		else if (obj instanceof NODE_LOGICALADDRESSCHANGED) {
			NODE_LOGICALADDRESSCHANGED ne = (NODE_LOGICALADDRESSCHANGED) obj;
			System.out.println("CustomEventHandler: event NODE_LOGICALADDRESSCHANGED happens!");
		}
		else if (obj instanceof NODE_NEIGHBORHOODCHANGED) {
			NODE_NEIGHBORHOODCHANGED ne = (NODE_NEIGHBORHOODCHANGED) obj;
			System.out.println("CustomEventHandler: event NODE_NEIGHBORHOODCHANGED happens!");
		}
		else
			throw new IllegalArgumentException("unknown event happens!");
	}
}