// Java Applet Demonstration of Go-Back-N Protocol.
// Coded by Shamiul Azom. ASU ID: 993456298
// as project assigned by Prof. Martin Reisslein, Arizona State University
// Course No. EEE-459/591. Spring 2001
 
 

import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
 

public class gbn extends Applet implements ActionListener, Runnable
 {
 final int window_len_def = 5;    // Default values of parameters
 final int pack_width_def = 10;
 final int pack_height_def = 30;
 final int h_offset_def = 100;
 final int v_offset_def = 50;
 final int v_clearance_def = 300;
 final int total_packet_def = 20;
 final int time_out_sec_def = 30;
 
 final Color unack_color = Color.red;  //  Default colors of different packets
 final Color ack_color = Color.yellow;
 final Color sel_color = Color.green;
 final Color roam_pack_color = Color.red;
 final Color roam_ack_color = Color.yellow;
 final Color dest_color = Color.blue;
 
 int base, nextseq, fps, selected=-1;
 boolean timerFlag, timerSleep;
 Button send, stop, fast, slow, kill, reset;
 Thread gbnThread, timerThread;
 
 Dimension offDimension;    // flashing eliminator: double buffering
    Image offImage;
    Graphics offGraphics;
 
 String statusMsg, strCurrentValues;
 
 packet sender[];
 
 // Declaring properties
 
 int window_len, pack_width, pack_height, h_offset, v_offset, v_clearance, total_packet, time_out_sec;
 
 
 public void init()
  {
  String strWinLen, strPackWd, strPackHt, strHrOff, strVtOff, strVtClr, strTotPack, strTimeout;
 
        strWinLen = getParameter("window_length");  // Start collecting parameters
        strPackWd = getParameter("packet_width");
        strPackHt = getParameter("packet_height");
        strHrOff = getParameter("horizontal_offset");
        strVtOff = getParameter("vertical_offset");
        strVtClr = getParameter("vertical_clearance");
        strTotPack = getParameter("total_packets");
        strTimeout = getParameter("timer_time_out");
 
        // Get the values of the parameters into properties.
 
        try {
            if (strWinLen != null) window_len = Integer.parseInt(strWinLen);
            if (strPackWd != null) pack_width = Integer.parseInt(strPackWd);
            if (strPackHt != null) pack_height = Integer.parseInt(strPackHt);
            if (strHrOff != null) h_offset = Integer.parseInt(strHrOff);
            if (strVtOff != null) v_offset = Integer.parseInt(strVtOff);
            if (strVtClr != null) v_clearance = Integer.parseInt(strVtClr);
            if (strTotPack != null) total_packet = Integer.parseInt(strTotPack);
            if (strTimeout != null) time_out_sec = Integer.parseInt(strTimeout);
            } catch (Exception e) {}
 
        // If parameter is not found, use default values.
 
        window_len = (window_len > 0) ? window_len : window_len_def;
  pack_width = (pack_width > 0) ? pack_width : pack_width_def;
  pack_height = (pack_height > 0) ? pack_height : pack_height_def;
  h_offset = (h_offset > 0) ? h_offset : h_offset_def;
  v_offset = (v_offset > 0) ? v_offset : v_offset_def;
  v_clearance = (v_clearance > 0) ? v_clearance : v_clearance_def;
  total_packet = (total_packet > 0) ? total_packet : total_packet_def;
   time_out_sec = (time_out_sec > 0) ? time_out_sec : time_out_sec_def;
 
  base = 0;    // Defining base
  nextseq = 0;   // Defining Next sequence number.
  fps = 5;    // Defining default Frame per Second.
 
  sender = new packet[total_packet];
  statusMsg = "Ready to run. Press 'Send New' button to start.";
  strCurrentValues = "Window Base = "+ base +".   Next Sequence No. = "+ nextseq;
 
  // Defining the buttons
 
  send = new Button("Send New");
  send.setActionCommand("rdt");
  send.addActionListener(this);
 
  stop = new Button("Stop Animation");
  stop.setActionCommand("stopanim");
  stop.addActionListener(this);
 
  fast = new Button("Faster");
  fast.setActionCommand("fast");
  fast.addActionListener(this);
 
  slow = new Button("Slower");
  slow.setActionCommand("slow");
  slow.addActionListener(this);
 
  kill = new Button("Kill Packet");
  kill.setActionCommand("kl");
  kill.addActionListener(this);
  kill.setEnabled(false);
 
  reset = new Button("Reset");
  reset.setActionCommand("rst");
  reset.addActionListener(this);
 
  // Adding the buttons
 
  add(send);
  add(stop);
  add(fast);
  add(slow);
  add(kill);
  add(reset);
  }
 
 
 public void start()
  {
  if (gbnThread==null)     // Creating main thread and start it
   gbnThread = new Thread(this);
  gbnThread.start();
  }
 
 public void run()
  {
  Thread currenthread = Thread.currentThread();
 
  while (currenthread==gbnThread)   // While the animation is running
   if (onTheWay(sender))    // Checks if any of the packets are travelling
    {
    for (int i=0; i<total_packet; i++)
     if (sender[i]!= null)
      if (sender[i].on_way)     // If packet is roaming
       if (sender[i].packet_pos < (v_clearance-pack_height))
        sender[i].packet_pos+=5;  // Move packet
       else if (sender[i].packet_ack)  // If it is moving to destination
        {
        sender[i].reached_dest = true;
        if (check_upto_n(i))   // Send acknowledgement if all preceeding
         {       // packets are received.
         sender[i].packet_pos = pack_height+5;
         sender[i].packet_ack = false;
         statusMsg = "Packet "+i+" received. Acknowledge sent.";
         }
        else
         {
         sender[i].on_way = false;
         statusMsg = "Packet "+i+" received. No acknowledge sent.";
         if (i==selected)
          {
          selected = -1;
          kill.setEnabled(false);
          }
         }
        }
       else if (!sender[i].packet_ack)   // acknowledgement
        {
        statusMsg = "Packet "+ i +" acknowledge received.";
        sender[i].on_way = false;
        for (int n=0; n<=i; n++)
         sender[n].acknowledged = true;
        if (i==selected)
         {
         selected = -1;
         kill.setEnabled(false);
         }
 
        timerThread = null;    //resetting timer thread
 
        if (i+window_len<total_packet)
         base = i+1;
        if (nextseq < base+window_len) send.setEnabled(true);
 
        if (base != nextseq)
         {
         statusMsg += " Timer restarted.";
         timerThread = new Thread(this);
         timerSleep = true;
         timerThread.start();
         }
        else
         statusMsg += " Timer stopped.";
        }
    strCurrentValues = "Window Base = "+ base +".   Next Sequence No. = "+ nextseq;
    repaint();
 
    try {
     Thread.sleep(1000/fps);
     } catch (InterruptedException e)
      {
      System.out.println("Help");
      }
    }
   else
    gbnThread = null;
 
 
  while (currenthread == timerThread)
   if (timerSleep)
    {
    timerSleep=false;
    try {
     Thread.sleep(time_out_sec*1000);
     } catch (InterruptedException e)
       {
       System.out.println ("Timer interrupted.");
       }
    }
   else
    {
    for (int n=base; n<base+window_len; n++)
     if (sender[n] != null)
      if (!sender[n].acknowledged)
       {
       sender[n].on_way = true;
       sender[n].packet_ack = true;
       sender[n].packet_pos = pack_height+5;
       }
    timerSleep = true;
    if (gbnThread == null)
     {
     gbnThread = new Thread (this);
     gbnThread.start();
     }
 
    statusMsg = "Packets resent by timer. Timer restarted.";
    }
  }
 
 
 public void actionPerformed(ActionEvent e)
  {
  String cmd = e.getActionCommand();
 
  if (cmd == "rdt" && nextseq < base+window_len) // Send button is pressed
   {
   sender[nextseq] = new packet(true,pack_height+5);
 
   statusMsg = "Packet "+ nextseq +" sent.";
 
   if (base == nextseq)
    {    // i.e. the window is empty and a new data is getting in
    statusMsg += " Timer set for packet "+ base +".";
    if (timerThread == null)
     timerThread = new Thread(this);
    timerSleep = true;
    timerThread.start();
    }
 
   repaint();
   nextseq++;
   if (nextseq == base+window_len)
    send.setEnabled(false);
   start();
   }
 
  else if (cmd == "fast")    // Faster button pressed
   {
   fps+=2;
   statusMsg = "Simulation speed increased by 2 fps.";
   }
 
  else if (cmd == "slow" && fps>2)
   {
   fps-=2;
   statusMsg = "Simulation speed decreased by 2 fps.";
   }
 
  else if (cmd == "stopanim")
   {
   gbnThread = null;
   if (timerThread != null)
    {
    timerFlag = true;
    timerThread = null;   // added later
    }
   stop.setLabel("Resume");
   stop.setActionCommand("startanim");
 
   // disableing all the buttons
   send.setEnabled(false);
   slow.setEnabled(false);
   fast.setEnabled(false);
   kill.setEnabled(false);
 
   statusMsg = "Simulation paused.";
   repaint();
   }
 
  else if (cmd == "startanim")
   {
   statusMsg = "Simulation resumed.";
   stop.setLabel("Stop Animation");
   stop.setActionCommand("stopanim");
   if (timerFlag)
    {
    statusMsg += " Timer running.";
    timerThread = new Thread(this);
    timerSleep = true;
    timerThread.start();
    }
 
   // enabling the buttons
   send.setEnabled(true);
   slow.setEnabled(true);
   fast.setEnabled(true);
   kill.setEnabled(true);
   repaint();    // repainted to show new messages
 
   start();
   }
 
  else if (cmd == "kl")
   {
   if (sender[selected].packet_ack)
    statusMsg = "Packet "+ selected +" destroyed. Timer running for packet "+base+".";
   else
    statusMsg = "Acknowledgement of packet "+ selected +" destroyed. Timer running for packet "+base+".";
 
   sender[selected].on_way = false;
   kill.setEnabled(false);
   selected = -1;
   repaint();
   }
 
  else if (cmd == "rst")
   reset_app();
  }

 
 public boolean mouseDown(Event e, int x, int y)
  {
  int i, xpos, ypos;
  i = (x-h_offset)/(pack_width+7);
  if (sender[i]!= null)
   {
   xpos = h_offset+(pack_width+7)*i;
   ypos = sender[i].packet_pos;
 
   if (x>=xpos && x<= xpos+pack_width && sender[i].on_way)
    {
    if ((sender[i].packet_ack && y>=v_offset+ypos && y<=v_offset+ypos+pack_height) || ((!sender[i].packet_ack) && y>=v_offset+v_clearance-ypos && y<=v_offset+v_clearance-ypos+pack_height))
     {
     statusMsg = "Packet "+ i +" selected.";
     sender[i].selected = true;
     selected = i;
     kill.setEnabled(true);
     }
    else
     statusMsg = "Click on a moving packet to select.";
    }
   else
    statusMsg = "Click on a moving packet to select.";
   }
  return true;
  }
 

 public void paint(Graphics g)    // To eliminate flushing, update is overriden
  {
  update(g);
  }
 
 
 public void update(Graphics g)
  {
  Dimension d = size();
 
  //Create the offscreen graphics context, if no good one exists.
        if ((offGraphics == null) || (d.width != offDimension.width) || (d.height != offDimension.height))
   {
            offDimension = d;
            offImage = createImage(d.width, d.height);
            offGraphics = offImage.getGraphics();
   }
 
  //Erase the previous image.
        offGraphics.setColor(Color.white);
        offGraphics.fillRect(0, 0, d.width, d.height);
 
  //drawing window
 
 
  offGraphics.setColor(Color.black);
  offGraphics.draw3DRect(h_offset+base*(pack_width+7)-4, v_offset-3, (window_len)*(pack_width+7)+1, pack_height+6,true);
 
 
  for (int i=0; i<total_packet; i++)
   {
   // drawing the sending row
 
   if (sender[i]==null)
    {
    offGraphics.setColor(Color.black);
    offGraphics.draw3DRect(h_offset+(pack_width+7)*i, v_offset, pack_width,pack_height,true);
    offGraphics.draw3DRect(h_offset+(pack_width+7)*i, v_offset+v_clearance, pack_width,pack_height,true);
    }
   else
    {
    if (sender[i].acknowledged)
     offGraphics.setColor(ack_color);
    else
     offGraphics.setColor(unack_color);
    offGraphics.fill3DRect (h_offset+(pack_width+7)*i, v_offset,pack_width,pack_height,true);
 
 
    // drawing the destination packets
 
    offGraphics.setColor (dest_color);
    if (sender[i].reached_dest)
     offGraphics.fill3DRect (h_offset+(pack_width+7)*i, v_offset+v_clearance,pack_width,pack_height,true);
    else
     offGraphics.draw3DRect (h_offset+(pack_width+7)*i, v_offset+v_clearance,pack_width,pack_height,true);
 
    // drawing the moving packets
 
    if (sender[i].on_way)
     {
     if (i==selected)
      offGraphics.setColor (sel_color);
     else if (sender[i].packet_ack)
      offGraphics.setColor (roam_pack_color);
     else
      offGraphics.setColor (roam_ack_color);
 
     if (sender[i].packet_ack)
      offGraphics.fill3DRect (h_offset+(pack_width+7)*i, v_offset+sender[i].packet_pos,pack_width,pack_height,true);
     else
      offGraphics.fill3DRect (h_offset+(pack_width+7)*i, v_offset+v_clearance-sender[i].packet_pos,pack_width,pack_height,true);
     }
    }
   }   // for loop ends
 
   // drawing message boxes
 
   offGraphics.setColor(Color.black);
   int newvOffset = v_offset+v_clearance+pack_height;
   int newHOffset = h_offset;
 
   offGraphics.drawString(statusMsg,newHOffset,newvOffset+25);
   //offGraphics.drawString(strCurrentValues,newHOffset,newvOffset+40);
 
   offGraphics.drawString("Packet",newHOffset+15,newvOffset+60);
   offGraphics.drawString("Acknowledge",newHOffset+85,newvOffset+60);
   offGraphics.drawString("Received Pack",newHOffset+185,newvOffset+60);
   offGraphics.drawString("Selected",newHOffset+295,newvOffset+60);
 
   offGraphics.drawString("Base = "+base,h_offset+(pack_width+7)*total_packet+10,v_offset+v_clearance/2);
   offGraphics.drawString("NextSeq = "+nextseq,h_offset+(pack_width+7)*total_packet+10,v_offset+v_clearance/2+20);
 
   offGraphics.setColor(Color.blue);
   offGraphics.drawString("Sender",h_offset+(pack_width+7)*total_packet+10,v_offset+12);
   offGraphics.drawString("Receiver",h_offset+(pack_width+7)*total_packet+10,v_offset+v_clearance+12);
 
   offGraphics.setColor(Color.gray);
   offGraphics.draw3DRect(newHOffset-10,newvOffset+42,360,25,true);
 
   offGraphics.setColor(Color.red);
   offGraphics.draw3DRect(h_offset+(pack_width+7)*total_packet+5,v_offset+v_clearance/2-15,80,40,true);
 
   offGraphics.setColor(roam_pack_color);
   offGraphics.fill3DRect(newHOffset, newvOffset+50,10,10,true);
 
   offGraphics.setColor(roam_ack_color);
   offGraphics.fill3DRect(newHOffset+70, newvOffset+50,10,10,true);
 
   offGraphics.setColor(dest_color);
   offGraphics.fill3DRect(newHOffset+170, newvOffset+50,10,10,true);
 
   offGraphics.setColor(sel_color);
   offGraphics.fill3DRect(newHOffset+280, newvOffset+50,10,10,true);
 
 
  g.drawImage(offImage, 0, 0, this);
  }    // method paint ends
 
 
 // checks out if an array is on the way to source or destination
 
 public boolean onTheWay(packet pac[])
  {
  for (int i=0; i<pac.length; i++)
   if (pac[i] == null)
    return false;
   else if (pac[i].on_way) return true;
  return false;
  }
 
 // checkes all the packets before packno. Returns false if any packet has
 // not reached destination and true if all the packets have reached destination.
 
 public boolean check_upto_n(int packno)
  {
  for (int i=0; i<packno; i++)
   if (!sender[i].reached_dest)
    return false;
  return true;
  }
 
 public void reset_app()
  {
  for (int i=0; i<total_packet; i++)
   if (sender[i] != null)
    sender[i] = null;
  base = 0;
  nextseq = 0;
  selected = -1;
  fps = 5;
  timerFlag = false;
  timerSleep = false;
  gbnThread = null;
  timerThread = null;
  if(stop.getActionCommand()=="startanim")  // in case of pause mode, enable all buttons
   {
   slow.setEnabled(true);
   fast.setEnabled(true);
   }
 
  send.setEnabled(true);
  kill.setEnabled(false);
 
  stop.setLabel("Stop Animation");
  stop.setActionCommand("stopanim");
  statusMsg = "Simulation restarted. Press 'Send New' to start.";
  repaint();
  }
 }
 

class packet
 {
 boolean on_way, reached_dest, acknowledged, packet_ack, selected;
 int packet_pos;
 
 packet()
  {
  on_way = false;
  selected = false;
  reached_dest = false;
  acknowledged = false;
  packet_ack = true;
  packet_pos = 0;
  }
 
 packet(boolean onway, int packetpos)
  {
  on_way = onway;
  selected = false;
  reached_dest = false;
  acknowledged = false;
  packet_ack = true;
  packet_pos = packetpos;
  }
 }