  
//package queuesim; 

import java.awt.*; 
import java.awt.event.*; 
import java.awt.image.*; 
import java.applet.*; 
import java.util.*; 

////////////////////////////////////////////////////////////////////////////// 
public class QueueSim extends Applet 
////////////////////////////////////////////////////////////////////////////// 
{ 
  Button start=new Button("Start"); 
  Button reset=new Button("Reset"); 
  MyChoice emitRate=new MyChoice(new String[] {"500  packet/s","350 packet/s"}, new double[] {2E-3,3E-3},1); 
  MyChoice processorRate=new MyChoice(new String[] {"1000 packet/s","500  packet/s","350 packet/s"}, new double[] {1E-3,2E-3,3E-3},3); 
  SimTimer myTimer; 

  public void init() 
  { 
    try 
    { 
      myTimer=new SimTimer(1E-4,1000,this); 
      setBackground(Color.white); 
      add(new Label("Emission rate",Label.RIGHT)); 
      add(emitRate); 
      add(new Label("Transmission rate",Label.RIGHT)); 
      add(processorRate); 
      start.addActionListener(new ActionListener() 
        { 
        public void actionPerformed (ActionEvent event) 
          { 
            emitRate.setEnabled(false); 
            processorRate.setEnabled(false); 
            start.setEnabled(false); 
            myTimer.launchSim(emitRate.getVal(), processorRate.getVal()); 
          } 
        }); 
      add(start); 
      reset.addActionListener(new ActionListener() 
        { 
        public void actionPerformed (ActionEvent event) 
          { 
            emitRate.setEnabled(true); 
            processorRate.setEnabled(true); 
            start.setEnabled(true); 
            myTimer=new SimTimer(1E-4,1000,QueueSim.this); 
          } 
        }); 
      add(reset); 
    } 
    catch(Exception e) {} 
  } 

  public void paint (Graphics g) 
  { 
    update(g); // eliminate flashing : update is overriden 
  } 

  public void update (Graphics g) 
  { 
    //work on a offscreen image 
    Dimension offDimension = getSize(); 
    Image offImage = createImage(offDimension.width, offDimension.height); 
    Graphics offGraphics = offImage.getGraphics(); 
    myTimer.draw(offGraphics); 
    g.drawImage(offImage, 0, 0, this); 
  } 

} 

////////////////////////////////////////////////////////////////////////////// 
class SimTimer implements Runnable 
////////////////////////////////////////////////////////////////////////////// 
{ 
  private double time; 
  private double tic; 
  private Line line1; 
  private Sender sender1; 
  private Dropper dropper1; 
  private Queue queue1; 
  private Line line2; 
  private Processor proc1; 
  private Applet target; 
  private Thread timerThread; 

  public SimTimer(double tick, double length, Applet tgt) 
  { 
    line1=new Line(1E-2,new Rectangle(10,60,200,10)); 
    sender1=new Sender(1E-3,line1); 
    dropper1= new Dropper(.6E-2,new Rectangle(200,70,10,60)); 
    queue1=new Queue(10,line1,dropper1,new Rectangle(210,40,100,50)); 
    line2=new Line(1E-2,new Rectangle(340,60,200,10)); 
    proc1=new Processor(2E-3,queue1,line2,new Rectangle(310,50,30,30)); 
    target=tgt; 
    time=0; 
    tic=tick; 
    timerThread=new Thread(this); 
  } 

  public void launchSim(double emitRate, double processorRate) 
  { 
    sender1.setEmitInterval(emitRate); 
    proc1.setWrkLength(processorRate); 
    timerThread.start(); 
  } 

  public void run() 
  { 
    while (true) 
    { 
      dropper1.setTime(time); 
      sender1.setTime(time); 
      line1.setTime(time); 
      queue1.setTime(time); 
      proc1.setTime(time); 
      line2.setTime(time); 
      target.repaint(); 
      time+=tic; 
      try {timerThread.sleep(50);} catch (Exception e) { }; 
    } 
  } 

  public void draw(Graphics g) 
  { 
    dropper1.draw(g); 
    line1.draw(g); 
    line2.draw(g); 
    queue1.draw(g); 
    proc1.draw(g); 
    g.drawString(TimedClass.timeToString(time),10,110); 
    g.drawString(queue1.getDropStat(),10,125); 
  } 
} 
////////////////////////////////////////////////////////////////////////////// 
class Packet 
////////////////////////////////////////////////////////////////////////////// 
{ 
  static final double emmissionDelay=1E-3; 
  double emmissionTime; 
  Color color; 

  public Packet(double eT, Color c) 
  { 
    emmissionTime=eT; 
    color=c; 
  } 
} 

