/* ***************************************************************************
FILE   : DMutexDemo.java
SUBJECT: Demonstrates Lamport's distributed mututal exclusion.
AUTHOR : (C) Copyright 2011 by Peter C. Chapin

Please send comments or bug reports to

     Peter C. Chapin
     Computer Information Systems
     Vermont Technical College
     Randolph Center, VT 05061
     PChapin@vtc.vsc.edu
*************************************************************************** */

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class DMutexDemo {

    //
    // This class listens to the network and deals with incoming messages. It also notices when
    // resource becomes available and prints a message on the console to that effect.
    //
    private static class NetworkListener implements Runnable {

        DatagramSocket theSocket;
        DistributedQueue dqueue;

        // The constructor initializes the NetworkListener. The socket and the distributed queue
        // must be created outside this class (since the caller needs them too).
        //
        public NetworkListener(
            DistributedQueue incomingQueue, DatagramSocket incomingSocket)
        {
            theSocket = incomingSocket;
            dqueue = incomingQueue;
        }


        // Here is the main loop of this thread.
        public void run()
        {
            boolean printFlag = true; // Used to control resource message.

            byte[] buffer = new byte[128];
            DatagramPacket incomingPacket = new DatagramPacket(buffer, 128);

            try {
                // Keep processing messages from the network.
                while (true) {
                    theSocket.receive(incomingPacket);
                    InetAddress sourceAddress = incomingPacket.getAddress();

                    // Parse the message. This needs improvement. There is minimal error
                    // handling. (Probably this should use java.util.StringTokenizer).
                    //
                    int endIndex   = 0;
                    int startIndex = endIndex;

                    while (buffer[endIndex] != (byte)':') endIndex++;
                    String messageTypeAsString =
                        new String(buffer, startIndex, endIndex - startIndex);

                    endIndex++;
                    startIndex = endIndex;
                    while (buffer[endIndex] != (byte)':') endIndex++;
                    String messageTimestampAsString =
                        new String(buffer, startIndex, endIndex - startIndex);

                    endIndex++;
                    startIndex = endIndex;
                    String messageSourceNodeAsString =
                        new String(buffer,
                            startIndex, incomingPacket.getLength() - startIndex);

                    int messageType =
                        Integer.parseInt(messageTypeAsString);
                    int messageTimestamp =
                        Integer.parseInt(messageTimestampAsString);
                    int messageSourceNode =
                        Integer.parseInt(messageSourceNodeAsString);

                    // Tell the distributed queue that we have a message.
                    dqueue.messageReceived(
                        messageType,
                        messageTimestamp,
                        messageSourceNode,
                        theSocket,
                        sourceAddress
                    );

                    // Check to see if an outstanding request can be satisfied.
                    boolean ready = dqueue.resourceReady();
                    if (ready && printFlag) {
                        System.out.println("YOU HAVE THE RESOURCE!");
                        printFlag = false;
                    }
                    else if (!ready && !printFlag) {
                        printFlag = true;
                    }
                }
            }
            catch (java.io.IOException e) {
                System.out.println("Exception caught while listening to the network: " + e);
                System.out.println("Network listener thread terminated!");
            }
        }
    }


    public static void main(String[] args)
        throws java.net.UnknownHostException
    {
        if (args.length != 1) {
            System.out.println("Expected my node number on the command line.");
            System.exit(1);
        }

        try {
            int myNodeID = Integer.parseInt(args[0]);
            System.out.println("My node number is: " + myNodeID);

            DistributedQueue dqueue = new DistributedQueue(myNodeID);
            System.out.println("Created my distributed queue");

            DatagramSocket mySocket = new DatagramSocket(9999);
            System.out.println("Created my datagram socket");

            // Start the network thread.
            NetworkListener myNetwork = new NetworkListener(dqueue, mySocket);
            Thread networkThread = new Thread(myNetwork);
            networkThread.start();

            interactWithUser(dqueue, mySocket);

            // This close causes the network listener thread to throw an exception with it tries
            // to read the socket. The network listener thread then terminates.
            //
            mySocket.close();
        }
        catch (java.net.SocketException e) {
            System.out.println("Socket exception caught: " + e);
        }
    }


    private static void interactWithUser(
        DistributedQueue dqueue, DatagramSocket mySocket)
        throws java.net.UnknownHostException
    {
        try {
            BufferedReader input = new
                BufferedReader(new InputStreamReader(System.in));

            while (true) {
                System.out.print(
                    "\nNode ready! 0=> quit, 1=> request, 2=> release. ");
                String response = input.readLine();
                int menuSelection = Integer.parseInt(response);

                if (menuSelection == 0) break;
                if (menuSelection == 1) {
                    System.out.println("Requesting resource!");
                    dqueue.requestResource(mySocket);
                }
                else if (menuSelection == 2) {
                    System.out.println("Releasing resource!");
                    dqueue.releaseResource(mySocket);
                }
            }
        }
        catch (IOException e) {
            System.out.println("Caught IOException: " + e);
            System.out.println("Aborting!");
        }
    }

}
