/*<applet code="bank" width = 500 height=600>
</applet>*/
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.*;
import javax.swing.event.*;
import java.lang.*;
import java.util.*;
import java.io.*;
	public class bank extends JApplet
	{
	
		JButton start,stop;	
		Container contentPane = getContentPane();
		
		public void init()
		{
			 contentPane.add(new testPanel());
			 //contentPane.add(new counters());
		}
	}

	class testPanel extends JPanel implements ActionListener,ChangeListener
    {
	 JPanel Panel1;
	 JButton start, stop;
	 Box b1,b2,b3;
	 JSlider slider1,slider2;
	 JTextField t1,t2,t3;
	 JLabel label1,label2;
	 counters cntpanel;
	 Sim simulate;
		 public testPanel()
		 {
	  	  	Panel1 = new JPanel();
			
			this.setLayout(new GridLayout(2,1));
			Panel1.setLayout(new GridLayout(4,1));
			
			t1 = new JTextField("bank program",20);
			Panel1.add(t1);
			start = new JButton("start");
			start.setActionCommand("start");
			start.addActionListener(this);

		 	stop = new JButton("stop");
			stop.setActionCommand("stop");
			stop.addActionListener(this);

			b1 = Box.createHorizontalBox();
			b1.add(Box.createGlue());
			b1.add(start);
			b1.add(Box.createGlue());
			b1.add(stop);
			b1.add(Box.createGlue());
			Panel1.add(b1);
			
			slider1 = new JSlider();
			label1 = new JLabel("arrival rate");

			
			slider1.setMajorTickSpacing(5);
			slider1.setMinorTickSpacing(1);
			
			slider1.setMinimum(0);
			slider1.setMaximum(10);
			slider1.setValue(1);
			
			slider1.setPaintTicks(true);
			slider1.setPaintLabels(true);

			slider1.addChangeListener(this);	

			
			b2 = Box.createVerticalBox();
			b2.add(Box.createGlue());
			b2.add(label1);
			b2.add(Box.createGlue());
			b2.add(slider1);
			Panel1.add(b2);

			
			slider2 = new JSlider();
			label2  = new JLabel("service rate");
			slider2.setMajorTickSpacing(5);
			slider2.setMinorTickSpacing(1);
			
			slider2.setMinimum(0);
			slider2.setMaximum(10);
			slider2.setValue(1);
			
			slider2.setPaintTicks(true);
			slider2.setPaintLabels(true);

			slider2.addChangeListener(this);	
			
			b3 = Box.createVerticalBox();
			b3.add(Box.createGlue());
			b3.add(label2);
			b3.add(Box.createGlue());
			b3.add(slider2);
			Panel1.add(b3);

			t2 = new JTextField(" ",10);
        	Panel1.add(t2);
			
			t3 = new JTextField(" ",10);
			Panel1.add(t3);
			add(Panel1);
			
			cntpanel = new counters();
			add(cntpanel);
		 }
		 




	public void actionPerformed(ActionEvent e){

  
         if(e.getActionCommand().equals("start"))
	     {                                   
			t1.setText("Program is Started");
			  
			for(int i=0;i<5;i++) 
				cntpanel.setFlag(i,false);
			cntpanel.resetCount();
			cntpanel.setEnd(false);
			//int n=3,time=5,a_rate=12,s_rate=2;

			int n = 5; 
			int time = 25;
			int a_rate = slider1.getValue();
			int s_rate = slider2.getValue();
			simulate = new Sim(time,a_rate,s_rate,n,cntpanel);
			simulate.start();
		}
		 
		 else if(e.getActionCommand().equals("stop"))
		{                                   
			t1.setText("Program is Stopped");
			simulate.setfinish();
			cntpanel.setEnd(true);
		}
	}




	


		public void stateChanged(ChangeEvent evt) 
		{
			JSlider source = (JSlider)evt.getSource();
			if (!source.getValueIsAdjusting())
			{
				if(source == slider2)
				{
   					int srate = (int)source.getValue();
					simulate.setServRate(srate);
			    }
		 		else if(source == slider1)
				{
   					int arate = (int)source.getValue();
					simulate.setArrRate(arate);
       		    }
   		    }
			if (source.getValueIsAdjusting())
			{
				if(source == slider2)
				{
   					int srate = (int)source.getValue();
					t3.setText(Integer.toString(srate));
			    }
		 		else if(source == slider1)
				{
   					int arate = (int)source.getValue();
					t2.setText(Integer.toString(arate));
       		    }
   		    }
		}
}
	

			






 
class counters extends JPanel {
	public counters(){
		count = 0;
		for(int i=0;i<N_COUNT;i++)
			flag[i] = false;
	}

	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		Color clr = getBackground();
		Color clr1 = g.getColor();
//		setBackground(Color.white);
		if(end){
			g.drawString("Simulation Ends",100,7);
		}
			g.setColor(clr1);
			g.drawString("Counters",15,9);
			for(int i=0;i<N_COUNT;i++){
				g.setColor(clr1);
				g.drawString(Integer.toString(i+1),(i+1)*40,24);
				g.drawRect(25+40*i,30,30,30);
				if(flag[i]) 
				{
					g.setColor(Color.green);
					g.fillRect(30+40*i,60,10,10);
				}
				else{
					g.setColor(clr);
					g.fillRect(30+40*i,60,10,10);
					g.setColor(Color.green);
				}
			}

