/*		"My first JAVA program!"
 * Copyright (c) 1996 Superior Software. All Rights Reserved.
 *	Copy freely, but don't sell this program or remove this copyright notice.
 *		Don_Corley@msn.com

 * Although there's not much of the original code left, this is based on the following code:

 *	Thanks Sami!

 * @(#)ArcTest.java	1.9 95/09/01 Sami Shaio

 *

 * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved.

 */

//+ I Can't figure out how to add a carriage return to a TextArea
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Rectangle;
import java.awt.TextField;
import java.util.Date;

//****************************************************************

// Biorythm - Main (Applet) class

//****************************************************************

public class Biorythm extends Applet {

	BiorythmControls controls;

	// + TextArea textArea = null;

	public void init() {
		String strBirthdate;
		String strName;
		strBirthdate = getParameter("birthdate");
		strName = getParameter("name");

		String strTextArea = getParameter("textarea");

		setLayout(new BorderLayout());
		BiorythmCanvas c = new BiorythmCanvas();
		c.init(this, strBirthdate);
		add("Center", c);
		add("North", controls = new BiorythmControls(c, strName));
		// + if (strTextArea == null)
		// + add("South", textArea = new TextArea(6, 60));
		BiorythmWave wHack = new BiorythmWave(); // **HACK** Performance -
		// Load up this class now,
		// or pay the price (1 min)
		// later
	}

	// ----------------------------------------------------------------
	// start

	public void start() {
		controls.enable();
	}

	// ----------------------------------------------------------------
	// stop
	public void stop() {
		controls.disable();
	}

	// ----------------------------------------------------------------
	// handleEvent
	public boolean handleEvent(Event e) {
		if (e.id == Event.WINDOW_DESTROY) {
			System.exit(0);
		}
		return false;
	}

	// ----------------------------------------------------------------
	// main
	public static void main(String args[]) {
		Frame f = new Frame("Biorythm");
		Biorythm biorythmTest = new Biorythm();
		biorythmTest.init();
		biorythmTest.start();
		f.add("Center", biorythmTest);
		f.resize(300, 300);
		f.show();
	}
}

// ****************************************************************
// BiorythmCanvas - Canvas to paint the biorythm graph
// ****************************************************************
class BiorythmCanvas extends Canvas {
	Font font;
	Biorythm m_biorythm; // App reference
	EDate m_dateNow;
	EDate m_dateStart;
	EDate m_dateEnd;
	EDate m_dateBirth;
	static final double kOneDay = 24 * 60 * 60 * 1000;
	static final int kDayOne = 1;
	static final int kMonthJanuary = 0;
	static final int kMonthDecember = 11;
	static final int kPhysicalCycle = 23;
	static final int kEmotionalCycle = 28;
	static final int kIntellectualCycle = 33;

	// ----------------------------------------------------------------
	// init
	public synchronized void init(Biorythm biorythm, String strBirthdate) {
		this.m_biorythm = biorythm;
		m_dateNow = new EDate(); // Today's date
		m_dateBirth = new EDate(); // Today's date
		if (strBirthdate != null)
			m_dateBirth.parseString(strBirthdate);
		if (strBirthdate == null)
			m_dateBirth.setDefaultTimeOfDay(m_dateBirth.getHours(), m_dateBirth
					.getMinutes());
		else
			m_dateBirth.setDefaultTimeOfDay(12, 0); // Dates entered without
		// time will default to
		// 12:00 noon
		m_dateStart = new EDate();
		m_dateStart.setDefaultTimeOfDay(0, 0);
		m_dateStart.setDate(kDayOne);
		m_dateStart.setHours(0);
		m_dateStart.setMinutes(0);
		m_dateStart.setSeconds(0);
		m_dateEnd = new EDate(m_dateStart.dGetTime());
		int iMonth = m_dateEnd.getMonth();
		if (iMonth < kMonthDecember)
			m_dateEnd.setMonth(iMonth + 1);
		else {
			m_dateEnd.setMonth(kMonthJanuary);
			m_dateEnd.setYear(m_dateEnd.getYear() + 1);
		}
		for (int i = 0; i < 100; i++) {
			m_dateEnd.dSetTime(m_dateEnd.dGetTime() - (1000 * 30)); // Minus 30
			// seconds
			if (m_dateEnd.getDate() != kDayOne)
				break; // Last time of previous month
		}
		m_dateEnd.setDefaultTimeOfDay(23, 59);
	}

