import java.applet.*;
import java.awt.*;

class AnimatedImage extends Canvas {
	Router A;
	Router B;
	Router C;
	Router D;
	double time;
	Link L1;
	Link L2;
	Link L3;
	Dimension d;
	Image offScreenImage;
	Graphics offScreenGraphics;
	boolean isText;

	public AnimatedImage(Router A, Router B, Router C, Router D, double time, Link L1, Link L2, Link L3, Image offScreenImage, boolean isText) {
		resize(560,315);
		d = size();
		this.A = A;
		this.B = B;
		this.C = C;
		this.D = D;
		this.time = time;
		this.L1 = L1;
		this.L2 = L2;
		this.L3 = L3;
		this.offScreenImage = offScreenImage;
		offScreenGraphics = offScreenImage.getGraphics();
		this.isText = isText;
	}

	public Dimension preferredSize() {
		return (new Dimension(560,315));
	}

	public Dimension minimumSize() {
		return (new Dimension(560,295));
	}

	public void updateImage(Router A, Router B, Router C, Router D, double time, Link L1, Link L2, Link L3) {
		this.A = A;
		this.B = B;
		this.C = C;
		this.D = D;
		this.time = time;
		this.L1 = L1;
		this.L2 = L2;
		this.L3 = L3;
	}

	public void update(Graphics g) {
		paint(g);
	}

