// file: $ISIP_IFC/class/java/ConnectionManagerDemoNetworkHandler.java
// version: $Id: DemoNetworkHandler.java 10231 2005-09-13 14:29:38Z stanley $

// import necessary java libraries
//
import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
 
/** 
 * This class gets connected to the server using the specified port
 * number and IP address.  The class handles all the network traffic
 * between the client and server programs.  It creates the new socket
 * data streams.  When it receives a message, it passes it on to the
 * associated <code> DemoNetworkListener </code>.  The class can also
 * send messages using the <code> sendMessage </code> routine.  This
 * class is functionally similar to the <code> DemoClientHandler
 * </code> class on the server side.
 */
public class DemoNetworkHandler {

    //-------------------------------------------------------------------------
    //
    // public constants
    //
    //-------------------------------------------------------------------------


    //------------------------------------------------------------------------
    //
    // protected data
    //
    //-----------------------------------------------------------------------
  
    /**
     * Socket to use in connection with server
     */
    protected Socket socket_d;

    /**
     * Handler used to receive messages from server
     */
    protected IncomingMessageHandler incoming_message_handler_d;

    /**
     * Handler used to send messages to server.
     */
    protected OutgoingMessageHandler outgoing_message_handler_d;

    /**
     * Listener used to process received messages.
     */
    protected DemoNetworkListener listener_d;

    /**
     * If <code> True </code> then the <code> NetworkHandler </code>
     * is connected to the server.
     */
    protected boolean connected_d;

    //-------------------------------------------------------------------------
    //
    // public methods
    //
    //-------------------------------------------------------------------------
       
    /**
     * Constructs a new network handler using the specified port
     * number and IP address.
     *
     * <code> NetworkListener </code> interface.
     */
    public DemoNetworkHandler() {
	
	// create the object
	//
	socket_d = null;
	listener_d = null;
	connected_d = false;
    }
    
    public boolean init(String address_a, int port_a, 
			DemoNetworkListener listener_a) {
        try {
	    this.listener_d = listener_a;
	    socket_d = new Socket(address_a, port_a);
	    
	    // creating in/out data stream
	    //
	    DataInputStream in = new DataInputStream
		(socket_d.getInputStream());
	    DataOutputStream out = new DataOutputStream
		(socket_d.getOutputStream());
	    
	    // creating incoming and outgoing message handler
	    //    
	    incoming_message_handler_d = new IncomingMessageHandler(in);
	    outgoing_message_handler_d = new OutgoingMessageHandler(out);
	    
	    // set the connect to true
	    //
	    connected_d = true;  
	}
        catch (IOException e)  {            
	    System.out.println("Unable to connect: " + e);
	    
	    JFrame frame = new JFrame("Warning"); 
	    String warningMessage = new 
		String("Could not connect to Process Manager. Please check the Process Manager Server first.");
	    JOptionPane.showMessageDialog(frame, warningMessage);
	    
	    if (listener_d != null)
		listener_d.networkDisconnected(new DemoNetworkEvent
					       (this, 
						DemoNetworkEvent.NETWORK_DISCONNECTED,
						null));
	    return false;
	}
	
	return true;
    }
    
    // declare inner classes
    //

    /**
     * Inner class of <code> DemoNetworkHandler </code>.  Creates a
     * thread that reads from the passed <code>DataInputStream</code>
     * and notifies the <code> DemoNetworkListener </code> associated
     * with the <code> DemoNetworkHandler </code> when a message is
     * received.
     */
    public class IncomingMessageHandler implements Runnable  {
	
	// local variables
	//
	protected DataInputStream in_stream_d;
	protected Thread receiver_d;

	// declare class constructors
	//
	/**
	 * Creates and starts a new <code>Thread</code> to read from
	 * the passed <code>DataInputStream</code>.
	 *
	 * @param in_a the input stream for communication from the
	 * server.
	 */
        public IncomingMessageHandler(DataInputStream in_a) {
            in_stream_d = in_a;
            receiver_d = new Thread (this);
            receiver_d.start();
        }
	