	// ----------------------------------------------------------------
	// dateConvertXtoDate - Convert the grid x value to the Date
	public EDate dateConvertXtoDate(int x) {
		double dTimeSpan = m_dateEnd.dGetTime() - m_dateStart.dGetTime();
		double dWidth = size().width;
		double dX = x;
		double timeX = (long) (dTimeSpan * (dX / dWidth));
		EDate dateX = new EDate(m_dateStart.dGetTime() + timeX);
		return dateX;
	}

	// ----------------------------------------------------------------
	// iConvertStrengthToY - Convert the value (range 1 to -1) to a grid y value
	public int iConvertStrengthToY(double dGraphYValue) {
		int height = size().height;
		int width = size().width;
		int iGraphMargin;
		if (height > 150)
			iGraphMargin = 14 * 2;
		else
			iGraphMargin = (int) (height * .2); // 80%
		// Now, Convert to the y position on the screen
		int iOrigin = (int) (height / 2);
		double dScaleValue = height / 2 - iGraphMargin;
		int y = (int) (dGraphYValue * dScaleValue); // Weight of value
		y = -y; // " on reversed axis
		y = y + iOrigin; // Position if axis went
		return y;
	}

	// ----------------------------------------------------------------
	// paint - Paint the canvas
	public void paint(Graphics g) {
		Rectangle r = bounds();
		// Draw the horizontal lines every 10 pixels
		g.setColor(Color.pink);
		for (int i = r.height / 2; i >= 0; i = i - 10) {
			g.drawLine(0, i, r.width, i);
		}
		for (int i = r.height / 2; i <= r.height; i = i + 10) {
			g.drawLine(0, i, r.width, i);
		}
		// Draw the vertical lines every day break
		int sx = 10;
		int sy = r.height - 42;
		int iStartDay = 0;
		int iEndDay = 0;
		int iStartBox = 0;
		String strDate;
		int istringWidth;
		for (int x = 0; x < size().width; x++) {
			if (dateConvertXtoDate(x).getDate() != dateConvertXtoDate(x + 1)
					.getDate()) {
				g.drawLine(x, 0, x, r.height);
				iEndDay = x;
				if (iEndDay - iStartDay > 10) { // Put the day in the middle of
					// the box
					g.setColor(Color.black);
					strDate = dateConvertXtoDate(x).toString().substring(4, 6);
					if (strDate.charAt(0) == '0')
						strDate = strDate.substring(1, 2);
					istringWidth = g.getFontMetrics().stringWidth(strDate);
					sx = iStartBox + (iEndDay - iStartBox - istringWidth) / 2;
					sy = 10;
					g.drawString(strDate, sx, sy);
					g.setColor(Color.pink);
					iStartDay = iEndDay;
				}
				iStartBox = iEndDay;
			}

			if (dateConvertXtoDate(x).dGetTime() < m_dateNow.dGetTime())
				if (dateConvertXtoDate(x + 1).dGetTime() >= m_dateNow
						.dGetTime()) {
					g.setColor(Color.white);
					g.drawLine(x, 0, x, r.height);
					g.setColor(Color.pink);
				}
		}
		g.setColor(Color.black);
		g.setFont(font);
		g.drawLine(0, r.height / 2, r.width, r.height / 2);
		// Draw the legend
		g.setColor(Color.red);
		sx = 10;
		sy = r.height - 7;
		g.drawLine(sx, sy, sx + 20, sy);
		g.drawString("Physical Cycle", sx + 25, sy + 5);
		g.setColor(Color.green);
		sx = r.width / 2 - 30;
		g.drawLine(sx, sy, sx + 20, sy);
		g.drawString("Emotional Cycle", sx + 25, sy + 5);
		g.setColor(Color.blue);
		sx = r.width - 10 - 100;
		g.drawLine(sx, sy, sx + 20, sy);
		g.drawString("Intellectual Cycle", sx + 25, sy + 5);
		// Now, Draw the sine wave(s)
		BiorythmWave wPhysical = new BiorythmWave(this, g, kPhysicalCycle);
		BiorythmWave wEmotional = new BiorythmWave(this, g, kEmotionalCycle);
		BiorythmWave wIntellectual = new BiorythmWave(this, g,
				kIntellectualCycle);

		for (int x = 0; x < size().width; x++) {
			wPhysical.drawLine(x);
			wEmotional.drawLine(x);
			wIntellectual.drawLine(x);
		}

		// Now, put in all the criticals, etc
		String strDesc = new String();
		for (int x = 0; x < size().width; x++) {
			strDesc += wPhysical.drawTickmarks(x, r);
			strDesc += wEmotional.drawTickmarks(x, r);
			strDesc += wIntellectual.drawTickmarks(x, r);
		}

		// + if (m_biorythm.textArea != null)
		// + m_biorythm.textArea.setText(strDesc);
	}