	public void paint(Graphics g) {
		if (isText) {  //textual animation
			offScreenGraphics.clearRect(0,0,d.width, d.height);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("A:  current queue: ", 150,30);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString((int)A.bit_stream.queue), 280,30);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("A:  current packet: ", 150, 45);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(A.bit_stream.cur_packet), 280, 45);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,55);
			drawL1(offScreenGraphics);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,85);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("B: received buffer: ", 150,95);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(B.bit_stream.rec_packet), 280, 95);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("B:  current queue: ",150,110);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString((int)B.bit_stream.queue), 280,110);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("B: current packet: ", 150, 125);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(B.bit_stream.cur_packet),280, 125);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,135);
			drawL2(offScreenGraphics);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,165);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("C: received buffer: ", 150,175);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(C.bit_stream.rec_packet), 280, 175);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("C:  current queue: ",150,190);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString((int)C.bit_stream.queue), 280,190);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("C: current packet: ",150,205);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(C.bit_stream.cur_packet),280, 205);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,215);
			drawL3(offScreenGraphics);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,245);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("D: received buffer: ",150,255);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(D.bit_stream.rec_packet), 280, 255);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("D:  current queue: ",150,270);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString((int)D.bit_stream.queue), 280,270);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("D: current packet: ",150, 285);
			offScreenGraphics.setColor(Color.black);
			offScreenGraphics.drawString(Integer.toBinaryString(D.bit_stream.cur_packet), 280, 285);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString("------------------------------------------------------",150,295);
			offScreenGraphics.setColor(Color.blue);
			offScreenGraphics.drawString("TIME ELAPSED: ",150,305);
			offScreenGraphics.setColor(Color.red);
			offScreenGraphics.drawString(padding(Double.toString(time)),280,305);
			g.drawImage(offScreenImage,0,0,this);
		} else {  //graphical animation
			
			//clear the screen
			offScreenGraphics.setColor(Color.pink);
			offScreenGraphics.fillRect(0, 0, d.width, d.height);

			//draw routers
			drawRouter(offScreenGraphics,"A");
			drawRouter(offScreenGraphics,"B");
			drawRouter(offScreenGraphics,"C");
			drawRouter(offScreenGraphics,"D");

			//draw links
			drawGLine(offScreenGraphics,"L1");
			drawGLine(offScreenGraphics,"L2");
			drawGLine(offScreenGraphics,"L3");

			//draw queues
			drawQ(offScreenGraphics,"A");
			drawQ(offScreenGraphics,"B");
			drawQ(offScreenGraphics,"C");
			drawQ(offScreenGraphics,"D");

			//draw current packets
			drawCurPacket(offScreenGraphics,"A");
			drawCurPacket(offScreenGraphics,"B");
			drawCurPacket(offScreenGraphics,"C");
			drawCurPacket(offScreenGraphics,"D");

			//draw receiving packets
			drawRecPacket(offScreenGraphics,"A");
			drawRecPacket(offScreenGraphics,"B");
			drawRecPacket(offScreenGraphics,"C");
			drawRecPacket(offScreenGraphics,"D");

			//draw time elapsed
			offScreenGraphics.setColor(Color.blue);
			String st_time = Double.toString(time);
			offScreenGraphics.drawString("TIME ELAPSED: " + padding(st_time), 220, 20);

			//draw Legend
			drawLegend(offScreenGraphics);

			g.drawImage(offScreenImage,0,0,this);
		}
	}

	//set the string representation of time to have two decimal degits
	public String padding(String s) {
		int num_decimal_digit = 0;
		boolean decimal = false;
		String newString;
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '.') {
				num_decimal_digit = i;
				decimal = true;
			}
		}
		if (decimal == true) {
			num_decimal_digit = s.length() - 1 - num_decimal_digit;
		}

		switch (num_decimal_digit) {
		case 0:
			newString = s + ".00";
			break;
		case 1:
			newString = s + "0";
			break;
		default:
			newString = s;
		}

		return newString;
	}
	
	public void drawL1 (Graphics g) {
		g.setColor(Color.blue);
		g.drawString("L1: ", 150, 70);
		g.setColor(Color.darkGray);
		g.drawRect(280,63,15,5);
		g.drawRect(310,63,15,5);
		g.drawRect(340,63,15,5);
		g.setColor(Color.cyan);
		if (L1.bit_pos[0]) {
			g.fillRect(281,64,14,4);
		} else {
			g.clearRect(281,64,14,4);
		}
		if (L1.bit_pos[1]) {
			g.fillRect(311,64,14,4);
		} else {
			g.clearRect(311,64,14,4);
		}
		if (L1.bit_pos[2]) {
			g.fillRect(341,64,14,4);
		} else {
			g.clearRect(341,64,14,4);
		}
	}
	public void drawL2 (Graphics g) {
		g.setColor(Color.blue);
		g.drawString("L2: ", 150, 150);
		g.setColor(Color.darkGray);
		g.drawRect(280,143,15,5);
		g.drawRect(310,143,15,5);
		g.drawRect(340,143,15,5);
		g.setColor(Color.cyan);
		if (L2.bit_pos[0]) {
			g.fillRect(281,144,14,4);
		} else {
			g.clearRect(281,144,14,4);
		}
		if (L2.bit_pos[1]) {
			g.fillRect(311,144,14,4);
		} else {
			g.clearRect(311,144,14,4);
		}
		if (L2.bit_pos[2]) {
			g.fillRect(341,144,14,4);
		} else {
			g.clearRect(341,144,14,4);
		}
	}

	public void drawL3 (Graphics g) {
		g.setColor(Color.blue);
		g.drawString("L3: ", 150, 230);
		g.setColor(Color.darkGray);
		g.drawRect(280,223,15,5);
		g.drawRect(310,223,15,5);
		g.drawRect(340,223,15,5);
		g.setColor(Color.cyan);
		if (L3.bit_pos[0]) {
			g.fillRect(281,224,14,4);
		} else {
			g.clearRect(281,224,14,4);
		}
		if (L3.bit_pos[1]) {
			g.fillRect(311,224,14,4);
		} else {
			g.clearRect(311,224,14,4);
		}
		if (L3.bit_pos[2]) {
			g.fillRect(341,224,14,4);
		} else {
			g.clearRect(341,224,14,4);
		}
	}

	public void drawRouter(Graphics g, String r) {
		int x = 17;
		int y = 190;
		int width = 60;
		int height = 50;
		Font font;

		if 	(r.equalsIgnoreCase("A")) {
			x = 17;
		} else if (r.equalsIgnoreCase("B")) {
			x += 155;
		} else if (r.equalsIgnoreCase("C")) {
			x += 310;
		} else if (r.equalsIgnoreCase("D")) {
			x += 465;
		}

		//draw router frame
		g.setColor(Color.blue);
		g.drawRect(x,y,width,height);

		//draw the left interior box
		
		if(r.equalsIgnoreCase("A")) {
			
		} else {
			g.setColor(Color.red);
			g.fillRect(x+6, y+5, 10, 41);
		}

		g.setColor(Color.yellow);
		g.fillRect(x+26, y+5, 10, 41);

		if (r.equalsIgnoreCase("D")){

		} else {
			g.setColor(Color.green);
			g.fillRect(x+46, y+5, 10, 41);
		}

		//draw the right interior box
		//g.setColor(Color.lightGray);
		//g.fillRect(x+25, y+5 , 30, 41 );

		//draw Label
		g.setColor(Color.blue);
		font = new Font(this.getFont().getName(), Font.BOLD, this.getFont().getSize());
		g.setFont(font);
		g.drawString(r, x+28, y+65);

		if (r.equalsIgnoreCase("A")) {
			g.drawString("Source", x + 12, y + 77);
		} else if (r.equalsIgnoreCase("D")) {
			g.drawString("Destination", x - 2, y + 77);
		}
	}

	public void drawGLine(Graphics g, String l){
		int x = 78;
		int y = 212;
		int width = 94;
		int height = 5;
		
		if (l.equalsIgnoreCase("L1")) {
			x = 78;
			if (!(L1.prop_status)) {
				drawNPDLine(g, x, y, width, height);
			} else {
				drawPDLine(g, x, y, height, L1.bit_pos[0], L1.bit_pos[1], L1.bit_pos[2], L1.bit_pos[3], L1.packet_number[0], L1.packet_number[1], L1.packet_number[2], L1.packet_number[3]);
			}

			//draw L1 label
			g.setColor(Color.black);
			g.drawString("L1",x+40,y+20);
		} else if (l.equalsIgnoreCase("L2")) {
			x += 155;
			if (!(L2.prop_status)) {
				drawNPDLine(g, x, y, width, height);
			} else {
				drawPDLine(g, x, y, height, L2.bit_pos[0], L2.bit_pos[1], L2.bit_pos[2], L2.bit_pos[3], L2.packet_number[0], L2.packet_number[1], L2.packet_number[2], L2.packet_number[3]);
			}

			//draw L2 label
			g.setColor(Color.black);
			g.drawString("L2",x+40,y+20);

		} else if (l.equalsIgnoreCase("L3")) {
			x += 310;
			if (!(L3.prop_status)) {
				drawNPDLine(g, x, y, width, height);
			} else {
				drawPDLine(g, x, y, height, L3.bit_pos[0], L3.bit_pos[1], L3.bit_pos[2], L3.bit_pos[3], L3.packet_number[0], L3.packet_number[1], L3.packet_number[2], L3.packet_number[3]);
			}

			//draw L3 label
			g.setColor(Color.black);
			g.drawString("L3",x+40,y+20);

		} //end if

	} //end drawGLine(Graphics g, String l)

	//draw no-propogational-delay link in the graphics animation
	public void drawNPDLine(Graphics g, int x, int y, int width, int height) {
		g.setColor(Color.cyan);
		g.fillRect(x, y, width, height);
	}

	//draw propogational-delay link in the graphics animation
	public void drawPDLine(Graphics g, int x, int y, int height, boolean p1, boolean p2, boolean p3, boolean p4, int p1_num, int p2_num, int p3_num, int p4_num) {
		
		//draw packet frame
		g.setColor(Color.black);
		for (int i = 0; i < 4; i++) {
			g.drawRect(x + 13 + (20 * i), y, 10, height);
		}

		//draw the individual bit
		g.setColor(Color.cyan);
		if (p1) {
			if (p1_num == 1) {
				g.setColor(Color.blue);
			}
			g.fillRect(x + 14, y + 1, 9, height - 1);
		}
		
		g.setColor(Color.cyan);
		if (p2) {
			if (p2_num == 1) {
				g.setColor(Color.blue);
			}
			g.fillRect(x + 34, y + 1, 9, height - 1);
		}

		g.setColor(Color.cyan);
		if (p3) {
			if (p3_num == 1) {
				g.setColor(Color.blue);
			}
			g.fillRect(x + 54, y + 1, 9, height - 1);
		}

				g.setColor(Color.cyan);
		if (p4) {
			if (p4_num == 1) {
				g.setColor(Color.blue);
			}
			g.fillRect(x + 74, y + 1, 9, height - 1);
		}
	}

	public void drawQ(Graphics g, String r) {
		int x = 42;
		int y = 165;
		int width = 10;
		int height = 5;
		int num_bit = 0;

		//draw queue frame
		if (r.equalsIgnoreCase("A")) {
			x = 42;
			num_bit = A.bit_stream.bitNotSentInQueue();
		} else if (r.equalsIgnoreCase("B")) {
			x += 155;
			num_bit = B.bit_stream.bitNotSentInQueue();
		} else if (r.equalsIgnoreCase("C")) {
			x += 310;
			num_bit = C.bit_stream.bitNotSentInQueue();
		} else if (r.equalsIgnoreCase("D")) {
			x += 465;
			num_bit = D.bit_stream.bitNotSentInQueue();
		}

		for (int i = 0; i < A.bit_stream.data_size; i++) {
			g.setColor(Color.black);
			g.drawRect(x, y - (i * 9), width, height);
			/**if ((i%5) == 0) { //draw Scale (5)
				g.drawLine(x - 20, y - (i * 9) + 7, x + 30, y - (i * 9) + 7);
			}*/
		}

		//draw queue label
		g.setColor(Color.black);
		g.drawString("B" ,x+2, y+20);

		//draw queue content
		for (int i = 0; i < num_bit; i++) {
			g.setColor(Color.yellow);
			g.fillRect(x + 1, y - (i * 9) + 1 , width - 1, height - 1);
		}
	}

	public void drawCurPacket(Graphics g, String r) {
		int x = 62;
		int y = 165;
		int width = 10;
		int height = 5;
		int num_bit = 0;
		int cur_packet_size = 0;

		//draw queue frame
		if (r.equalsIgnoreCase("A")) {
			x = 62;
			num_bit = A.bit_stream.bitNotSentInCurPacket();
			cur_packet_size = A.bit_stream.cur_packet_size;
		} else if (r.equalsIgnoreCase("B")) {
			x += 155;
			num_bit = B.bit_stream.bitNotSentInCurPacket();
			cur_packet_size = B.bit_stream.cur_packet_size;
		} else if (r.equalsIgnoreCase("C")) {
			x += 310;
			num_bit = C.bit_stream.bitNotSentInCurPacket();
			cur_packet_size = C.bit_stream.cur_packet_size;
		} else if (r.equalsIgnoreCase("D")) {
			x += 465;
			num_bit = D.bit_stream.bitNotSentInCurPacket();
			cur_packet_size = D.bit_stream.cur_packet_size;
		}

		//draw frame
		for (int i = 0; i < cur_packet_size; i++) {
			g.setColor(Color.black);
			g.drawRect(x, y - (i * 9), width, height);
		}

		//draw Current Packet label
		if (r.equalsIgnoreCase("D")) {
			
		} else {
			g.setColor(Color.black);
			g.drawString("T" ,x+2, y+20);
		}

		//draw current packet content
		for (int i = 0; i < num_bit; i++) {
			g.setColor(Color.green);
			g.fillRect(x + 1, y - (i * 9) + 1 , width - 1, height - 1);
		}
	}

		public void drawRecPacket(Graphics g, String r) {
		int x = 22;
		int y = 165;
		int width = 10;
		int height = 5;
		int num_bit = 0;
		int rec_packet_size = 0;

		//draw queue frame
		if (r.equalsIgnoreCase("A")) {
			x = 22;
			num_bit = A.bit_stream.bitReceivedFromCurPacket();
			rec_packet_size = 0;
		} else if (r.equalsIgnoreCase("B")) {
			x += 155;
			num_bit = B.bit_stream.bitReceivedFromCurPacket();
			rec_packet_size = B.rec_packet_size;
		} else if (r.equalsIgnoreCase("C")) {
			x += 310;
			num_bit = C.bit_stream.bitReceivedFromCurPacket();
			rec_packet_size = C.rec_packet_size;
		} else if (r.equalsIgnoreCase("D")) {
			x += 465;
			num_bit = D.bit_stream.bitReceivedFromCurPacket();
			rec_packet_size = D.rec_packet_size;
		}

		for (int i = 0; i < rec_packet_size; i++) {
			g.setColor(Color.black);
			g.drawRect(x, y - (i * 9), width, height);
		}

		//draw Received Packet label
		if (r.equalsIgnoreCase("A")) {

		} else {
			g.setColor(Color.black);
			g.drawString("R" ,x+2, y+20);
		}

		//draw received packet content
		for (int i = 0; i < num_bit; i++) {
			g.setColor(Color.red);
			g.fillRect(x + 1, y - (i * 9) + 1 , width - 1, height - 1);
		}
	}

	public void drawLegend(Graphics g) {
		g.setColor(Color.black);
		g.drawString("Each rectangle represents 1 kbit of data.", 165,280);
		g.drawRect(105,283,355,20);
		g.drawString("R: receive buffer" + "    " + "B: internal buffer" + "     " + "T: transmit buffer", 120,298);
	}

}