	/** 
	 * Continually tries to read from the <code>DataInputStream</code>
	 * and passes any received network events to the associated
	 * <code>NetworkListener</code> through the listener's
	 * <code>networkMessageReceived()</code> method.  
	 */
        public void run() {
	    
            Thread thisThread = Thread.currentThread();
            while (receiver_d == thisThread) {
                try {
                    String message = in_stream_d.readUTF();
                    listener_d.networkMessageReceived(new DemoNetworkEvent
						      (this, DemoNetworkEvent.NETWORK_MESSAGE_RECEIVED,
						       message));
                }
                catch (IOException e) {
		    disconnect();
		}
            }
        }
        
	/**
	 * Sets the <code>Thread receiver</code> to null.  This
	 * condition causes the thread to exit the while loop
	 * in the <code>run()</code> method and stop execution.
	 */
	public void destroy() {
            receiver_d = null;
        }
    }
    
    /**
     * Class: OutGoingMessageHandler
     *
     * Inner class of <code> DemoNetworkHandler </code>.  Allows
     * messages to be sent to the connected server.  Messages are
     * added to the <code> OutGoingMessageHandler </code>'s message
     * list with the <code> addMessage </code> method.  The
     */
    public class OutgoingMessageHandler implements Runnable  {
	
	// local variables
	//
        protected Thread sender_d;
        protected LinkedList<String> message_list_d;
        protected DataOutputStream out_stream_d;
	
	/**
         * Creates the <code> LinkedList </code> to hold outgoing messages
	 * and creates a new <code> Thread </code> to continually check
	 * for outgoing messages and send any that exist using the
	 * passed <code> DataOutputStream </code>.
	 *
	 * @param out_a the output stream for communication to the
	 * server.
         */
        public OutgoingMessageHandler(DataOutputStream out_a) {
            out_stream_d = out_a;
            message_list_d = new LinkedList<String>();
            sender_d = new Thread(this);
            sender_d.start();
        }
        
	/**
         * Add message to the outgoing message list.
	 *
	 * @param message_a the outgoing message.
	 *
	 * @return a boolean value indicating status.
         */
        public boolean addMessage(String message_a) {
            synchronized (message_list_d) {
                message_list_d.add(message_a);
                message_list_d.notify();
            }
	    
	    // exit gracefully
	    //
	    return true;
        }

	/** 
	 * Waits for outgoing messages to be added to the message list
	 * and sends existing messages using the <code>
	 * DataOutputStream out</code>.
	 */   
        public void run() {
            String message;
            Thread thisThread = Thread.currentThread();
            while (sender_d == thisThread) {
                synchronized (message_list_d) {
		    if (message_list_d.isEmpty() && sender_d != null) {
			try {
			    message_list_d.wait();
			}
			catch (InterruptedException e) { }
		    }
		}
                
                while (message_list_d.size() > 0) {
		    synchronized (message_list_d) {
			message = (String)message_list_d.removeFirst();
		    }
                    
                    try {
                        out_stream_d.writeUTF(message);
                    }
                    catch (IOException e) {
                        disconnect();
                    }
                }
            }        
        }
        
	/**
	 * Sets the <code> Thread sender </code> to null.  This
	 * condition causes the thread to exit the while loop in the
	 * <code> run </code> method and stop execution.
	 */
        public void destroy() {
            sender_d = null;
            
            synchronized (message_list_d)  {

		// wake up if stuck in waiting stage
		//
                message_list_d.notify(); 
            }
        }
    }

    /**
     * Destroys the <code> incomingMessageHandler </code> and <code>
     * outgoingMessageHandler </code>.  It closes the socket and
     * notifies the associated <code> DemoNetworkListener </code> that
     * the network has been disconnected.
     */
    public synchronized boolean disconnect() {

        System.out.println("Disconnect");

        if (connected_d) {

	    System.out.println("Disconnect equal to false");
	    
            connected_d = false;
            incoming_message_handler_d.destroy();
            outgoing_message_handler_d.destroy();
        
            try {
                socket_d.close();
            }
            catch(Exception e) {}
            socket_d = null;
            
            listener_d.networkDisconnected(new DemoNetworkEvent(this,
		       DemoNetworkEvent.NETWORK_DISCONNECTED, null));
        }

	// exit gracefully
	//
	return true;
    }
    
    /**
     * Adds an outgoing message to the <code> outgoingMessageHandler
     * </code>'s message list using the handlers <code> addMessage
     * </code> method.
     *
     * @return a boolean value indicating status.
     */
    public boolean sendMessage(String message_a) {

        outgoing_message_handler_d.addMessage(message_a);

	// exit gracefully
	//
	return true;
    }   
}





