/*********************************************
 * Written by Nick Exner (6/2/98)				*
 * Updated by: Michael McKelvey (10/29/01)	*
 * Free to distribute								*
 *********************************************/
//<APPLET CODE="Birthday.class" WIDTH=552 HEIGHT=372></APPLET>

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.text.*;

public class Birthday extends Applet
{
//	boolean statusNum = false;
	boolean tempStatus = false;
	final Color myColor = new Color(200,200,255);
	TextField inputB;
	TextArea ta;
 	Button calcButton;
 	Button run100Button;
 	Button resetButton;
 	int arrayDate[],
	    c=0,
//	    oldNum=15,
	    expProb;
	
	Image cake;
	String s[];
	double average;
	int trialNum;
	Label l_duplicates,
			l_average,
			l_diff,
			l_trialNum,
			l_expProb;
	
	ResultPanel rp;

	public void init()
	{
		rp = new ResultPanel(getImage(getCodeBase(),"cake.jpg"),(Applet)this);
		setBackground(myColor);
		setLayout(new BorderLayout());
		expProb=0;
		average = 0;
		trialNum =0 ;
		Panel p = new Panel();
	
		Label inputA = new Label("Enter number of Birthdays to calculate: ");
		inputB = new TextField(3);
		setNumBDays(15);
		calcButton = new Button("Calculate");
		run100Button = new Button("Run 100 Trials");
		resetButton = new Button("Reset");
		p.add (inputA);
		p.add (inputB);
		p.add (calcButton);
		p.add (run100Button);
		p.add (resetButton);
		add("North",p);
	
		Panel p6 = new Panel();
		p6.setLayout(new BorderLayout());
	
		Panel p4 = new Panel();
		p4.setLayout(new GridLayout(5,1));
	
		Panel p5 = new Panel();
		p5.setLayout(new GridLayout(5,1));
	
		l_duplicates = new Label("0");
		l_average = new Label("0");
		l_diff = new Label("0");
		l_trialNum = new Label("0");
		l_expProb = new Label("0");
	
		Label l1 = new Label("Number of dates duplicated: ");
		//l1.setForeground(Color.red);
		
		Label l2 = new Label("Average # of dates duplicated: ");
		//l2.setForeground(Color.blue);
		
		Label l3 = new Label("Experimental probability: ");
		l3.setForeground(Color.blue);
	
		p4.add(l1);
		p5.add(l_duplicates);
	
		p4.add(l2);
		p5.add(l_average);
		
		p4.add(new Label("Trial effect on average: "));
		p5.add(l_diff);
	
		p4.add(l3);
		p5.add(l_expProb);
	
		p4.add(new Label("Number of trials: "));
		p5.add(l_trialNum);
	
		p6.add("West",p4);
		p6.add("Center",p5);
	
		Panel p3 = new Panel();
		p3.setLayout(new GridLayout(2,1));
		p3.add(rp);
		p3.add(p6);

		Panel p2 = new Panel();
		p2.setLayout(new GridLayout(1,2));
		ta = new TextArea(20,30);
		ta.setText("Click in the grid or type a number in the text\n" +
						"box to select the number of birthdays.\n" +
						"Then click Calculate a few times to see\n" +
						"the likelihood that 2 people in a group\n" +
						"of that size have the same birthday.");
		p2.add(ta);
		p2.add(p3);
	
		add("Center",p2);
		rp.repaint();
		repaint();
		start();
	}

//	public void reset(int val)
//	{

//		statusNum = false;
//	}

	public void resetApplet()
	{
//		reset();
		setNumBDays(15);
		ta.setText("Click in the grid or type a number in the text\n" +
						"box to select the number of birthdays.\n" +
						"Then click Calculate a few times to see\n" +
						"the likelihood that 2 people in a group\n" +
						"of that size have the same birthday.");
		l_duplicates.setText("0");
		l_average.setText("0");
		l_diff.setText("0");
		l_expProb.setText("0");
		l_trialNum.setText("0");
		rp.reset();		
	}