class Data extends Object {
	//field
	int rec_packet = 0;	     //received packet
	int packet_size;	     //standard packet size
	int data_size;		     //initial data size
	int cur_packet_temp;     //temp copy of current packet to be sent out
	int std_packet;		     //standard packet
	int cur_packet = 0;	     //current packet
	int cur_packet_size = 0; //current packet size
	long queue;			     //queue

	//constructor
	public Data(int data_size, int packet_size, String s){
		this.packet_size = packet_size;	//default =  4
		this.data_size = data_size;     //default = 24
		std_packet = (1 << packet_size) - 1;

		if (s.equalsIgnoreCase("Sender")) {
			queue = (1 << data_size) - 1; //if sender, then data = initial 12-bit data
		} else {
			queue = 0;
		}
	}

	//method
	//make next packet
	public void nextPacket() {
		if (isEntirePacketSent()) {
			if (queue > std_packet) {
				cur_packet = std_packet;		            //current packet = standard packet
				cur_packet_size = packet_size;	            //current packet size = standard packet size
				queue >>>= packet_size;			            //dequeue
			} else {
				cur_packet = (int) queue;		            //current packet = standard packet size
				cur_packet_size = bitNotSentInCurPacket();  //current packet size num of bit in current packt
				queue = 0;								    //dequeue
			}
		}
		cur_packet_temp = cur_packet; //temp copy of current packet for the destination to verify
	}

