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

public class GroupCommunicator{
	private MulticastSocket socket;
	private int ttl, port;
	private InetAddress group, outAdd;
	private NetworkReader reader;
	private NetworkInterface nic;

	private class NetworkReader extends Thread{
		private List<GDelta> inBuffer;
		private List<ArrayList> killBuffer;

		public NetworkReader(){
			inBuffer = Collections.synchronizedList(new ArrayList<GDelta>());
		}

		public void run(){
			while(true){
				moveDataToBuffer();
			}
		}

		public void moveDataToBuffer(){
			// Receive packet
			DatagramPacket packet = new DatagramPacket(new byte[11], 11);
			try{
				socket.receive(packet);
				
			}
			catch(IOException e){
				System.out.println("Unable to receive.");
				e.printStackTrace();
			}
			byte[] data = packet.getData();
			byte[] addr = new byte[4];
			System.arraycopy(data, 0, addr, 0, 4);
			try{
				if(!InetAddress.getByAddress(addr).equals(outAdd)){
					synchronized(inBuffer){
						inBuffer.add(new GDelta(data));
					}
				}
			}
			catch(UnknownHostException e){
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// System.out.println(inBuffer);

		}
	}

	public GroupCommunicator(String nicName, int portNum, String groupNum){

		try{
			this.nic = NetworkInterface.getByName(nicName);
			System.out.println("NIC Configured as: " + nicName);
		}
		catch(SocketException e){
			System.out.println("NIC Not Configured");
		}

		try{
			port = portNum;
			group = InetAddress.getByName(groupNum);
			socket = new MulticastSocket(port);
			socket.joinGroup(group);
			ttl = socket.getTimeToLive();
			socket.setTimeToLive(ttl);
		}
		catch(IOException e){
			System.out.println("broken");
		}

		outAdd = null;
		Enumeration<InetAddress> addresses = nic.getInetAddresses();

		System.out.println(addresses);

		// Get IPv4 address: loop through addresses until you get an address
		// 4 bytes long
		// Testing/research needed to see if it's possible for there to be
		// more than one IPv4 address per adapter.

		while(addresses.hasMoreElements()){
			byte[] add = addresses.nextElement().getAddress();
			if(add.length == 4){
				try{
					outAdd = InetAddress.getByAddress(add);

				}
				catch(UnknownHostException e){
					// TODO Auto-generated catch block
					// e.printStackTrace();
				}
			}
		}

		reader = new NetworkReader();
		reader.start();

	}

	public void sendData(GDelta delta){
		// sends data out to the group
		// Need to decide what to do with byte 4 (0-indexed), environment object
		// ID
		// Currently left null
		try{
			// Construct data array
			byte[] buf = new byte[11];
			// Insert id, x, y, and health into byte[] to be transmitted
			System.arraycopy(delta.source.getAddress(), 0, buf, 0, 4);
			buf[4] = delta.localID;
			System.arraycopy(bitwiseUtil.makeByte2FromShort(delta.x), 0, buf,
					5, 2);
			System.arraycopy(bitwiseUtil.makeByte2FromShort(delta.y), 0, buf,
					7, 2);
			buf[9] = delta.health;
			buf[10] = delta.type;

			// Construct packet, put data array into packet
			DatagramPacket packet = new DatagramPacket(buf, buf.length, group,
					port);

			// Send packet
			socket.send(packet);
		}
		catch(IOException e){
			System.out.println("Unable to transmit.");
			e.printStackTrace();
		}
	}

	public List<GDelta> flushBuffer(){
		ArrayList<GDelta> out = new ArrayList<GDelta>();
		synchronized(reader.inBuffer){
			for(GDelta g: reader.inBuffer){
				out.add(g);
			}
			reader.inBuffer.clear();
		}
		return out;
	}

	public void closeSocket(){
		try{
			reader.interrupt();
			socket.leaveGroup(group);
			socket.close();
		}
		catch(IOException e){
			e.printStackTrace();
		}

	}

	public InetAddress getInetAddress(){
		return outAdd;
	}

	public static void main(String[] args){
		// GroupCommunicator alpha = new GroupCommunicator("en1", 4446,
		// "230.0.0.1");
		// alpha.sendData(new GameElement(alpha.getInetAddress(), (byte) 0));
		// System.out.println(getInetAddress());

		// System.out.println(alpha.flushBuffer());
		// alpha.closeSocket();

		// More stuff we were trying
		/*
		 * try{ InetAddress add = NetworkInterface.getByName("eth0")
		 * .getInetAddresses().nextElement(); InetAddress.getLocalHost();
		 * 
		 * for(int i = 0; i < add.getAddress().length; i++){
		 * System.out.println(0x000000FF & ((int) add.getAddress()[i])); }
		 * 
		 * System.out.println(add.getHostAddress());
		 * System.out.println(add.getHostName());
		 * 
		 * } catch(UnknownHostException e){ e.printStackTrace(); }
		 * catch(SocketException e){
		 * 
		 * }
		 */
	}

}