	public void createTrial(int n)
	{
	 	arrayDate = new int [n];
		s 	    = new String[n];
		
		for (int i=0; i<n; i++)
		{
			arrayDate[i] = 1+(int) (Math.random() * 365);
			s[i] = convertToMonth(arrayDate[i]);
		}
		
		if(!rp.hasModified[n-1])
		{
			rp.duplicates[n-1] = 0;
			rp.value[n-1] = 0;
			rp.expProb[n-1] = 0;
			rp.numDup[n-1] = 0;
			rp.avgDup[n-1] = 0;
			rp.effect[n-1] = 0;
			rp.trials[n-1] = 0;
		}
		
//		statusNum=true;
		c=n;
//		trialNum++;
	}

	public void runTrial(int current)
	{
		if(rp.changeVal > 0)
		{
			setNumBDays(rp.changeVal);
			rp.changeVal = 0;
		}
		else
		{
			StringBuffer b = new StringBuffer();
			int t;
			synchronized (this)
			{
				t = countDuplicates(c);
			}
			if (current >= 1 && current <= 60)
			{
				rp.trials[current]++;
				rp.effect[current] = (double)Math.abs(Math.abs(rp.avgDup[current]/(rp.trials[current]-1))-Math.abs((rp.avgDup[current]+t)/rp.trials[current]));
				rp.numDup[current]+=t;
				rp.avgDup[current] = (double)(rp.numDup[current])/rp.trials[current];

				if (t > 0)
				{
					rp.duplicates[current]++;
				}
				rp.value[current] = (int)Math.round(150*((rp.duplicates[current]*1.0)/rp.trials[current]));
				rp.expProb[current] = (rp.duplicates[current]*1.0)/rp.trials[current];
				
System.out.println("rp.trials["+current+"]: " + rp.trials[current]);
System.out.println("rp.numDup["+current+"]: " + rp.numDup[current]);
System.out.println("rp.avgDup["+current+"]: " + rp.avgDup[current]);
System.out.println("rp.effect["+current+"]: " + rp.effect[current]);
System.out.println("rp.duplicates["+current+"]: " + rp.duplicates[current]);
System.out.println("rp.value["+current+"]: " + rp.value[current]);
System.out.println("rp.expProb["+current+"]: " + rp.expProb[current]);

				l_duplicates.setText(""+t);
				l_diff.setText(""+round(rp.effect[current]));
				l_average.setText(""+round(rp.avgDup[current]));
				l_trialNum.setText(""+rp.trials[current]);
				l_expProb.setText(""+round(rp.expProb[current]));
			
				for (int i=0; i<c; i++) 
					b.append(""+s[i]+"\n");
			
				ta.setText(b.toString());
				rp.hasModified[current] = true;
			}
			else
			{
				
			}
		}
		repaint();
	}

	public void paint(Graphics g)
	{
		if(rp.changeVal > 0)
		{
			setNumBDays(rp.changeVal);
			rp.changeVal = 0;
			
			int bdays = (Integer.parseInt((String)inputB.getText())) - 1;
			if(rp.hasModified[bdays])
			{
				l_duplicates.setText("");
				l_diff.setText(""+round(rp.effect[bdays]));
				l_average.setText(""+round(rp.avgDup[bdays]));
				l_trialNum.setText(""+rp.trials[bdays]);
				l_expProb.setText(""+round(rp.expProb[bdays]));
			}
			else
			{
				l_duplicates.setText("0");
				l_diff.setText("0");
				l_average.setText("0");
				l_trialNum.setText("0");
				l_expProb.setText("0");
				
			}
		}
	}

	public boolean action(Event evt, Object arg) {
		if(evt.target == calcButton || evt.target instanceof TextField)
		{
			int temp = (Integer.parseInt((String)inputB.getText()));
			if (temp > 2000)
			{
				temp = 2000;
				inputB.setText(""+temp);
			}
//			if (temp != oldNum){
//				reset(temp);
//				oldNum = temp;
//			} 
			createTrial(temp);
			runTrial(temp-1);
		}
		else if(evt.target == run100Button)
		{
			int temp = (Integer.parseInt((String)inputB.getText()));
			if (temp > 2000)
			{
				temp = 2000;
				inputB.setText(""+temp);
			}

			for(int i=0;i<100;i++)
			{
//				if (temp != oldNum)
//				{
//					reset();
//					oldNum = temp;
//				} 
				createTrial(temp);
				runTrial(temp-1);
			}
		}
		else if(evt.target == resetButton)
		{
			resetApplet();
		}

		repaint();
		rp.repaint();
		return true;
	}