			g.setColor(clr1);
			g.drawString("Customers",80,200);
			g.setColor(Color.pink);

			//for(int i=0;i<50;i++)
			//	g.drawRect(90+13*i,110,10,10);
			for(int i=0;i<count;i++)
				g.fillRect(90+13*i,110,10,10);
			
	}

	public void resetCount(){
		count = 0 ;
	}

	public void incCount(){
		count++ ;
		try{ Thread.sleep(200);}catch(InterruptedException e){}
		repaint();
	}

	public void decCount(){
		count-- ;
		try{ Thread.sleep(200);}catch(InterruptedException e){}
		repaint();
	}

	public void setFlag(int index, boolean value){
		flag[index] = value ;
		try{ Thread.sleep(200);}catch(InterruptedException e){}
		repaint();
	}

	public void setEnd(boolean flag){
		end = flag ;
		repaint();
	}

	private boolean end = false;
	private final int N_COUNT = 5;
	private boolean flag[] = new boolean[N_COUNT];
	private int count;
}

class p_distr
{
	private double avg;	
	private double ran;
	private Random generator;
	public p_distr(){};
	public p_distr(double mn) {
		avg=mn;
	}
	
	public double Next() {	
		generator = new Random(System.currentTimeMillis());
		ran = generator.nextDouble();
		ran/= 352;
		ran = (-1.0/avg) *(Math.log(1-ran));
		return ran;
	}
}

class Event
{
	private int type;   		// type of event
	private	double time;		// time the event will take place
	private	int gen; 			// generator no ( which customer will come when)

	public Event() {}
	
	public Event (int etype, double etime,int egen) {
		type=etype; gen=egen ;time=etime;
	}
	
	public 	Event ( Event e ) {
		type=e.type; time=e.time; gen=e.gen;
	}
		
	public 	int GetType() {
		return type; 
	}
	
	public 	int GetGen() {
		return gen;
	}

	public 	double GetTime() { 
		return time; 
	}

					
}


class EventQueue extends Vector{
	private Event evt;
	public EventQueue(){
		super();
	}
	public void push(Event e){
		int i;
		for(i=0;i<super.elementCount;i++){
			evt = (Event)super.get(i);	
			if(evt.GetTime() > e.GetTime())
				break;
		}
		insertElementAt(e,i);
	}
}


class counter
{
	private int CounterNumber,CustomerNumber;
	private double  EndTime;
	private boolean wait;
	public EventQueue entque;
	private p_distr poisGen;
	private	double servTime;
	private Event e1;

	public counter() {}
	
	public counter(int n,EventQueue que,int i) {
		CounterNumber = n;
		entque = que;
		wait = false;
		poisGen = new p_distr(i);
	}

	public double GetRemTime() { 
		return EndTime;
	}
	
	public boolean IsWait() { 
		return wait; 
	}
	
	public void SetWait(boolean i) {
		wait = i;
	}
	