	//number of bit unsent in the packet
	public int bitNotSentInCurPacket(){
		int cur_packet_clone = cur_packet;
		int bitNotSent = 0;
		while (cur_packet_clone > 0) {
			cur_packet_clone >>= 1;
			bitNotSent++;
		}
		return bitNotSent;
	}

	//number of bit recieved from the current packet
	public int bitReceivedFromCurPacket(){
		int rec_packet_clone = rec_packet;
		int bitReceived = 0;
		while (rec_packet_clone > 0) {
			rec_packet_clone >>= 1;
			bitReceived++;
		}
		return bitReceived;
	}


	//number of bit unsent in the data
	public int bitNotSentInQueue(){
		long queue_clone = queue;
		int bitNotSent = 0;
		while (queue_clone > 0) {
			queue_clone >>= 1;
			bitNotSent++;
		}
		return bitNotSent;
	}

	//is the packet sent successfully
	public boolean isEntirePacketSent(){
		if (cur_packet == 0) {
			return true;
		} else {
			return false;
		}
	}

	//is the entire queue consumed
	public boolean isEntireQueueConsumed(){
		if (queue == 0) {
			return true;
		} else {
			return false;
		}
	}
}

class Router extends Object {
	//Fields
	String name;
	Data bit_stream;
	int tran_packet_size = 0;
	int tran_packet_number = 0;
	int rec_packet_size = 0;
	int rec_packet_number = 0;
	byte bit_to_sent = 0;
		