	// ----------------------------------------------------------------
	// redraw - Redraw this canvas using these dates
	public void redraw(String strDate, String strStart, String strEnd) {
		this.m_dateBirth.parseString(strDate);
		this.m_dateStart.parseString(strStart);
		this.m_dateEnd.parseString(strEnd);
		repaint();
	}
}

// ****************************************************************
// BiorythmWave - Class to paint a specific biorythm class on the screen
// ****************************************************************
class BiorythmWave extends Object {
	BiorythmCanvas m_canvas;
	Graphics m_g;
	int m_iCycleLength;
	Color m_color;

	// ----------------------------------------------------------------
	// BiorythmWave - **HACK** Dummy constructor used to load class at initial
	// run
	public BiorythmWave() {
	}

	// ----------------------------------------------------------------
	// BiorythmWave - Class constructor
	public BiorythmWave(BiorythmCanvas canvas, Graphics g, int iCycleLength) {
		this.m_canvas = canvas;
		this.m_g = g;
		this.m_iCycleLength = iCycleLength;
		// if (iCycleLength == BiorythmCanvas.kPhysicalCycle)
		this.m_color = Color.red;
		if (iCycleLength == BiorythmCanvas.kEmotionalCycle)
			this.m_color = Color.green;
		if (iCycleLength == BiorythmCanvas.kIntellectualCycle)
			this.m_color = Color.blue;
	}

	// ----------------------------------------------------------------
	// drawLine - Draw a line from x to x+1 on the canvas
	public void drawLine(int x) {
		m_g.setColor(m_color);
		m_g.drawLine(x, dConvertXtoY(x), x + 1, dConvertXtoY(x + 1));
	}

	// ----------------------------------------------------------------
	// dConvertXtoY - Convert this x value to a y value
	public int dConvertXtoY(int x) {
		EDate dateX = m_canvas.dateConvertXtoDate(x);
		double dGraphYValue = this.dConvertDateToStrength(dateX);
		int y = m_canvas.iConvertStrengthToY(dGraphYValue);
		return y;
	}

	// ----------------------------------------------------------------
	// dConvertDateToStrength - Convert this date value to a strength value
	public double dConvertDateToStrength(EDate dateToConvert) {
		// First, convert x to stength of the biorythm
		double dTimeChange = dateToConvert.dGetTime()
				- m_canvas.m_dateBirth.dGetTime(); // Change since their
		// birthdate
		dTimeChange = dTimeChange / m_canvas.kOneDay; // In days
		double dPercentThroughCycle = dTimeChange / m_iCycleLength
				- Math.floor(dTimeChange / m_iCycleLength); // Generate a number
		// 0 - .99
		// Now, Convert the strength to radians
		double dRadians = dPercentThroughCycle * Math.PI * 2;
		// Now, Convert to a sin wave (0->1->0->-1->0)
		double dValue = Math.sin(dRadians);
		return dValue;
	}

