/*************************************************
 * Author: 	Evelyn Lai-Tee Cheok
 *			Department of Electrical Engineering
 *			Center for Telecommunications Research
 *			Schapiro Research Building
 *			Columbia University
 *
 * Email: 	laitee@ctr.columbia.edu
 *
 **************************************************/
import java.net.*;
import java.lang.*;
import java.io.*;
import java.util.*;
import RTP_Header;
import DataHandler;

/** RTP Packet Handler contains methods to assemble and disassemble
	incoming RTP packets received over the RTP multicast receiver socket **/
public class PacketHandler {
	MenuWindow main_parent;
	byte byteRead, byte2Read;
	RTP_Header rtp_header;
	int SSRC, timestamp, seq_num;
	int seq_numRead, timestampRead, SSRCRead;
	String data_str;
	Random random_obj = new Random(System.currentTimeMillis());

	/** Constructor function to randomly generate sequence number,
		timestamp and SSRC. The SSRC generated is stored in the
		StateInfo class for reference by other parts of the program. **/
	public PacketHandler(MenuWindow parent) {
		this.main_parent = parent;
		seq_num = (int) (random_obj.nextFloat() * 1000);
		timestamp = (int) (random_obj.nextFloat() * 2000);
		SSRC = (int) (random_obj.nextFloat() * 3000);
		parent.state_info.SSRC = SSRC;
	}

	/** Segments the RTP header from the data portion of the incoming
		RTP packet, before updating a vector for keeping track of the
		number of active senders during a particular transmission
		interval. The RTP header is then stored and can be retrieved
		using getRTP_Header. **/
	public byte[] disassemble(DatagramPacket pkt) {
		ByteArrayInputStream byteStream;
		DataInputStream dataStream;
		int data_len = pkt.getLength() - 12;
		byte buf[] = new byte[data_len];

		 byteStream = new ByteArrayInputStream(pkt.getData());
		 rtp_header = new RTP_Header(byteStream);
		 dataStream = new DataInputStream(byteStream);
		 try {
			byteRead = dataStream.readByte();
			byte2Read = dataStream.readByte();
			seq_numRead = dataStream.readUnsignedShort();
			timestampRead = dataStream.readInt();
			SSRCRead = dataStream.readInt();
			dataStream.readFully(buf, 0, data_len);
		} catch(java.io.IOException e) {
			System.out.println("IOException: " + e);
			return null;
		}

		 updateActiveSenderVec(SSRCRead);

		rtp_header.version_num = ((byteRead & 192) >>> 6);
		rtp_header.padding = ((byteRead & 32) >>> 5);
		rtp_header.extension = ((byteRead & 16) >>> 4);
		rtp_header.CSRC_count = (byteRead & 15);
		rtp_header.marker = (byte2Read >>> 7);
		rtp_header.payload_type = (byte2Read & 127);
		rtp_header.sequence_num = seq_numRead;
		rtp_header.timestamp = timestampRead;
		rtp_header.SSRC = SSRCRead;

		/* returns buf for further processing if not receiving own RTP packet,
		 * else returns null such that the data dispatcher in DataHandler 
		 * need not do further processing to update chat display panel or 
		 * whiteboard canvas */
		if (SSRCRead != SSRC)
			return (buf);
		else
			return null;
	}

	/** Checks whether the SSRC read from the incoming packet is
		already present in the vector. If not, add on to the vector
		and increment the variable : activeSenders. **/

	public void updateActiveSenderVec(int SSRCRead) {
		Integer Int_SSRCRead = new Integer(SSRCRead);
		if (main_parent.state_info.activeSenderVec.isEmpty() == true) {
			main_parent.state_info.activeSenders = 1;
			main_parent.state_info.activeSenderVec.addElement(Int_SSRCRead);
			System.out.println(" activeSenders received = " +
				      main_parent.state_info.activeSenders);
			return;
		}
		if (main_parent.state_info.activeSenderVec.contains(Int_SSRCRead) == false) {
			main_parent.state_info.activeSenderVec.addElement(Int_SSRCRead);
			main_parent.state_info.activeSenders++;
			System.out.println(" activeSenders received = " +
				      main_parent.state_info.activeSenders);
		}
	}

	/** Assembles data passed from Chat Tool or Whiteboard module with
		RTP header. Function also pads data to 32-bit boundary and sets
		the padding bit of the header to 1 accordingly.
		
		@ return array of bytes pertaining to the assembled packet. **/
	public byte[] assemble(byte[] data_buf) {
		byte ver = (byte) 2;
		byte p = (byte) 1;
		byte extension = (byte) 1;
		byte CC = (byte) 6;
		byte M = (byte) 0;
		byte PT = (byte) 88;
		byte first_byte, second_byte;

		ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
		 first_byte = (byte) (((ver << 6) & 192) | ((p << 5) & 32) | ((extension << 4) & 16) | (CC & 15));
		 second_byte = (byte) (((M << 7) & 128) | (PT & 127));
		DataOutputStream dataStream = new DataOutputStream(byteStream);
		 try {
			dataStream.writeByte(first_byte);
			dataStream.writeByte(second_byte);
			dataStream.writeShort(seq_num);
			dataStream.writeInt(timestamp);
			dataStream.writeInt(SSRC);
			dataStream.write(data_buf, 0, data_buf.length);
		} catch(java.io.IOException e) {
			System.out.println(" IOException in writing out : " + e);
			return (null);
		}
		seq_num++;
		timestamp++;
		main_parent.state_info.we_sent = true;
		return (byteStream.toByteArray());
	}

	/** returns RTP header obtained from the disassemble function of
		this class **/
	public RTP_Header getRTP_Header() {
		return (rtp_header);
	}
}