	public void Processing(double clk,int n, counters mp){	
		mp.setFlag(CounterNumber,true);
		servTime = poisGen.Next()*1000;
		//System.out.println(" Service time of  "+n+" is : "+servTime);
		EndTime = clk + servTime;
		e1= new Event(1,EndTime,CounterNumber);
		wait=true;
		entque.push(e1);
	}
}

class Sim extends Thread
{
	private double GlobClk;   //Global Clock
	private	int arr_rate;
	private	int ser_rate;
	private	int sim_time;
	private	int q_len;
	private	int no_counters;
	private EventQueue que;
	private EventQueue custq;
	private counter[] cnt= new counter[10];
//	private counter[] cnt= new counter[NoOfCounters];
	private p_distr eventpois;
	private Event NextCustomer;
	private Event nextCustomer;
	private Event ent;
	private Event cust;
	public counters SPanel;
	public final int THRESHOLD = 4;
	boolean finish;
	
	public Sim() {}
	public Sim(int st,int ar, int sr,int nc,counters mp) {
		GlobClk = 0;  
		finish=false;
		q_len = 0;
		sim_time = st; arr_rate = ar; ser_rate = sr;
		no_counters = nc;
		SPanel = mp;
		eventpois = new p_distr(arr_rate);	
		que = new EventQueue();
		for (int i=0; i<nc; i++)
			cnt[i] = new counter(i,que,ser_rate);
	 	NextCustomer = new Event(0,0,0);
		custq = new EventQueue();
	 	que.push(NextCustomer);
	 	custq.push(NextCustomer);
	}
		
	public void setfinish(){
		this.suspend();
		finish=true;
		this.resume();
	}

	public void setArrRate(int ar){
		arr_rate = ar;
	}

	public void setServRate(int sr){
		ser_rate = sr;
	}

	public void run() {

		ent = new Event();
		cust = new Event();
		double cum_wait=0,avg_wait=0,wt=0,cum_qlength=0,a_qlen=0,Prev_clk=0;
		double frac_time=0,ex_tm=0,cum_ex_tm=0;
		int count = 0,no_served=0;
		int cust_no=1;

		//System.out.println("Globclk = "+GlobClk+"simulation time = "+sim_time);
		while(!finish) {
			//System.out.println("Globclk = "+GlobClk+"simulation time = "+sim_time);
			ent = (Event)que.firstElement();
			que.removeElementAt(0);
			//ent.showOutput();

			switch (ent.GetType()) {
				case 0:
					{
						q_len++;////////////////////////////////////
						SPanel.incCount();
						//System.out.println("Customer inserted in queue; queue length :  " + q_len);
						GlobClk = ent.GetTime();
						nextCustomer = new Event(0,GlobClk+eventpois.Next()*500,cust_no);
						++cust_no;
						que.push(nextCustomer);
						custq.push(nextCustomer);
					}
					break;
				case 1:
					{
						int CounterNumber = ent.GetGen();
						cnt[CounterNumber].SetWait(false);
						SPanel.setFlag(CounterNumber,false);
						GlobClk = ent.GetTime();
					}
					break;
			};
			wt=0;
			if (q_len > 0) {
				for (int i =0 ; i < no_counters; i++) {
					if (!(cnt[i].IsWait())) {
						cust = (Event)custq.firstElement();
						custq.removeElementAt(0);
						cnt[i].Processing(GlobClk,cust.GetGen(),SPanel);
						q_len--;///////////////////////////////////////
						SPanel.decCount();
						wt = GlobClk - cust.GetTime();
						//System.out.println("waiting time for the customer "+cust.GetGen()+"is "+wt);
						no_served++;   //////////////
						break;
					}
				}
			
			}
			count ++;
			if (q_len>THRESHOLD)
			  ex_tm = GlobClk - Prev_clk;
				else
				ex_tm=0.0;
			cum_ex_tm += ex_tm;
			if (GlobClk != 0)
				frac_time = cum_ex_tm/GlobClk;
			else
				frac_time = 0;
			cum_qlength += q_len;
			cum_wait += wt;
			avg_wait += cum_wait/no_served;
			a_qlen = cum_qlength/count;
			//System.out.println("GlobClk = "+GlobClk+" a_qlen = "+a_qlen+" frac_time = "+frac_time+" avg_wait = "+avg_wait);
			Prev_clk = GlobClk;
		}
	}
}