	// ----------------------------------------------------------------
	// drawTickmarks -
	public String drawTickmarks(int x, Rectangle r) {
		String strDesc = new String();
		int sx = 10;
		int sy = r.height - 42;
		int iStartDay = 0;
		int iEndDay = 0;
		int iStartBox = 0;
		String strDate;
		int istringWidth;
		int iCenter = r.height / 2;
		int iTrend = 0;
		double dXGraphYValue = this.dConvertDateToStrength(m_canvas
				.dateConvertXtoDate(x));

		double dXNextGraphYValue = this.dConvertDateToStrength(m_canvas
				.dateConvertXtoDate(x + 1));

		if (((dXGraphYValue >= 0) && (dXNextGraphYValue < 0))
				|| ((dXGraphYValue <= 0) && (dXNextGraphYValue > 0)))
			strDesc += this.addThisTickmark(x, iTrend);
		if (x != 0) {
			double dXLastGraphYValue = this.dConvertDateToStrength(m_canvas
					.dateConvertXtoDate(x - 1));

			if (dXLastGraphYValue < dXGraphYValue)
				iTrend = -1;
			else
				iTrend = +1;
			if (dXGraphYValue < dXNextGraphYValue) {
				if (iTrend == +1)
					strDesc += this.addThisTickmark(x, iTrend);
				iTrend = -1;
			} else {
				if (iTrend == -1)
					strDesc += this.addThisTickmark(x, iTrend);
				iTrend = +1;
			}
		}
		return strDesc;
	}

	// ----------------------------------------------------------------
	// addThisTickmark - Draw the tickmark at this location
	public String addThisTickmark(int x, int iTrend) {
		int sy = (int) dConvertXtoY(x);
		m_g.setColor(m_color);
		m_g.drawLine(x, sy - 4, x, sy + 4);
		if (iTrend == -1)
			sy -= 5;
		else
			sy += 14;
		String strDate = m_canvas.dateConvertXtoDate(x).toString().substring(4,
				6);
		if (strDate.charAt(0) == '0')
			strDate = strDate.substring(1, 2);
		int istringWidth = m_g.getFontMetrics().stringWidth(strDate);
		int sx = x - istringWidth / 2;
		m_g.drawString(strDate, sx, sy);
		String strPoint;
		if (iTrend == 0)
			strPoint = "Critical";
		else if (iTrend == -1)
			strPoint = "High";
		else
			strPoint = "Low";
		String strCycle = "Physical";
		if (m_iCycleLength == BiorythmCanvas.kEmotionalCycle)
			strCycle = "Emotional";
		if (m_iCycleLength == BiorythmCanvas.kIntellectualCycle)
			strCycle = "Intellectual";
		String strDesc = m_canvas.dateConvertXtoDate(x).toString().substring(0,
				11)
				+ " - "
				+ strCycle
				+ " "
				+ strPoint
				+ "11\1112\1213\1314\1415\1516\1617\1718\1819\1920\20";
		return strDesc;
	}
}

// ****************************************************************
// BiorythmControls - Control for entering from, to, and birthdate
// ****************************************************************
class BiorythmControls extends Panel {
	BiorythmCanvas canvas;
	TextField tfBirthdate;
	TextField tfStartdate;
	TextField tfEnddate;
	String m_strName;

	// ----------------------------------------------------------------
	// BiorythmControls - Constructor
	public BiorythmControls(BiorythmCanvas canvas, String strName) {
		this.canvas = canvas;
		this.m_strName = strName;
		add(new Label("Birthdate:"));
		if (strName == null)
			add(this.tfBirthdate = new TextField(canvas.m_dateBirth.toString(),
					17));
		else
			add(this.tfBirthdate = new TextField(strName, 17));
		canvas.m_dateBirth.setDefaultTimeOfDay(12, 0); // Dates entered without
		// time will default to
		// 12:00 noon
		add(new Button("Draw"));
		add(new Label("Start:"));
		add(this.tfStartdate = new TextField(canvas.m_dateStart.toString(), 12));
		add(new Label("End:"));
		add(this.tfEnddate = new TextField(canvas.m_dateEnd.toString(), 12));
	}

