// Queue.java

// Written by Julian Devlin, 8/97, for the text book
// "Introduction to Probability," by Charles M. Grinstead & J. Laurie Snell

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

public class Queue extends java.applet.Applet
{
	DLLFloat lx;
	DLLFloat ly;
	Float[] waitX;
	Float[] waitY;
	Float[] lenX;
	Float[] lenY;
	Float[] lenDistX;
	Float[] lenDistY;
	
	LineGraph lg;				// AWT elements
	AreaBarGraph abg, qlbg;
	Label numl1, numl2, numl3;			// Controls
	Label qlLab, wlLab, qldLab;
	TextField num1, num2, num3;
	Button go;
	
		
	Panel graphArea;
	Panel controls;
	Panel qlp, wlp, qldp;
	
	GridBagLayout gbl;
	GridBagConstraints cc;
	
	JRandom myRand;

	// Set up all controls and put them in the window
	public void init() {
		numl1 = new Label("Avg. arrival time =");	// Create controls
		num1 = new TextField("2", 2);
		numl2 = new Label("Avg. service time =");
		num2 = new TextField("1", 2);
		numl3 = new Label("Total time =");			// Create controls
		num3 = new TextField("1000", 4);
		go = new Button("Simulate");
		
		graphArea = new Panel();				// Set up window
		controls = new Panel();
		
		qlp = new Panel();
		wlp = new Panel();
		qldp = new Panel();
		qlp.setLayout(new BorderLayout());
		wlp.setLayout(new BorderLayout());
		qldp.setLayout(new BorderLayout());
		qlLab = new Label("Queue Length");
		wlLab = new Label("Wait Time Distribution");
		qldLab = new Label("Queue Length Distribution");
		qlp.add("South", qlLab);
		wlp.add("South", wlLab);
		qldp.add("South", qldLab);
		
		setLayout(new BorderLayout(5, 5));
	
		gbl = new GridBagLayout();
		cc = new GridBagConstraints();
		controls.setLayout(gbl);
		cc.gridx = 0;
		cc.gridy = 0;
		gbl.setConstraints(numl1, cc);
		controls.add(numl1);
		
		cc.gridx = 1;
		gbl.setConstraints(num1, cc);
		controls.add(num1);
		
		cc.gridx = 0;
		cc.gridy = 1;
		gbl.setConstraints(numl2, cc);
		controls.add(numl2);
		
		cc.gridx = 1;
		gbl.setConstraints(num2, cc);
		controls.add(num2);
		
		cc.gridx = 0;
		cc.gridy = 2;
		gbl.setConstraints(numl3, cc);
		controls.add(numl3);
		
		cc.gridx = 1;
		gbl.setConstraints(num3, cc);
		controls.add(num3);
		
		cc.gridx = 0;
		cc.gridy = 3;
		cc.gridwidth = 2;
		gbl.setConstraints(go, cc);
		controls.add(go);
		
		/*Float[] xx1 = new Float[2];
		Float[] yy1 = new Float[2];
		xx1[0] = new Float(0);
		xx1[1] = new Float(1);
		yy1[0] = new Float(0);
		yy1[1] = new Float(0);
		Float[] xx2 = xx1;
		Float[] xx3 = xx1;
		Float[] yy2 = yy1;
		Float[] yy3 = yy1;*/
		lg = new LineGraph(); // initialize a graphing space
		abg = new AreaBarGraph();
		qlbg = new AreaBarGraph();
		add("Center", graphArea);
		graphArea.setLayout(new GridLayout(3, 1));
		graphArea.add(qlp);
		graphArea.add(wlp);
		graphArea.add(qldp);
		qlp.add("Center", lg);
		wlp.add("Center", abg);
		qldp.add("Center", qlbg);
		add("South", controls);
		
		myRand = new JRandom();
	}
	
	// Make random numbers according to distribution
	public float nextRand(float l) {
		float temp = myRand.nextFloat(0, 1);
		return (float) (Math.log(1 - (double) temp) / (-1 * l));
	}
	
