package cz.vutbr.feec.packets;

/**
 * The Class PacketUtils.
 * 
 * @brief This class provides generic packet assembly and disassembly functions, which
 * are used in various parts of the project e.g. assembling and disassembling
 * RTP and RTCP packets etc.
 */

public class PacketUtils {
	
	/**
	 * Bounds packet length to 32 bits.
	 * 
	 * @param length the length
	 * 
	 * @return bounded length on octets count
	 */
	public synchronized static int bound32Bit(int length) {
		int result = length;
		int rest = length % 4;
		if(rest != 0){
			result += 4 - rest;
		}
		return result;
	}
	/**
	 * Bounds packet length to 32 bits.
	 * 
	 * @param length the length
	 * 
	 * @return bounded length on octets count
	 */
	public synchronized static int bound32(int length) {
		int result = length;
		int rest = length % 4;
		if(rest != 0){
			result += 4 - rest;
		}
		return result;
	}
	public synchronized static int bound32(byte[] buffer, int offset, int length) {
		int result = length;
		int rest = length % 4;
		if(rest != 0){
			result += 4 - rest;
		}
		// mazu prazdna mista
		for (int i = length+1; i <= result; i++) {
			buffer[offset + i] = 0;
		}
		return result;
	}
	public static int copy(byte[] src, byte[] dst, int dstPos, int length) {
		System.arraycopy(src, 0, dst, dstPos, length );
		return length;
	}
	@Deprecated
	public static int copy(byte[] src, int dstPos, byte[] dst) {
		return copy(src, dst, dstPos, src.length );
	}
	
	/**
	 * Append two byte arrays. Appends packet B at the end of Packet A (Assuming
	 * Bytes as elements). Returns packet ( AB ).
	 * 
	 * @param packetB The second packet.
	 * @param packetA The first packet.
	 * 
	 * @return The desired packet which is A concatenated with B.
	 */
	@Deprecated
	public synchronized static byte[] append(byte[] packetA, byte[] packetB) {
		// Create a new array whose size is equal to sum of packets
		// being added
		byte packetAB[] = new byte[packetA.length + packetB.length];

		// First paste in packetA
		for (int i = 0; i < packetA.length; i++)
			packetAB[i] = packetA[i];

		// Now start pasting packetB
		for (int i = 0; i < packetB.length; i++)
			packetAB[i + packetA.length] = packetB[i];

		return packetAB;
	}

	/**
	 * Check size.
	 * 
	 * @param value the value
	 * @param noOfBits the no of bits
	 * 
	 * @return true, if check size
	 */
	public static boolean checkSize(long value, int noOfBits) {
		long i = 1;
		i = i << noOfBits;
		return  i > value && value >=0;
	}
	
	/**
	 * Convert signed int to long by taking 2's complement if necessary.
	 * 
	 * @param intToConvert The signed integer which will be converted to Long.
	 * 
	 * @return The unsigned long representation of the signed int.
	 */

	public synchronized static long convertSignedIntToLong(int intToConvert) {
		int in = intToConvert;

		in = (in << 1) >> 1;

		long Lin = in;
		Lin = Lin + 0x7FFFFFFF;

		return Lin;
	}

	/**
	 * Convert 64 bit long to n bytes.
	 * @Deprecated Use setOctet2 setOctet3 or setOctet4 instead
	 * 
	 * @param ldata The long from which the n byte array will be constructed.
	 * @param numberOfBytes The desired number of bytes to convert the long to.
	 * 
	 * @return The desired byte array which is populated with the long value.
	 */
	@Deprecated
	public synchronized static byte[] longToBytes(long longData, int numberOfBytes) {
		byte buff[] = new byte[numberOfBytes];

		for (int i = numberOfBytes - 1; i >= 0; i--) {
			// Keep assigning the right most 8 bits to the
			// byte arrays while shift 8 bits during each iteration
			buff[i] = (byte) longData;
			longData = longData >> 8;
		}
		return buff;
	}