	//Constructor
	public Router(int data_size, int packet_size, String s) {
		this.name = s;
		bit_stream = new Data(data_size, packet_size, s);
	}

	//methods
	//make new packet and dequeue
	public void dequeue() {
		bit_stream.nextPacket();
	}

	//move all data in rec_packet to queue
	public void enqueue() {
		bit_stream.queue = (bit_stream.rec_packet << (bit_stream.bitNotSentInQueue())) | bit_stream.queue;
		bit_stream.rec_packet = 0;
	}
}

class Link extends Object {
	//Fields
	Router source;
	Router destination;
	boolean[] bit_pos = new boolean[4];	//bit_pos[0,1,2] represent the current bit location at
	                                    //0.25s, 0.50s, and 0.75s time interval	respectively
	                                    //if there is a propagation delay
	boolean[] last_bit = new boolean[4];
	int[] packet_size = new int[4];
	int[] packet_number = new int[4];
	boolean prop_status; //propagation delay status: true or false

	//Constructor
	public Link(Router source, Router destination, boolean prop_status){
		this.source = source;
		this.destination = destination;
		this.prop_status = prop_status;
		for (int i = 0; i < 4; i++) {
			bit_pos[i] = false;
			last_bit[i] = false;
			packet_size[i] = 0;
			packet_number[i] = 0;
		}
	}

