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

import edu.nps.moves.dis.*;
import edu.nps.moves.disutil.DisTime;
import edu.nps.moves.disutil.PduFactory;

/**
 * listens for TCP connection from AngelBlankReceiverAtoTCP and sends ESPDU in IEEE binary format. 
 *
 * @author Angelopoulos/Blankenbeker
 * @version 8 MAR 2018
 */
public class AngelBlankEspduTCPReceiverBSenderA
{
    public static final int NUMBER_TO_SEND = 5000;

    public enum NetworkMode{UNICAST, MULTICAST, BROADCAST};
    public static final int MAX_PDU_SIZE = 8192;
    /** default multicast group we send on */
    public static final String DEFAULT_MULTICAST_GROUP="239.1.2.4";
    public static final int TCP_DESTINATION_PORT = 2998;
    /** Port we send on */
    public static final int DIS_DESTINATION_PORT = 3000;

public static void main(String args[])
{
    /** an entity state pdu */
    EntityStatePdu espdu = new EntityStatePdu();
    MulticastSocket socket = null;
    DisTime disTime = DisTime.getInstance();
    int alternator = -1;
    
    int port = DIS_DESTINATION_PORT;
    NetworkMode mode = NetworkMode.MULTICAST;
    InetAddress destinationIp = null;
    PduFactory pduFactory = new PduFactory();
    
    try
    {
        destinationIp = InetAddress.getByName(DEFAULT_MULTICAST_GROUP);
    }
    catch(Exception e)
    {
        System.out.println(e + " Cannot create multicast address");
        System.exit(0);
    }
    
    // All system properties, passed in on the command line via -Dattribute=value
    Properties systemProperties = System.getProperties();
    
    // IP address we send to
    String destinationIpString = systemProperties.getProperty("destinationIp");
    
    // Port we send to, and local port we open the socket on
    String portString = systemProperties.getProperty("port");
    
    // Network mode: unicast, multicast, broadcast
    String networkModeString = systemProperties.getProperty("networkMode"); // unicast or multicast or broadcast
    // Set up a socket to send information
    try
    {
 
        // Port we send to
        if(portString != null)
            port = Integer.parseInt(portString);
        
        socket = new MulticastSocket(DIS_DESTINATION_PORT);
        
        // Where we send packets to, the destination IP address
        if(destinationIpString != null)
        {
            destinationIp = InetAddress.getByName(destinationIpString);
        }

        // Type of transport: unicast, broadcast, or multicast
        if(networkModeString != null)
        {
            if(networkModeString.equalsIgnoreCase("unicast"))
                mode = NetworkMode.UNICAST;
            else if(networkModeString.equalsIgnoreCase("broadcast"))
                mode = NetworkMode.BROADCAST;
            else if(networkModeString.equalsIgnoreCase("multicast"))
            {
                mode = NetworkMode.MULTICAST;
                if(!destinationIp.isMulticastAddress())
                {
                    throw new RuntimeException("Sending to multicast address, but destination address " + destinationIp.toString() + "is not multicast");
                }
                
                socket.joinGroup(destinationIp);
                
            }
        } // end networkModeString
    }
    catch(Exception e)
    {
        System.out.println("Unable to initialize networking. Exiting.");
        System.out.println(e);
        System.exit(-1);
    }

    try
    {
        int connectionCount = 0;
        DatagramSocket ds = new DatagramSocket(TCP_DESTINATION_PORT);

        while(true){
            try
            {
                byte buffer[] = new byte[MAX_PDU_SIZE];
                DatagramPacket tcpPacket = new DatagramPacket(buffer, buffer.length);
                ds.receive(tcpPacket);
                //Socket clientConnection = serverSocket.accept();
                connectionCount++;
                System.out.println("Current PDUs transferred over TCP: "+ connectionCount);
                List<Pdu> pduBundle = pduFactory.getPdusFromBundle(tcpPacket.getData());
                //System.out.println("Bundle size is " + pduBundle.size());
                Iterator it = pduBundle.iterator();

                while(it.hasNext()){
                    //System.out.println("Entity ID transferred: ");
                    Pdu aPdu = (Pdu)it.next();
                    //System.out.print("got PDU of type: " + aPdu.getClass().getName());
                    if(aPdu instanceof EntityStatePdu){
                        EntityID eid = ((EntityStatePdu)aPdu).getEntityID();
                        System.out.println("Entity ID transferred: "+eid.getEntity());
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        DataOutputStream dos = new DataOutputStream(baos);
                        aPdu.marshal(dos);
                        byte[] data = baos.toByteArray();
                        DatagramPacket udpPacket = new DatagramPacket(data, data.length, InetAddress.getByName(DEFAULT_MULTICAST_GROUP), DIS_DESTINATION_PORT);
                        socket.send(udpPacket); 
                    }       
                }
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
        }
    }
    catch(Exception e)
    {
        System.out.println(e);
    }
        
}

 /**
    * A number of sites get all snippy about using 255.255.255.255 for a bcast
    * address; it trips their security software and they kick you off their 
    * network. (Comcast, NPS.) This determines the bcast address for all
    * connected interfaces, based on the IP and subnet mask. If you have
    * a dual-homed host it will return a bcast address for both. If you have
    * some VMs running on your host this will pick up the addresses for those
    * as well--eg running VMWare on your laptop with a local IP this will
    * also pick up a 192.168 address assigned to the VM by the host OS.
    * 
    * @return set of all bcast addresses
    
   public static Set<InetAddress> getBroadcastAddresses()
   {
       Set<InetAddress> bcastAddresses = new HashSet<InetAddress>();
       Enumeration interfaces;
       
       try
       {
           interfaces = NetworkInterface.getNetworkInterfaces();
           
           while(interfaces.hasMoreElements())
           {
               NetworkInterface anInterface = (NetworkInterface)interfaces.nextElement();
               
               if(anInterface.isUp())
               {
                   Iterator it = anInterface.getInterfaceAddresses().iterator();
                   while(it.hasNext())
                   {
                       InterfaceAddress anAddress = (InterfaceAddress)it.next();
                       if((anAddress == null || anAddress.getAddress().isLinkLocalAddress()))
                           continue;
                       
                       //System.out.println("Getting bcast address for " + anAddress);
                       InetAddress abcast = anAddress.getBroadcast();
                       if(abcast != null)
                        bcastAddresses.add(abcast);
                   }
               }
           }
           
       }
       catch(Exception e)
       {
           e.printStackTrace();
           System.out.println(e);
       }
       
       return bcastAddresses;   
   }
**/
}