/* DistributedWaveUDP.java */

package distributedwave;

import java.net.InetAddress;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.SocketException;
import java.io.IOException;

/** Concrete implementation of abstract class DistributedWave that communicates 
 *  with neighbors via datagram packets using UDP.
 *
 *  Copyright (c) 2010, 2011 - Russell C. Bjork
 */
public class DistributedWaveUDP extends DistributedWave
{
    /** Constructor
     *
     *  @param leftNeighbor the left neighbor specified by the user - null
     *         if no neighbor was specified at startup
     *  @param rightNeighbor the right neighbor specified by the user - null
     *         if no neighbor was specified at startup
     *  @exception SocketException if there is a problem creating a socket
     */
    public DistributedWaveUDP(InetAddress leftNeighbor,
                              InetAddress rightNeighbor) throws SocketException

    {
        super();

        // Record IPs for neighbors

        neighborAddress[LEFT] = leftNeighbor;
        neighborAddress[RIGHT] = rightNeighbor;
        
        // Create sockets for communicating with the two neighbors. 

        communicateWithNeighborSocket[LEFT] = 
                new DatagramSocket(COMMUNICATE_WITH_NEIGHBOR_PORT[LEFT]);
        communicateWithNeighborSocket[RIGHT] =
                new DatagramSocket(COMMUNICATE_WITH_NEIGHBOR_PORT[RIGHT]);
    }

    /** Send a message to a neighbor
     *
     *  @param side the side of send to
     *  @param waveMessage the message describing this wave to be sent to the
     *         neighbor
     */
    void sendToNeighbor(int side, byte [] waveMessage)
    {
        if (neighborAddress[side] != null)
        {
            // If we are sending to a left neighbor, we need to use that
            // neighbors right port because the neighbor sees us as its right
            // neighbor and vice versa

            DatagramPacket sendPacket = new DatagramPacket(
                waveMessage,
                waveMessage.length,
                neighborAddress[side],
                COMMUNICATE_WITH_NEIGHBOR_PORT[1-side]);

            try
            {
                communicateWithNeighborSocket[side].send(sendPacket);
            }
            catch(IOException e)
            {
                System.err.println("Exception sending to neighbor on " +
                                    SIDE_NAME[side] + ": " + e);
                System.exit(1);
            }
        }
    }
    
    /** Receive a message from a neighbor.  This method blocks until a
     *  message is received
     *
     *  @param side the side to receive from
     *  @return the message received from this neighbor
     *  @exception IOException if an exception was thrown during communication
     */
    byte [] receiveMessage(int side)
    {
        DatagramPacket receivePacket = new DatagramPacket(
            new byte[MESSAGE_LENGTH],
            MESSAGE_LENGTH);

        try
        {
            communicateWithNeighborSocket[side].receive(receivePacket);
        }
        catch(IOException e)
        {
            System.err.println("Exception on receiving from neighbor on " +
                                SIDE_NAME[side] + ": " + e);
            System.exit(1);
        }

        neighborAddress[side] = receivePacket.getAddress();
        return receivePacket.getData();
    }

    /** IPs for neighbors
     */
    private InetAddress [] neighborAddress = new InetAddress[2];

    /** Sockets used in communication.  
     */
    private DatagramSocket []
            communicateWithNeighborSocket = new DatagramSocket[2];
}
