package de.tu_darmstadt.tk.SmartHomeNetworkSim.core.protocols.packets;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Packet;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Port;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.util.ICMPv6Checksum;
/**
 * IcMPv6 Ping Packages
 * 
 * @see <a href="http://www.networksorcery.com/enp/protocol/icmpv6.htm">http://www.networksorcery.com/enp/protocol/icmpv6.htm</a>
 * @see <a href="http://www.networksorcery.com/enp/rfc/rfc4443.txt">RFC4443</a>
 * @author Andreas T. Meyer-Berg
 */
public class Ping_packet extends Packet {
	
	/**
	 * Echo Request type
	 */
	public static byte EchoRequest = 0x10; // 128;
	/**
	 * Echo reply type
	 */
	public static byte EchoReply = 0x11; //129;
	/**
	 * Type of the packet
	 */
	private byte type;
	/**
	 * Code byte
	 */
	private byte code = 0x00;
	/**
	 * Checksum
	 */
	private byte[] checksum = new byte[]{0x00,0x00};
	/**
	 * Identifier
	 */
	private byte[] id = new byte[]{0x00,0x00};
	/**
	 * Sequence Number
	 */
	private byte[] sequenceNumber = new byte[]{0x00,0x00};
	/**
	 * Payload data
	 */
	private byte[] data = "abcdefghijklmnopqrstuvwabcdefghi".getBytes();
	
	/*
	 *  0                   1                   2                   3
     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     * |     Type      |     Code      |          Checksum             |
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     * |           Identifier          |        Sequence Number        |
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     * |     Data ...
     * +-+-+-+-+-
	 */
	
	/* Failure codes on response
	 * 0 network unreachable
	 * 1 host unreachable
	 * 2 protocol unreachable
	 * 3 port unreachable
	 * 4 fragmentation needed but don‘t fragment bit set
	 * 5 source route failed
	 * 6 destination network unknown
	 * 7 destination host unknown
	 * 8 source host isolated
	 * 9 destination network administratively prohibited
	 * 10 destination host administratively prohibited
	 * 11 network unreachable for requested type of service
	 * 12 host unreachable for requested type of service
	 * 13 communication administratively prohibited by filtering
	 * 14 host precedence violation
	 * 15 precedence cutoff in effect
	 */
	
	/**
	 * Ping package
	 * @param timestamp time the package was sent
	 * @param source source of the package
	 * @param destination destination of the package
	 * @param type type, should be EchoRequest, EchoReply
	 * @param identifier number in the sequence, specified by sender
	 * @param sequenceNumber number in the sequence, specified by sender
	 */
	public Ping_packet(long timestamp, Port source, Port destination, byte type, short identifier, short sequenceNumber) {
		super(timestamp, source, destination);
		this.type = type;
		this.id = new byte[2];
		id[0] =  (byte)(identifier & 0xff);
		id[1] =  (byte)((identifier>>8) & 0xff);
		this.sequenceNumber[0] = (byte)(sequenceNumber & 0xff);
		this.sequenceNumber[1] = (byte)((sequenceNumber >> 8) & 0xff);
	}
	
	/**
	 * Ping package without identifier
	 * @param timestamp time the package was sent
	 * @param source source of the package
	 * @param destination destination of the package
	 * @param type type, should be EchoRequest, EchoReply
	 * @param sequenceNumber number in the sequence, specified by sender
	 */
	public Ping_packet(long timestamp, Port source, Port destination, byte type, short sequenceNumber) {
		super(timestamp, source, destination);
		this.type = type;
		this.id = new byte[]{0x00, 0x00};
		this.sequenceNumber[0] = (byte)(sequenceNumber & 0xff);
		this.sequenceNumber[1] = (byte)((sequenceNumber >> 8) & 0xff);
	}

	@Override
	public byte[] dumpBytes() {
		byte[] bytes = new byte[8+data.length];
		//Checksum cleared for calculation
		byte[] header = new byte[]{type,code,0x00,0x00,id[0],id[1],sequenceNumber[0],sequenceNumber[1]};
		int i;
		for(i = 0; i < 8; i++)
			bytes[i] = header[i];
		for(int j=0;j<data.length;j++){
			bytes[i]=data[j];
			i++;
		}
		/**
		 * checksum
		 */
		long sum = ICMPv6Checksum.calculateChecksum(bytes);
		checksum[0] = (byte)(sum & 0xff);
		checksum[1] = (byte)(sum>>8 & 0xff);
		
		bytes[2] = checksum[0];
		bytes[3] = checksum[1];
		
		return bytes;
	}

	@Override
	public String getTextualRepresentation() {
		return "[Ping: "+getType()+"; time:"+timestamp+"; source:"+source.getOwner().getName()+"; destination:"+destination.getOwner().getName()+"; Payload: "+getPayload()+"]";
	}

	@Override
	public String getPayload() {
		return new String(data);
	}
	
	/**
	 * Type as String
	 * 
	 * @return String representation of the Type
	 */
	private String getType(){
		if(type == EchoRequest)
			return "EchoRequest";
		if(type == EchoReply)
			return "EchoReply";
		return "unknown";
	}
	
	/**
	 * Create package and checksum
	 * @param args unused
	 */
	public static void main(String[] args) {
		Ping_packet pinPacket = new Ping_packet(1000, null, null, EchoRequest, (short) 12);
		byte[] bytes = pinPacket.dumpBytes();
		for(int i = 0; i<bytes.length; i++)
			System.out.println(bytes[i]);
	}

	@Override
	public String getProtocolName() {
		return "ICMPv6 Ping";
	}
}