	//method
	//move one bit between two routers.
	public void moveBit() {

		emitBit();

		//if propagation delay then take care all interval and end points' movement
		if (prop_status) {
			//move bit_pos[3]
			if (bit_pos[3]) {
				bit_pos[3] = false;
				destination.bit_stream.rec_packet = (destination.bit_stream.rec_packet << 1) + 1;
				destination.rec_packet_size = packet_size[3];
				destination.rec_packet_number = packet_number[3];
			}

 			//move bit_pos[2]
			if (bit_pos[2]) {
				bit_pos[2] = false;
				bit_pos[3] = true;
				packet_size[3] = packet_size[2];
				packet_number[3] = packet_number[2];
			}


			//move bit_pos[1]
			if (bit_pos[1]) {
				bit_pos[1] = false;
				bit_pos[2] = true;
				packet_size[2] = packet_size[1];
				packet_number[2] = packet_number[1];
			}

			//move bit_pos[0]
			if (bit_pos[0]) {
				bit_pos[0] = false;
				bit_pos[1] = true;
				packet_size[1] = packet_size[0];
				packet_number[1] = packet_number[0];
			}
			//move source.bit_to_sent
			if (source.bit_to_sent == 1) {
				source.bit_to_sent = 0;
				bit_pos[0] = true;
				packet_size[0] = source.tran_packet_size;
				packet_number[0] = source.tran_packet_number;
			}
		} // end if


		//if no propagation delay then move the bit to receiver immediately
		if (!(prop_status)) {
			if (source.bit_to_sent == 1) { //move the bit if there is data to be sent
				source.bit_to_sent = 0;
				destination.bit_stream.rec_packet = (destination.bit_stream.rec_packet << 1) + 1;
				destination.rec_packet_size = source.tran_packet_size;
				destination.rec_packet_number = source.tran_packet_number;
			}
		}

		//if the entire packet is received then move it to the queue and dequeue the source
		//if (destination.bit_stream.rec_packet == source.bit_stream.cur_packet_temp) {
		//if (destination.bit_stream.rec_packet == destination.bit_stream.std_packet) {
		if (destination.rec_packet_size == destination.rec_packet_number) {
			destination.enqueue();
			destination.rec_packet_size = 0;
			destination.rec_packet_number = 0;
		}

		if (source.bit_stream.cur_packet == 0) {
			//destination.enqueue();
			source.dequeue();
		}
	} // end moveBit()

	//emit (shoot out) a bit
	private void emitBit() {
		if (source.bit_stream.cur_packet > 0) {
			source.bit_to_sent = 1;
			source.tran_packet_size = source.bit_stream.cur_packet_size;
			//counting the bit sequence in the packet
            source.tran_packet_number = source.tran_packet_size - source.bit_stream.bitNotSentInCurPacket() + 1;

			source.bit_stream.cur_packet >>= 1;
		}
	}// end emitBit()
}

public class MessageSwitching extends Applet implements Runnable
{
	int data_size_input = 12;
	int packet_size_input = 4;
	boolean prop_delay_L1 = false; //propagation delay
	boolean prop_delay_L2 = false; //propagation delay
	boolean prop_delay_L3 = false; //propagation delay
	Router A;
	Router B;
	Router C; 
	Router D; 
	Link L1;
	Link L2;
	Link L3;
	double time;
	Button m_start = new Button("Start");
	Button m_pause = new Button("Pause");
	Button m_resume = new Button("Resume");
	Button m_stop = new Button("Stop");
	AnimatedImage canvas_image;
	Panel input = new Panel();
	Panel choices = new Panel();
	Panel pan_choices = new Panel();
	Panel pan_animation = new Panel();
	GridBagLayout gridbag = new GridBagLayout();
	GridBagConstraints c = new GridBagConstraints();
	CheckboxGroup display = new CheckboxGroup();
	Checkbox textual = new Checkbox("Textual Simulation", display, false);
	Checkbox graphical = new Checkbox("Graphical Simulation", display, true);
	Checkbox chk_prop_delay_L1 = new Checkbox("L1");
	Checkbox chk_prop_delay_L2 = new Checkbox("L2");
	Checkbox chk_prop_delay_L3 = new Checkbox("L3");
	Label l_data_size = new Label("Data Size (bit): ", Label.CENTER);
	Label l_packet_size = new Label("Packet Size (bit): ", Label.CENTER);
	Label l_set_ms_in_ps = new Label("* For message switching, set packet size equal to message size.", Label.LEFT);
	Label l_prop_delay = new Label("Optional Propagation Delay: ");
	Label l_simu_speed = new Label("Simulation Speed: ");
	Label l_simu_fast = new Label("fast",Label.LEFT);
	Label l_simu_slow = new Label("slow",Label.RIGHT);
	Label l_blank = new Label("", Label.LEFT);
	Choice cbo_data_size = new Choice();
	Choice cbo_packet_size = new Choice();
	Scrollbar sco_simu_speed = new Scrollbar(Scrollbar.HORIZONTAL,10,1,1,20);
	Image offScreenImage;
	Thread	 m_MessageSwitching = null;