	public double round(double x)
	{
		return Math.round(x*1000000000.0)/1000000000.0;
//		return ""+Math.round(x*1000000000.0)/1000000000.0;
	}

	public int countDuplicates(int n)
	{
		int a[] = new int[n];
		int num[] = new int[n];

		for (int i=0;i<n;i++)
		{
			a[i] = arrayDate[i];
			num[i]=i;
		}

		//Implementation of Bubble Sort Algorithm
		int temp = 0,
		    t2 = 0;
		for (int i=0; i<n-1; i++)
		  	for (int j=0; j<n-1-i; j++)
		    		if (a[j+1] < a[j])
		    		{  
		      		temp = a[j];      
		      		a[j] = a[j+1];
		      		a[j+1] = temp;    
	
						t2 		= num[j];
						num[j] 	= num[j+1];
						num[j+1]	= t2;
			  		}

		int counter = 0;
		for (int i=1; i < n-1; i++) 
			if (a[i] == a[i+1] && a[i] != a[i-1])
				counter++;

	//Check to see if duplicate date generated
			for (int i=1; i < n-1; i++)
		  		if (a[i] == a[i+1] || a[i] == a[i-1])
					if (!s[(num[i])].startsWith("*")) 
						s[(num[i])]="* "+s[(num[i])];
			if (n >= 2 && a[n-1] == a[n-2])
				if (!s[(num[n-1])].startsWith("*")) 
					s[(num[n-1])]="* "+s[(num[n-1])];
			if (n >= 2 && a[0] == a[1])
				if (!s[(num[0])].startsWith("*")) 
					s[(num[0])]="* "+s[(num[0])];
		return counter;
	}

	public String convertToMonth(int d)
	{
		String s = "";
		if (d >=0 && d <=31)
			s=("January " + (d));
	
		else if (d >31 && d <=59)
			s=("February " + (d % 31));
	
		else if (d >59 && d <=90)
			s=("March " + (d % 59));
	
		else if (d >90 && d <=120)
			s=("April " + (d % 90));
	
		else if (d >120 && d <=151)
			s=("May " + (d % 120));
		
		else if (d >151 && d <=181)
			s=("June " + (d % 151));
	
		else if (d >181 && d <=212)
			s=("July " + (d % 181));
	
		else if (d >212 && d <=243)
			s=("August " + (d % 212));
		
		else if (d >243 && d <=273)
			s=("September " + (d % 243));
	
		else if (d >273 && d <=304)
			s=("October " + (d % 273));
		
		else if (d >304 && d <=334)
			s=("November " + (d % 304));
	
		else if (d >334 && d <=365)
			s = ("December " + (d % 334));
		return s;
	}

	public void setNumBDays(int numBDays)
	{
		inputB.setText(""+numBDays);
	}

} // End class


/////////////////////////////////////////////
///////////////               ///////////////
///////////////  ResultPanel  ///////////////
///////////////               ///////////////
/////////////////////////////////////////////

class ResultPanel extends Panel
{
	int w = 250,
	    h =160;
	String s[];
	public int changeVal;
	Image cake;
	Applet parent;
	
	int		duplicates[];	//incremented one every trial that has any duplicates
	int		value[];
	double	expProb[];
	int		numDup[];		//total number of duplicates across the trials
	double	avgDup[];		//average # of duplicates across the trials
	double	effect[];
	int		trials[];
	boolean	hasModified[];
	
	public boolean showPopup;
	boolean isMouseDown = false;
	boolean mouseIn = false;
	int popupXCoord = 0;
	int popupYCoord = 0;
	int popupXVal = 0;
	double popupYVal = 0;
	int mouseDownX = 0;
	int mouseDownY = 0;
	Color popupbgColor = new Color(255,255,204);
	Color grayColor = new Color(204,204,204);
	DecimalFormat precision;

	public ResultPanel(int w, int h, Applet parent)
	{
		this.w = w;
		this.h = h;
		reset();
	}

	public ResultPanel(Image cake, Applet parent)
	{
		this.cake = cake;
		this.parent = parent;
		reset();
	}
	
