import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MulticastSender
{
    public static void main(String[] args) throws IOException, InterruptedException
    {
        if ( args.length != 5 )
        {
            System.out.println( String.format("Usage: java %s network_interface broadcast_ip broadcast_port msBetweenSends numPacketsToSend", MulticastSender.class.getName() ) );
            System.exit( -1 );
        }

        final int delay = Integer.parseInt( args[3] );
        final int packetsToSend = Integer.parseInt( args[4] );

        // Output multicast socket setup
        NetworkInterface outputNetworkInterface = NetworkInterface.getByInetAddress( InetAddress.getByName( args[0] ) );
        InetSocketAddress outputGroup = new InetSocketAddress( args[1], Integer.parseInt( args[2] ) );
        MulticastSocket outputSocket = new MulticastSocket( outputGroup.getPort() );
        outputSocket.setNetworkInterface( outputNetworkInterface );
        outputSocket.setTimeToLive( 25 );
        outputSocket.setTrafficClass( 0x10 ); // 0x10 IPTOS_LOWDELAY

        byte[] buffer = new byte[62];
        DatagramPacket out = new DatagramPacket( buffer, buffer.length, outputGroup.getAddress(), outputGroup.getPort() );

        System.out.println("Starting warmup phase...");
        for (int i=0; i<50000; i++)		// send a bunch of packets to warm up loop
        {
            outputSocket.send( out );
            System.nanoTime();
        }
        
        System.out.println("Starting timing phase...");
        long[] startTimesNanos = new long[ packetsToSend ];
        long[] endTimesNanos = new long[ packetsToSend ];
        for (int i=0; i<packetsToSend; i++)
        {
            long start = System.nanoTime();			// time how long it takes to send
            outputSocket.send( out );
            long end = System.nanoTime();
            startTimesNanos[i] = start;
            endTimesNanos[i] = end;

            Thread.sleep( delay );
        }
        System.out.println("Done with timing phase...");

        List<Double> list = new ArrayList<Double>( 1000 );
        for (int i=0; i<packetsToSend; i++)
        {
            list.add( (endTimesNanos[i] - startTimesNanos[i] ) / 1000.0 );
        }
        Collections.sort(list);
        double medianTime = list.get( list.size() / 2 );
        System.out.println( String.format("Sent %s packets %s ms apart, median send time: %.3f us", list.size(), delay, medianTime));
    }
}