	public MessageSwitching()
	{
	}

	public String getAppletInfo()
	{
		return "Name: MessageSwitching\r\n" +
		       "Author: Albert Huang\r\n" +
		       "Created with Microsoft Visual J++ Version 1.01";
	}


	public void init()
	{
		resize(560, 465);
		offScreenImage = createImage(560,430);
		setBackground(Color.white);

		//set up the panel for textual data size choices
		pan_choices.setLayout(new GridLayout(0,4));
		setupChoice(cbo_data_size, 16, "12"); //(name, num of choices, default selection)
		setupChoice(cbo_packet_size, 16, "4");
		pan_choices.add(new Label("Message Size (kbits): ", Label.CENTER));
		pan_choices.add(cbo_data_size);
		pan_choices.add(new Label("Packet Size (kbits): ", Label.CENTER));
		pan_choices.add(cbo_packet_size);
		choices.setLayout(new CardLayout());

		//setup the panel for choices (either textual or graphical)
		choices.setLayout(new CardLayout());
		choices.add("DataConfig", pan_choices);

		//set up the main panel (applet) for all the AWT components
		c.weightx = 1.0;
		c.anchor = GridBagConstraints.WEST;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.BOTH;
		gridbag.setConstraints(textual, c);
		gridbag.setConstraints(graphical, c);
		gridbag.setConstraints(choices, c);
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(l_set_ms_in_ps, c);

		c.gridwidth = 1;
		gridbag.setConstraints(l_prop_delay, c);
		gridbag.setConstraints(chk_prop_delay_L1, c);
		gridbag.setConstraints(chk_prop_delay_L2, c);
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(chk_prop_delay_L3, c);
		c.gridwidth = 1;
		gridbag.setConstraints(l_simu_speed, c);
		gridbag.setConstraints(l_simu_slow, c);
		gridbag.setConstraints(sco_simu_speed, c);
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(l_simu_fast, c);

		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(l_blank, c);

		c.gridwidth = 1;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 0.5;
		gridbag.setConstraints(m_start, c);
		gridbag.setConstraints(m_pause, c);
		gridbag.setConstraints(m_resume, c);
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(m_stop, c);
	}

	public void destroy()
	{
	}

	public void paint(Graphics g)
	{
	}

	public void start()
	{	
		input.setLayout(gridbag);
		//input.add(textual);
		//input.add(graphical);
		input.add(choices);
		input.add(l_set_ms_in_ps);
		input.add(l_prop_delay);
		input.add(chk_prop_delay_L1);
		input.add(chk_prop_delay_L2);
		input.add(chk_prop_delay_L3);
		input.add(l_simu_speed);
		input.add(l_simu_slow);
		input.add(sco_simu_speed);
		input.add(l_simu_fast);
		input.add(l_blank);
		input.add(m_start);
		input.add(m_pause);
		input.add(m_resume);
		input.add(m_stop);
		m_pause.disable();
		m_resume.disable();
		m_stop.disable();
		pan_animation.resize(500,430);
		pan_animation.setLayout(new BorderLayout());
		setLayout(new BorderLayout());

		add("North", input);
		add("Center", pan_animation);

		//initial unanimated diagram
		A = null;
		B = null;
		C = null;
		D = null;
		L1 = null;
		L2 = null;
		L3 = null;
		if (canvas_image != null) {
			pan_animation.remove(canvas_image);
		}
		canvas_image = null;
		m_MessageSwitching = null;
		A = new Router(data_size_input,packet_size_input,"Sender"); //(data_size, packet_size, name, queue_delay)
		B = new Router(0,packet_size_input,"B");
		C = new Router(0,packet_size_input,"C");
		D = new Router(0,packet_size_input,"Receiver");
		L1 = new Link(A,B, prop_delay_L1); //(source, destination, prop_delay)
		L2 = new Link(B,C, prop_delay_L2);
		L3 = new Link(C,D, prop_delay_L3);
		canvas_image = new AnimatedImage(A,B,C,D,time,L1,L2,L3,offScreenImage,textual.getState());
		pan_animation.add("Center", canvas_image);
		canvas_image.repaint();
	}
	
	public void stop()
	{
		if (m_MessageSwitching != null)
		{
			m_MessageSwitching.stop();
			m_MessageSwitching = null;
		}
	}