	public void reset()
	{
		hasModified = new boolean[60];
		duplicates = new int[60];
		value = new int[60];
		expProb = new double[60];
		numDup = new int[60];
		avgDup = new double[60];
		effect = new double[60];
		trials = new int[60];
		for(int i=0;i<60;i++)
		{
			hasModified[i] = false;
			duplicates[i] = -1;
			expProb[i] = -1;
			value[i] = -1;
			numDup[i] = -1;
			avgDup[i] = -1;
			effect[i] = -1;
			effect[i] = -1;
		}
		changeVal = 0;
		showPopup = false;
		isMouseDown = false;
		mouseIn = false;
		popupXCoord = 0;
		popupYCoord = 0;
		mouseDownX = 0;
		mouseDownY = 0;
		popupXVal = 0;
		popupYVal = 0;
		precision = new DecimalFormat();
		precision.setMinimumFractionDigits(3);
	}


	public void paint(Graphics g)
	{
		g.setColor(Color.white);
		g.fillRect(5,5,w-10,h-10);
		
		if(mouseIn)
		{
			g.setColor(popupbgColor);
			g.drawRect(4,4,w-8,h-8);
		}

		g.setColor(grayColor);
		if(isMouseDown)
		{
			g.drawLine(mouseDownX,6,mouseDownX,h-1);
			g.drawLine(mouseDownX+1,6,mouseDownX+1,h-1);
		}
		
		//grayColor
		g.drawLine(5,h-5-(int)Math.round(150*.25),w-5,h-5-(int)Math.round(150*.25));
		g.drawLine(5,h-5-(int)Math.round(150*.50),w-5,h-5-(int)Math.round(150*.50));
		g.drawLine(5,h-5-(int)Math.round(150*.75),w-5,h-5-(int)Math.round(150*.75));
	
		g.setColor(Color.black);
		g.setFont(new Font("Times New Roman", Font.PLAIN, 10));
		g.drawString("5",20,h+7);
		for(int i=2;i<=11;i++)
			g.drawString(""+i*5,i*20 - 2,h+7);
		
		g.setFont(new Font("Times New Roman", Font.PLAIN, 12));
		
		g.setColor(Color.blue);
		for (int i=0;i<60;i++)
		{
			if (value[i] > 0)
				g.fillOval(5+i*4,h-6-value[i],4,4);
			else if(value[i] == 0)
				g.fillOval(5+i*4,h-7-value[i],4,4);
		}
		
		g.setColor(Color.black);
		g.drawRect(5,5,w-10,h-10);
		g.drawString("1",w,17);
		g.drawString(".75",w,49);
		g.drawString(".5",w,83);
		g.drawString(".25",w,120);
		g.drawString("0",w,154);
		
		if(showPopup)
		{
			int showY = popupYCoord;
			int showX = popupXCoord;
			g.setColor(popupbgColor);
			g.fillRect(showX,showY,60,16);
			g.setColor(new Color(255,187,51));
			g.drawRect(showX,showY,60,16);
			g.setColor(Color.black);
			g.drawString("("+popupXVal+","+precision.format(popupYVal)+")",showX+3,showY+12);
		}
	}
	
	public boolean mouseDown(Event evt, int x, int y)
	{
		System.out.println("X: "+x+" Y: "+y);
		if(x < w-5 && x > 5)
			if(y < h-5 && y > 5)
{
		int tempVal = (x-5)/4+1;
		if(tempVal >= 1 && tempVal <= 60 && tempVal != changeVal)
			changeVal = tempVal;

		isMouseDown = true;
//		if(x < w-5 && x > 5)
//			if(y < h-10 && y > 26)
				mouseDownX = 2+(tempVal)*4;
		mouseDownY = y;
		
		popupXVal = tempVal;
		if(popupXVal >= 1 && popupXVal <= 60)
			popupYVal = expProb[popupXVal-1];
		
		popupYCoord = value[popupXVal-1];
		showPopup = false;
		if(popupYCoord >= 0)
				if(y >= h-8-popupYCoord && y <= h-4-popupYCoord)
			{
				showPopup = true;
				
				popupXCoord = x;
				if(popupXCoord >= w-60)
					popupXCoord -= 60;
				
				popupYCoord = y;
				if(popupYCoord >= h-16)
					popupYCoord -= 16;
			}
		
		parent.repaint();
		repaint();
}
		return true;
	}
	
	public boolean mouseEnter(Event evt, int x, int y)
	{
		mouseIn = true;
		repaint();
		return true;
	}
	
	public boolean mouseExit(Event evt, int x, int y)
	{
		mouseIn = false;
		repaint();
		return true;
	}
	
} // End Class