	/**
	 * Copy all bytes 'from' to 'to'. Starting on 'to[startIndex]'.
	 * @Deprecated Use setOctet2 setOctet3 or setOctet4 instead
	 * 
	 * @param to the to
	 * @param from the from
	 * @param startIndex the start index
	 */
	@Deprecated
	public synchronized static void copyBytes(byte[] to, byte[] from,
			int startIndex) {
		for (int i = 0; i < from.length; ++i) {
			to[startIndex + i] = from[i];
		}
	}

	/**
	 * Copy bytes.
	 * @Deprecated Use setOctet2 setOctet3 or setOctet4 instead
	 * 
	 * @param to the to
	 * @param length the length
	 * @param from the from
	 * @param startIndex the start index
	 */
	@Deprecated
	public synchronized static void copyBytes(byte[] to, byte[] from, int length, 
			int startIndex) {
		for (int i = 0; i < length; ++i) {
			to[startIndex + i] = from[i];
		}
	}
	
	/**
	 * Calculate number of octets required to fit the given number of octets
	 * into 32 bit boundary.
	 * 
	 * @param LengthOfUnpaddedPacket the Length of unpadded packet
	 * 
	 * @return The required number of octets which must be appended to this
	 * packet to make it fit into a 32 bit boundary.
	 */

	public synchronized static int calculatePadLength(int LengthOfUnpaddedPacket) {
		// Determine the number of 8 bit words required to fit the packet in 32
		// bit boundary
		int remain = (int) Math.IEEEremainder(LengthOfUnpaddedPacket,
				4);

		int PadLen = 0;

		// e.g. remainder -1, then we need to pad 1 extra
		// byte to the end to make it to the 32 bit boundary
		if (remain < 0)
			PadLen = Math.abs(remain);

		// e.g. remainder is +1 then we need to pad 3 extra bytes
		else if (remain > 0)
			PadLen = 4 - remain;

		return (PadLen);

	}
	
	/**
	 * Get 1 octet. (respects Network byte order)
	 * 
	 * @param rawData the raw data
	 * @param offset the offset
	 * 
	 * @return the octet1
	 * @throws PacketParseException 
	 */
	public static final int getOctet1(byte[] buffer, int offset) throws PacketParseException{
		if(buffer.length-1 < offset) {
			throw new PacketParseException("Exceeded buffer length");
		}
		return (buffer[offset] & 0xff);
	}
	
	/**
	 * get 2 octets. (respects Network byte order)
	 * 
	 * @param buffer the raw data
	 * @param offset the offset
	 * 
	 * @return the octet2
	 */
	public static final int getOctet2(byte[] buffer, int offset) throws PacketParseException{
		if(buffer.length-2 < offset) {
			throw new PacketParseException("Exceeded buffer length");
		}
		// from network byte order to host byte order
		return ((buffer[offset] & 0xff) << 8 )
		| ((buffer[offset+1] & 0xff) );
	}
	
	/**
	 * Get 4 octets. (respects Network byte order)
	 * 
	 * @param rawData the raw data
	 * @param offset the offset
	 * 
	 * @return the octet4
	 * @throws PacketParseException 
	 */
	public static final long getOctet4(byte[] buffer, int offset) throws PacketParseException {
		if(buffer.length-4 < offset) {
			throw new PacketParseException("Exceeded buffer length");
		}
		long val = 
		  (((long)buffer[offset + 0] & 0xff) << 24) 
		| (((long)buffer[offset + 1] & 0xff) << 16) 
		| (((long)buffer[offset + 2] & 0xff) << 8) 
		| ((long)buffer[offset + 3] & 0xff);
		return val & 0xffffffff;
	}
	
	
	
	/**
	 * Set 8 octets. (respects Network byte order)
	 * 
	 * @param value the value
	 * @param buffer the raw data
	 * @param offset the offset
	 */
	@Deprecated
	public static final void setOctet8(byte[] buffer, int offset, long value) {
		buffer[offset+7] = (byte)(value >> 56);
		buffer[offset+6] = (byte)(value >> 48);
		buffer[offset+5] = (byte)(value >> 40);
		buffer[offset+4] = (byte)(value >> 32);
		buffer[offset+3] = (byte)(value >> 24);
		buffer[offset+2] = (byte)(value >> 16);
		buffer[offset+1] = (byte)(value >> 8);
		buffer[offset]   = (byte)(value);
	}
	