	// ----------------------------------------------------------------
	// action - Respond to the (re)Draw button
	public boolean action(Event ev, Object arg) {
		if (ev.target instanceof Button) {
			String label = (String) arg;
			String strBirth = new String(this.tfBirthdate.getText());
			// Set this field to blank, in case there is an error
			this.tfBirthdate.setText("");
			canvas.redraw(strBirth, this.tfStartdate.getText(), this.tfEnddate
					.getText());
			// Set them back
			this.tfBirthdate.setText(canvas.m_dateBirth.toString());
			this.tfStartdate.setText(canvas.m_dateStart.toString());
			this.tfEnddate.setText(canvas.m_dateEnd.toString());
			return true;
		}
		return false;
	}
}

// ****************************************************************
// EDate - This is some sad class...
// Basically, this class is a quick hack to get around the problem
// that the "Date" class doesn't go back past 1970.
// So, here's what this class does:
// For dates on or after Jan 01, 1970, I pass the CDate information
// plus 70 years (in miliseconds) (70 * 365 + 18 leap years)
// For dates before 1970, I add a multiple of 4 years (365 * 4 + 1 leap year)
// to get the Date in the range of Jan 1 1970-Dec 31 1973, then I set the
// offset to 4 years (plus a leap day) times the offset (in miliseconds)
// (Of course, I still add the 70 year offset)
// I also change the date formatting a little:
// If the time is the default time, I exclude it from the format
// (ie., Sep 17 1970, instead of Sep 17 1970 12:00)
// ****************************************************************
class EDate extends Date {
	double m_dTimeOffset = 0; // For dates < 1970 offset to get after 1970
	int m_iHours = 12; // Default hour of the day
	int m_iMinutes = 0; // Default minute of the day
	static final double kOneDay = 24 * 60 * 60 * 1000;
	static final double kFourYears = (4 * 365 + 1) * kOneDay;
	static final double m_dDateBase = (70 * 365 + 18) * kOneDay; // Jan 01,
	// 1970; Jan
	// 1,1900 =
	// 0

	static final int kSpaceChar = ' '; // ASCII Space
	static final int kColonChar = ':'; // ASCII Colon

	// ----------------------------------------------------------------
	// EDate - Default constructor (Same as Date)
	public EDate() {
	}

	public EDate(double dDate) {
		this.dSetTime(dDate);
	}

	public EDate(String strDate) {
		this.parseString(strDate);
	}

	// ----------------------------------------------------------------
	// parseString - Set the date - Using this date string
	public void parseString(String strDate) {
		int iYear = 0;
		int iTimeFlag = strDate.indexOf(kColonChar);
		int iStartYear = strDate.lastIndexOf(kSpaceChar);
		int iEndYear = strDate.length();
		if (iTimeFlag > iStartYear) { // There is a time here
			iEndYear = iStartYear;
			iStartYear = strDate.lastIndexOf(kSpaceChar, iStartYear - 1);
		}
		if (iStartYear != -1) {
			String strYear = strDate.substring(iStartYear + 1, iEndYear); // Year
			try {
				iYear = Integer.valueOf(strYear).intValue();
			} catch (NumberFormatException e) {
				System.out.println("error processing year: " + strYear);
			}
			if (iYear < 100)
				iYear += 1900;
			if (iYear >= 1970)
				m_dTimeOffset = 0;
			else { // Calculate year offset
				double dYearOffset = 1970 - iYear;
				double dFortYearOffset = Math
						.floor((dYearOffset + 3 + .01) / 4);
				m_dTimeOffset = dFortYearOffset * kFourYears;
				iYear = iYear + (int) (dFortYearOffset) * 4;
				strYear = String.valueOf(iYear).trim();
				if (iTimeFlag > iStartYear)
					strDate = strDate.substring(0, iStartYear + 1) + strYear
							+ strDate.substring(iEndYear, strDate.length());
				else
					strDate = strDate.substring(0, iStartYear + 1) + strYear;
			}
		}
		this.setTime(super.parse(strDate));
		if (iTimeFlag == -1) { // No hh:mm entered, leave time unchanged
			this.setHours(m_iHours);
			this.setMinutes(m_iMinutes);
		}
	}