	// Does the simulation, creating two arrays to store game states, then passes them
	// to a AreaBarGraph
	public void simulate(float l, float m, float t) {
		int[] wy;
		
		lx = new DLLFloat();
		ly = new DLLFloat();
	
		wy = new int[11];					// Choose different interval??
		wy[0] = 0;
		waitX = new Float[11];
		waitY = new Float[11];
	
		float lastArrive = 0;
		float lastService = 0;
		float arrive, service;
		int people = 0;
		lx.add(new Float(0));
		ly.add(new Float(0));
		while (lastArrive < t && lastService < t) {			// Keep going until simulation time runs out
			people++;
			arrive = nextRand(l);
			service = nextRand(m);
			lastArrive += arrive;
			if (lastArrive >= lastService) {				// If everyone has been service ( length == 0 )
				lastService = lastArrive + service;		// service time is service after arrival time
				lx.end();
				ly.end();
				lx.add(new Float(lastArrive));			// we arrive, making the length of the q be 1
				ly.add(new Float(1));
				lx.add(new Float(lastService));			// and leave - it is zero again
				ly.add(new Float(0));	
			}
			else {											// already people waiting
				lastService += service;					// add service time to lastService
				lx.end();
				ly.end();
				lx.add(new Float(lastService));			// At that time, decrease q length by one
				ly.add(new Float(ly.curVal().floatValue() - 1));
				// bump up all counts after this arrival
				while(lx.curVal().floatValue() > lastArrive) {		// Should check for beginning, but there
					ly.setCurVal(new Float(ly.curVal().floatValue() + 1));	// will always be a first customer whose
					lx.back(1);											// arrival time is guaranteed to be before
					ly.back(1);											// this one.
				}
				lx.add(new Float(lastArrive));						// insert the actual arrival
				ly.add(new Float(ly.curVal().floatValue() + 1));
			}
			if ((lastService - lastArrive) <= 50)						// keep track of wait times of up to fifty
				wy[(int) Math.ceil((double) (lastService - lastArrive) / 5)]++;	// bar graph to closest 5
		}
		
		while (lastArrive < t) {			// add in extra arrivals, even if services are past limit
			arrive = nextRand(l);
			lastArrive += arrive;	
			lx.end();
			ly.end();
			while(lx.curVal().floatValue() > lastArrive) {		// Should check for beginning, but there
				ly.setCurVal(new Float(ly.curVal().floatValue() + 1));	// will always be a first customer whose
				lx.back(1);											// arrival time is guaranteed to be before
				ly.back(1);											// this one.
			}
			lx.add(new Float(lastArrive));						// insert the actual arrival
			ly.add(new Float(ly.curVal().floatValue() + 1));
		}
		
		// The wait time of the customers is only recorded for those who are completely processed in the time restraint.
		for (int i = 0; i < waitX.length; i++) {
			waitX[i] = new Float(i * 5);
			waitY[i] = new Float((float) wy[i] / (float) people);
		}
		
		qlp.remove(lg);
		wlp.remove(abg);
		qldp.remove(qlbg);
		qlp.remove(qlLab);
		wlp.remove(wlLab);
		qldp.remove(qldLab);
		
		lenX = lx.toArray();
		lenY = ly.toArray();
		lenDistX = new Float[11];					// do a distribution of queue lengths of up to 50
		lenDistY = new Float[11];
		for (int i = 0; i < lenDistX.length; i++) {
			lenDistX[i] = new Float(i * 5);
			lenDistY[i] = new Float(0);
		}
		lenDistY[0] = new Float((lenX[0].floatValue() - 0f) / (float) t);
		for (int i = 1; i < lenX.length; i++) {
			if (lenY[i].floatValue() <= 50) {
				lenDistY[(int) Math.ceil((double) lenY[i].floatValue() / 5)] = new Float(
					lenDistY[(int) Math.ceil((double) lenY[i].floatValue() / 5)].floatValue() +
					(lenX[i].floatValue() - lenX[i - 1].floatValue()) / (float) t);
			}
		}
		lg = new LineGraph(lenX, lenY);
		abg = new AreaBarGraph(waitX, waitY);	// Create new AreaBarGraph
		qlbg = new AreaBarGraph(lenDistX, lenDistY);
		qlp.add("Center", lg);
		wlp.add("Center", abg);
		qldp.add("Center", qlbg);	
		
		qlp.add("South", qlLab);
		wlp.add("South", wlLab);
		qldp.add("South", qldLab);
		
		validate();
		
		// Debugging stuff
		/*		
		Dimension d;
		d = lg.size();
		qlLab.setText(String.valueOf(d.width) + "   " + String.valueOf(d.height));
		d = abg.size();
		wlLab.setText(String.valueOf(d.width) + "   " + String.valueOf(d.height));
		d = qlbg.size();
		qldLab.setText(String.valueOf(d.width) + "   " + String.valueOf(d.height));
					*/	
		validate();
	}
	
	// Watch for a click on the go button
	public boolean action(Event evt, Object arg) {
		if (evt.target instanceof Button) {
			if (evt.target == go && evt.id == Event.ACTION_EVENT)	// When button is clicked
			{
				simulate(1f / Float.valueOf(num1.getText()).floatValue(),
        			1f / Float.valueOf(num2.getText()).floatValue(),
        			Float.valueOf(num3.getText()).floatValue());
        		return true;					// Generate correct number of tosses
			}
		}
		return super.handleEvent(evt);	// Handle other events as usual
	}
	
	public Insets insets() {
    	return new Insets(5,5,5,5);
	}
}