	/**
	 * Get 8 octets. (respects Network byte order)
	 * 
	 * @param buffer the raw data
	 * @param offset the offset
	 * 
	 * @return the octet8
	 */
	@Deprecated
	public static final long getOctet8(byte[] buffer, int offset) {
		return  
        ((buffer[7 + offset] & 0xffl ) << 56) 
		| ((buffer[6 + offset] & 0xffl ) << 48) 
		| ((buffer[5 + offset] & 0xffl ) << 40) 
		| ((buffer[4 + offset] & 0xffl ) << 32) 
		| ((buffer[3 + offset] & 0xffl ) << 24) 
		| ((buffer[2 + offset] & 0xffl)  << 16) 
		| ((buffer[1 + offset] & 0xffl)  << 8) 
		| (buffer[0 + offset] & 0xffl);
	}
	
	/**
	 * Set 2 octets. (respects Network byte order)
	 * 
	 * @param value the value
	 * @param buffer the raw data
	 * @param offset the offset
	 */
	public static final void setOctet2(byte[] buffer, int offset, int value) {
		// network byte order
		buffer[offset +1] = (byte)((value) & 0xff);
		buffer[offset +0] = (byte)((value & 0xff00) >> 8);
	}
	
	public static final void setOctet2(byte[] buffer, int offset, long value) {
		// network byte order
		buffer[offset +1] = (byte)((value) & 0xff);
		buffer[offset+ 0] = (byte)((value & 0xff00) >> 8);
	}
	/**
	 * Set 4 octets. (respects Network byte order)
	 * 
	 * @param value the value
	 * @param buffer the raw data
	 * @param offset the offset
	 */
	public static final void setOctet4(byte[] buffer, int offset, long value) {
		buffer[offset+0] = (byte)(value >> 24);
		buffer[offset+1] = (byte)(value >> 16);
		buffer[offset+2] = (byte)(value >> 8);
		buffer[offset+3] = (byte)(value);
	}
	
	/**
	 * Set 2 octets. (respects Network byte order)
	 * 
	 * @param value the value
	 * @param buffer the raw data
	 * @param offset the offset
	 */
	public static final void setOctet3(byte[] buffer, int offset, long value) {
		// network byte order
		buffer[offset+2] = (byte)((value) & 0xff);
		buffer[offset+1] = (byte)((value & 0xff00) >> 8);
		buffer[offset+0] = (byte)((value & 0xff0000) >> 16);
	}
	public static final int setOctets(byte[] buffer, int offset, String value, int length) {
		
		byte[] valueB = value.getBytes();
		
		for (int i = 0; i < length; i++) {
			
			buffer[offset + i] = valueB[i];
			
		}
		return (length/4);
	}
	
	
	public static final String getOctets(byte[] buffer, int startPos, int lengthOfText){
		
		byte[] value = new byte[lengthOfText];
		for (int i = 0; i < lengthOfText; i++) {
			value[i]=buffer[i+startPos];
		}
		String text = new String(value);
		return text ;
	}
	/**
	 * Set 1 octets. (respects Network byte order)
	 * 
	 * @param value the value
	 * @param buffer the raw data
	 * @param offset the offset
	 */
	public static final void setOctet1(byte[] buffer, int offset, long value) {
		// network byte order
		buffer[offset] = (byte)((value) & 0xff);
	}
	/**
	 * get 2 octets. (respects Network byte order)
	 * 
	 * @param buffer the raw data
	 * @param offset the offset
	 * 
	 * @return the octet3
	 */
	public static final long getOctet3(byte[] buffer, int offset) {
		// from network byte order to host byte order
		return (buffer[offset+2] & 0xff) 
		| ((buffer[offset+1] & 0xff) <<8)
		| ((buffer[offset+0] & 0xff) <<16);
	}
}