///////////////////////////////////////
//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;
}
}