////////////////////////////////////////////////////////////////////////////// 
class PacketFIFO 
////////////////////////////////////////////////////////////////////////////// 
{ 
  private int length; 
  private Packet[] content; 

  public PacketFIFO() 
  { 
    length=0; 
  } 

  public int getLength() 
  { 
    return length; 
  } 

  public void add(Packet P) 
  { 
    if (length==0) 
    { 
      content=new Packet[1]; 
      content[0]=P; 
    } 
    else 
    { 
        Packet[] temp=new Packet[length+1]; 
        for (int i=0;i<length;i++) {temp[i]=content[i];} 
        temp[length]=P; 
        content=temp; 
    } 
    length++; 
  } 

  public void rem() 
  { 
    Packet[] temp=new Packet[length-1]; 
    for (int i=0;i<length-1;i++) {temp[i]=content[i+1];} 
    content=temp; 
    length--; 
  } 

  public Packet getPacket(int index) 
  { 
    return content[index]; 
  } 

  public Packet getFirstPacket() 
  { 
    return content[0]; 
  } 
} 
////////////////////////////////////////////////////////////////////////////// 
class Sender 
///////////////////////////////////////////////////////////////////////////// 
{ 
  final Color packetsColors[]={Color.blue,Color.red,Color.green,Color.gray,Color.magenta,Color.black,Color.pink}; 
  private double meanInterval; 
  private Line outputLine; 
  private double time; 
  private double nextEmission; 
  private int nextColor; 
  private Random randomGenerator; 

  public Sender(double meanI, Line outputL) 
  { 
    meanInterval=meanI; 
    outputLine=outputL; 
    randomGenerator=new Random(); 
  } 

  public void setEmitInterval(double val) {meanInterval=val;} 

  public void setTime(double now) 
  { 
    time=now; 
    update(); 
  } 

  private void update() 
  { 
    if (nextEmission<time) 
    { 
      outputLine.emitPacket(time,packetsColors[nextColor]); 
      nextColor++; 
      if (nextColor>=packetsColors.length){nextColor=0;} 
      nextEmission=2*randomGenerator.nextDouble()*(meanInterval-Packet.emmissionDelay)+Packet.emmissionDelay; 
      nextEmission+=time; 
    } 
  } 
} 

////////////////////////////////////////////////////////////////////////////// 
class Line extends TimedClass 
////////////////////////////////////////////////////////////////////////////// 
{ 
  private boolean availableArrivedPacket; 
  private Packet arrivedPacket; 
  protected PacketFIFO packets; 
  protected double propDelay; 

  public Line(double propD,Rectangle r) 
  { 
    packets=new PacketFIFO(); 
    propDelay=propD; 
    shape=r; 
    availableArrivedPacket=false; 
  } 

  public void emitPacket(double eT, Color c) 
  { 
    packets.add(new Packet(eT,c)); 
  } 

  public void draw(Graphics g) 
  { 
    for (int i=0;i<packets.getLength();i++) 
    { 
      drawPacket(packets.getPacket(i),g); 
    } 
    g.setColor(Color.black); 
    g.drawRect(shape.x,shape.y,shape.width,shape.height); 
  } 

  private void drawPacket(Packet P, Graphics g) 
  { 
    double x1=((double)shape.x)+(time-P.emmissionTime)*((double)shape.width)/propDelay; 
    double x2=x1-(P.emmissionDelay)*((double)shape.width)/propDelay; 
    if (x1>((double)(shape.x+shape.width))) x1=(double)(shape.x+shape.width); 
    if (x2<((double)(shape.x))) x2=(double)(shape.x); 
    g.setColor(P.color); 
    g.fillRect((int)x2,shape.y,(int)(x1-x2),shape.height); 
  } 

  protected void update() 
  { 
    if (packets.getLength()>0) 
    { 
      Packet fP=packets.getFirstPacket(); 
      if (fP.emmissionTime+fP.emmissionDelay+propDelay<time) 
      { 
        packets.rem(); 
        arrivedPacket=fP; 
        availableArrivedPacket=true; 
      } 
    } 
  } 
  public boolean getAvailableArrivedPacket() {return availableArrivedPacket;} 

  public Packet pickArrivedPacket() 
  { 
    availableArrivedPacket=false; 
    return arrivedPacket; 
  } 
} 