	public void run()
	{	
		while (D.bit_stream.queue != ((1 << data_size_input) - 1))
		{
			L1.moveBit();
			L2.moveBit();
			L3.moveBit();
			time += .25;

			try
			{
				canvas_image.updateImage(A,B,C,D,time,L1,L2,L3);
				canvas_image.repaint();
				Thread.sleep(5000 - (250 * sco_simu_speed.getValue()));
			}
			catch (InterruptedException e)
			{
				stop();
			}
		}
		enableControls();
		m_start.enable();
		m_pause.disable();
		m_resume.disable();
		m_stop.disable();
	}

	
	//Set up Choice
	public void setupChoice(Choice ch, int size, String optimal) {
		for (int i = 1; i < (size + 1); i++) {
			ch.addItem(Integer.toString(i));
		}
		ch.select(optimal);
	}

	//Eventhandling
	public boolean action(Event e, Object arg) {
		if(e.target == graphical) {
			data_size_input = Integer.parseInt(cbo_data_size.getSelectedItem());
			packet_size_input = Integer.parseInt(cbo_packet_size.getSelectedItem());
			return true;
		} else if (e.target == textual) {
			data_size_input = Integer.parseInt(cbo_data_size.getSelectedItem());
			packet_size_input = Integer.parseInt(cbo_packet_size.getSelectedItem());
			return true;
		} else if (e.target == cbo_data_size) {
			data_size_input = Integer.parseInt(cbo_data_size.getSelectedItem());
			return true;
		} else if (e.target == cbo_packet_size) {
			packet_size_input = Integer.parseInt(cbo_packet_size.getSelectedItem());
			return true;
		} else if (e.target == chk_prop_delay_L1) {
			prop_delay_L1 = chk_prop_delay_L1.getState();
			return true;
		} else if (e.target == chk_prop_delay_L2) {
			prop_delay_L2 = chk_prop_delay_L2.getState();
			return true;
		} else if (e.target == chk_prop_delay_L3) {
			prop_delay_L3 = chk_prop_delay_L3.getState();
			return true;
		} else if (e.target == m_pause) {
			m_start.disable();
			m_pause.disable();
			m_resume.enable();
			m_stop.disable();
			m_MessageSwitching.suspend();
			return true;
		} else if (e.target == m_resume) {
			m_start.disable();
			m_pause.enable();
			m_resume.disable();
			m_stop.enable();
			m_MessageSwitching.resume();
			return true;
		} else if (e.target == m_stop) {
			enableControls();
			m_start.enable();
			m_pause.disable();
			m_resume.disable();
			m_stop.disable();
			m_MessageSwitching.stop();
			return true;
		} else if (e.target == m_start) {
			disableControls();
			m_start.disable();
			m_pause.enable();
			m_resume.disable();
			m_stop.enable();
			A = null;
			B = null;
			C = null;
			D = null;
			L1 = null;
			L2 = null;
			L3 = null;
			if (canvas_image != null) {
				pan_animation.remove(canvas_image);
			}
			canvas_image = null;
			m_MessageSwitching = null;
			A = new Router(data_size_input,packet_size_input,"Sender"); //(data_size, packet_size, name, queue_delay)
			B = new Router(0,packet_size_input,"B");
			C = new Router(0,packet_size_input,"C");
			D = new Router(0,packet_size_input,"Receiver");
			L1 = new Link(A,B, prop_delay_L1); //(source, destination, prop_delay)
			L2 = new Link(B,C, prop_delay_L2);
			L3 = new Link(C,D, prop_delay_L3);
			canvas_image = new AnimatedImage(A,B,C,D,time,L1,L2,L3,offScreenImage,textual.getState());
			pan_animation.add("Center", canvas_image);
			time = 0.0f;

			//start making packet in A
			A.dequeue();

			//start new thread for each new requested simulation
			if (m_MessageSwitching == null)
			{
				m_MessageSwitching = new Thread(this);
				m_MessageSwitching.start();
			}
			return true;
		}
		return false;
	}

	public void disableControls() {
		textual.disable();
		graphical.disable();
		cbo_data_size.disable();
		cbo_packet_size.disable();
		chk_prop_delay_L1.disable();
		chk_prop_delay_L2.disable();
		chk_prop_delay_L3.disable();
		sco_simu_speed.disable();
	}

	public void enableControls() {
		textual.enable();
		graphical.enable();
		cbo_data_size.enable();
		cbo_packet_size.enable();
		chk_prop_delay_L1.enable();
		chk_prop_delay_L2.enable();
		chk_prop_delay_L3.enable();
		sco_simu_speed.enable();
	}

}