	// ----------------------------------------------------------------
	// toString - Convert this date to a string (Format MMM DD TT:TT YYYY)
	public String toString() {
		String stringDate = super.toString();
		int iSpace = -1;
		int iLastSpace = 0;
		iLastSpace = iSpace + 1;
		iSpace = stringDate.indexOf(kSpaceChar, iLastSpace);
		if (iSpace == -1)
			iSpace = stringDate.length();
		String strDay = stringDate.substring(iLastSpace, iSpace);
		iLastSpace = iSpace + 1;
		iSpace = stringDate.indexOf(kSpaceChar, iLastSpace);
		if (iSpace == -1)
			iSpace = stringDate.length();
		String strMonth = stringDate.substring(iLastSpace, iSpace);
		iLastSpace = iSpace + 1;
		iSpace = stringDate.indexOf(kSpaceChar, iLastSpace);
		if (iSpace == -1)
			iSpace = stringDate.length();
		String strDate = stringDate.substring(iLastSpace, iSpace);
		iLastSpace = iSpace + 1;
		iSpace = stringDate.indexOf(kSpaceChar, iLastSpace);
		if (iSpace == -1)
			iSpace = stringDate.length();
		String strTime = stringDate.substring(iLastSpace, iSpace);
		int iTimeSeconds = strTime.lastIndexOf(kColonChar);
		strTime = strTime.substring(0, iTimeSeconds);
		iLastSpace = iSpace + 1;
		iSpace = stringDate.indexOf(kSpaceChar, iLastSpace);
		if (iSpace == -1)
			iSpace = stringDate.length();
		iSpace = stringDate.length();
		iLastSpace = iSpace - 4;
		String strYear = stringDate.substring(iLastSpace, iSpace);
		if (m_dTimeOffset != 0) { // Convert the time offset to the correct
			// year
			int iYear = 0;
			try {
				iYear = Integer.valueOf(strYear).intValue();
			} catch (NumberFormatException e) {
				System.out.println("error processing year: " + strYear);
			}
			int iFortYearOffset = (int) Math.floor(m_dTimeOffset / kFourYears
					+ .01);
			int iYearOffset = iFortYearOffset * 4;
			iYear = iYear - iYearOffset;
			strYear = String.valueOf(iYear).trim();
		}
		if ((this.getHours() == m_iHours) && (this.getMinutes() == m_iMinutes))
			stringDate = strMonth + " " + strDate + " " + strYear; // Default
		// time,
		// don't
		// display
		// time!
		else
			stringDate = strMonth + " " + strDate + " " + strYear + " "
					+ strTime;
		return stringDate;
	}

	// ----------------------------------------------------------------
	// setDefaultTimeOfDay - If a string is parsed without a time, this is the
	// time
	// If a date is converted to a string and the time is the same, Don't add
	// time
	public void setDefaultTimeOfDay(int iHours, int iMinutes) {
		m_iHours = iHours; // Default hour of the day
		m_iMinutes = iMinutes; // Default minute of the day
	}

	// ----------------------------------------------------------------
	// dGetTime - Return the date as a double
	public double dGetTime() {
		double dTime = this.getTime();
		dTime += m_dDateBase - m_dTimeOffset; // Add 70 years
		return dTime;
	}

	// ----------------------------------------------------------------
	// dSetTime - Set the date as a double
	public void dSetTime(double dTime) {
		dTime -= m_dDateBase - m_dTimeOffset; // Subtract 70 years
		long lTime = (long) dTime;
		this.setTime(lTime);
	}
}


