/////////////////////////////////////// 
//LineSimApllet 
//written by David Grangier, Institut Eurecom, France 
//david.grangier@eurecom.fr 
/////////////////////////////////////// 

//package linesim; 

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

//Applet Class 
public class LineSimApplet extends Applet { 
  //buttons 
  Button start=new Button ("Start"); 
  Button stop=new Button ("Reset"); 
  //features lists 
  MyChoice length=new MyChoice(new String[] {"10 km","100 km","1000 km"},new double[] {10E3,100E3,1E6},3); 
  MyChoice rate=new MyChoice(new String[] {"512 kps","1 Mbps","10 Mbps","100 Mbps"},new double[] {512E3,1E6,10E6,100E6},2); 
  MyChoice size=new MyChoice(new String[] {"100 Bytes","500 Bytes","1 kBytes"},new double[] {8E2,4E3,8E3},1); 
  //to simulate time 
  Thread timerThread; 
  TickTask timerTask; 
  boolean simulationRunning=false; 
  //communication line 
  Line myLine; 

  public void init() { 
    try { 
      setBackground(Color.white); 
      add(new Label ("Length",Label.RIGHT)); 
      add(length); 
      add(new Label("Rate",Label.RIGHT)); 
      add(rate); 
      add(new Label("Packet size",Label.RIGHT)); 
      add(size); 
      //start 
      start.addActionListener( 
        new ActionListener() 
        { 
          public void actionPerformed (ActionEvent event) 
          { 
            launchSim(); 
          } 
        }); 
      add(start); 
      //stop 
      Button stop=new Button ("Reset"); 
      stop.addActionListener( 
        new ActionListener() 
        { 
          public void actionPerformed (ActionEvent event) 
          { 
            stopSim(); 
            //clear line 
            myLine.sendTime(0); 
            //redraw cleared line 
            LineSimApplet.this.repaint(); 
          } 
        }); 
      add(stop); 
      //line 
      myLine= new Line(40,50,450,10); 
    } 
    catch(Exception e) { 
      e.printStackTrace(); 
    } 
  } 

  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(); 
    myLine.drawLine(offGraphics); 

    //sender 
    offGraphics.setColor(Color.blue); 
    offGraphics.fillRect(10,40,30,30); 
    offGraphics.setColor(Color.black); 
    offGraphics.drawString("Sender",5,90); 
    offGraphics.drawRect(10,40,30,30); 

    //receiver 
    offGraphics.setColor(Color.blue); 
    offGraphics.fillRect(490,40,30,30); 
    offGraphics.setColor(Color.black); 
    offGraphics.drawString("Receiver",485,90); 
    offGraphics.drawRect(490,40,30,30); 

    offGraphics.drawString("Propagation speed : 2.8 x 10^8 m/sec",175,105); 
    //display offscreen image 
    g.drawImage(offImage, 0, 0, this); 
  } 

  private void launchSim() 
  { 
    setupEnabled(false); 
    //setup line 
    myLine.setup(length.getVal(), rate.getVal() ); 
    myLine.emitPacket(size.getVal(),0); 
    //setup timer 
    timerTask=new TickTask(1E-5,myLine.totalTime()); 
    timerThread=new Thread(timerTask); 
    //start simulation 
    simulationRunning=true; 
    timerThread.start(); 
  } 

  private void stopSim() 
  { 
    timerTask.endNow(); 
    simulationRunning=false; 
    setupEnabled(true); 
  } 

  public void setupEnabled(boolean value) 
  { 
    start.setEnabled(value); 
    length.setEnabled(value); 
    rate.setEnabled(value); 
    size.setEnabled(value); 
  } 

  //my choice 
  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; 
      select(defaultValue-1); 
    } 

    public double getVal() {return  vals[super.getSelectedIndex()];} 
  } 
  //tickTask 
  class TickTask implements Runnable 
  { 
    private double counter; 
    private double length; 
    private double tick; 

    public TickTask(double t,double l) 
    { 
      length=l; 
      tick=t; 
      counter=0; 
    } 

    public void run() 
    { 
      while (LineSimApplet.this.simulationRunning) 
      { 
        counter+=tick; 
        LineSimApplet.this.myLine.sendTime(counter); 
        LineSimApplet.this.repaint(); 
        if (counter>=length) 
        { 
          LineSimApplet.this.myLine.clearPackets(); 
          LineSimApplet.this.timerThread.suspend(); 
        } 
        try  {LineSimApplet.this.timerThread.sleep(50);} catch (Exception e) { } 
      } 
    } 

    public void endNow() { 
      length=counter; 
    } 
  } 
} 

//Line class 
class Line 
{ 
  //graphic variables 
  private int gX; 
  private int gY; 
  private int gWidth; 
  private int gHeight; 
  //characteristic variables 
  final double celerity = 2.8E+8; 
  private double length; 
  private double rate; 
  //simulation variables 
  private double time; 
  private Packet myPacket; 

  public Line(int x, int y, int w, int h) 
  { 
    //graphic init 
    gX=x; 
    gY=y; 
    gWidth=w; 
    gHeight=h; 
  } 

  public void setup(double l, double r) 
  { 
    length=l; 
    rate=r; 
  } 

  void sendTime(double now) 
  { 
    time=now; //update time 
    removeReceivedPackets(now); 
  } 

  void emitPacket(double s, double eT) 
  { 
    myPacket= new Packet(s,eT); 
  } 

  private void removeReceivedPackets(double now) 
  { 
    if (!(myPacket==null)) 
    { 
      if ( now>myPacket.emissionTime+(myPacket.size/rate)+length*celerity ) 
      { 
          clearPackets(); 
      } 
    } 
  } 

  public void clearPackets() 
  { 
    myPacket=null; 
  } 

  public double totalTime() 
  { 
    double emmissionTime=(myPacket.size/rate); 
    double onLineTime=(length/celerity); 
    return (emmissionTime+onLineTime); 
  } 

  public void drawLine(Graphics g) 
  { 
    g.setColor(Color.white); 
    g.fillRect(gX,gY+1,gWidth,gHeight-2); 
    g.setColor(Color.black); 
    g.drawRect(gX,gY,gWidth,gHeight); 
    g.setColor(Color.red); 
    g.drawString(timeToString(time),gX+gWidth/2-10,gY+gHeight+15); 
    drawPackets(g); 
  } 

  private void drawPackets(Graphics g) 
  { 
    if (!(myPacket==null)) 
    { 
      double xfirst; 
      double xlast; 
      //compute time units 
      xfirst=time-myPacket.emissionTime; 
      xlast=xfirst-(myPacket.size/rate); 
      //compute position 
      xfirst=xfirst*celerity*gWidth/length; 
      xlast=xlast*celerity*gWidth/length; 
      if (xlast<0) {xlast=0;} 
      if (xfirst>gWidth ) {xfirst=gWidth;} 
      //draw 
      g.setColor(Color.red); 
      g.fillRect(gX+(int)(xlast),gY+1,(int)(xfirst-xlast),gHeight-2); 
    } 
  } 

  static private String timeToString(double now) 
  { 
    String res=Double.toString(now*1000); 
    int dot=res.indexOf('.'); 
    String deci=res.substring(dot+1)+"000"; 
    deci=deci.substring(0,3); 
    String inte=res.substring(0,dot); 
    return inte+"."+deci+" ms"; 
  } 
} 

class Packet 
{ 
  double size; 
  double emissionTime; 

  Packet(double s, double eT) 
  { 
    size=s; 
    emissionTime=eT; 
  } 
} 
  