////////////////////////////////////////////////////////////////////////////// 
class Queue extends TimedClass 
////////////////////////////////////////////////////////////////////////////// 
{ 
  private int maxSize; 
  private int size; 
  private int receivedCounter; 
  private int droppedCounter; 
  private PacketFIFO packets; 
  private Line inputLine; 
  private Dropper dropper; 

  public Queue(int mS,Line inputL,Dropper defDropper,Rectangle r) 
  { 
    shape=r; 
    maxSize=mS; 
    inputLine=inputL; 
    packets=new PacketFIFO(); 
    dropper=defDropper; 
  } 

  protected void update() 
  { 
    //if a packet is at input then add it to the queue 
    if (inputLine.getAvailableArrivedPacket()) 
    { 
      addPacket(inputLine.pickArrivedPacket()); 
    } 
  } 

  private void addPacket(Packet P) 
  { 
    receivedCounter++; 
    //if the queue is full drop packet else queue it 
    if (size==maxSize) 
    { 
      dropPacket(P); 
    } 
    else 
    { 
      size++; 
      packets.add(P); 
    } 
  } 
  private void dropPacket(Packet P) 
  { 
    dropper.emitPacket(time,P.color); 
    droppedCounter++; 
  } 

  public void draw(Graphics g) 
  { 
    for (int i=0;i<maxSize;i++) 
    { 
      int xp=shape.x+shape.width-(i+1)*(shape.width/maxSize); 
      int wp=(shape.width/maxSize); 
      if (i<(packets.getLength())) 
      { 
        g.setColor(packets.getPacket(i).color); 
        g.fillRect(xp,shape.y,wp,shape.height); 
      } 
      g.setColor(Color.black); 
      g.drawRect(xp,shape.y,wp,shape.height); 
    } 
  } 

  public boolean getAvailableArrivedPacket() {return (packets.getLength()>0);} 

  public Packet pickArrivedPacket() 
  { 
    size--; 
    Packet fP=packets.getFirstPacket(); 
    packets.rem(); 
    return fP; 
  } 

  public String getDropStat() {return Integer.toString(droppedCounter)+" packets dropped out of "+Integer.toString(receivedCounter);} 
} 

////////////////////////////////////////////////////////////////////////////// 
class Processor extends TimedClass 
////////////////////////////////////////////////////////////////////////////// 
{ 
  private boolean busy; 
  private double workLength; 
  private double curWorkStart; 
  private Line outputLine; 
  private Queue inputQueue; 
  private Packet curPacket; 

  public Processor(double wrkLength, Queue inputQ, Line outputL,Rectangle r) 
  { 
    workLength=wrkLength; 
    inputQueue=inputQ; 
    outputLine=outputL; 
    shape=r; 
    busy=false; 
  } 

  public void setWrkLength(double wrkl) {workLength=wrkl;} 

  protected void update() 
  { //if there is a packet and the job is done then emit it 
    if ((busy)&&(workDone())) 
    { 
      outputLine.emitPacket(time, curPacket.color); 
      busy=false; 
    } 
    if ((!(busy))&&(inputQueue.getAvailableArrivedPacket())) 
    { 
      busy=true; 
      curWorkStart=time; 
      curPacket=inputQueue.pickArrivedPacket(); 
    } 
  } 

  private boolean workDone(){ 
    return (curWorkStart+workLength<=time); 
  } 

  public void draw(Graphics g) 
  { 
    if (busy) 
    { 
      g.setColor(curPacket.color); 
      g.fillRect(shape.x,shape.y,shape.width,shape.height); 
    } 
    g.setColor(Color.black); 
    g.drawRect(shape.x,shape.y,shape.width,shape.height); 
  } 
} 

class Dropper extends Line 
{ 
  public Dropper(double propD,Rectangle r){super(propD,r);} 

  public void draw(Graphics g) 
  { 
    for (int i=0;i<packets.getLength();i++) 
    { 
      drawPacket(packets.getPacket(i),g); 
    } 
  } 

  private void drawPacket(Packet P, Graphics g) 
  { 
    double y1=((double)shape.y)+(time-P.emmissionTime)*((double)shape.height)/propDelay; 
    double y2=y1-(P.emmissionDelay)*((double)shape.height)/propDelay; 
    if (y1>((double)(shape.y+shape.height))) y1=(double)(shape.y+shape.height); 
    if (y2<((double)(shape.y))) y2=(double)(shape.y); 
    g.setColor(P.color); 
    g.fillRect(shape.x,(int)y2,shape.width,(int)(y1-y2)); 
  } 
} 

////////////////////////////////////////////////////////////////////////////// 
abstract class TimedClass 
////////////////////////////////////////////////////////////////////////////// 
{ 
  protected Rectangle shape; 
  protected double time; 

  public void setTime(double now) 
  { 
    time=now; 
    update(); 
  } 

  protected abstract void update(); 

  public abstract void draw(Graphics g); 

  static String timeToString(double now) 
  { 
    String res=Double.toString(now*1000); 
    int dot=res.indexOf('.'); 
    String inte=res.substring(0,dot); 
    return inte+" msec"; 
  } 
} 
////////////////////////////////////////////////////////////////////////////// 
class MyChoice extends Choice 
////////////////////////////////////////////////////////////////////////////// 
{ 
  private double vals[]; 

  public MyChoice(String items[], double values[], int defaultValue) 
  { 
    for (int i=0; i<items.length;i++) {super.addItem(items[i]);} 
    vals=values; 
    super.select(defaultValue-1); 
  } 

  public double getVal() {return  vals[super.getSelectedIndex()];} 
} 
  
  
  
