/*  Biorhythm.c  */

/***************************************************************************
 *									   *
 *  Program to calculate an individual's Biorhythms			   *
 *									   *
 *  written by:  Kevin Rahe, Reliable Software				   *
 *									   *
 *  last updated:  1-25-87						   *
 *									   *
 ***************************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include <ffp.h>
#include <dates.h>

#define INTUITION_REV   33
#define MAXPOINTS	20
#define AREAX		40
#define AREAY		40
#define LPOSX		175
#define LPOSY		20
#define RPOSX		212
#define RPOSY		20
#define BPOSX		20
#define BPOSY		23
#define GRAPHX		30
#define GRAPHY		54

#define REQWIDTH	306
#define REQHEIGHT	106
#define REQLINES	7

	/*******  graphing constants  *******/
#define LEGY		46
#define BKGD		1
#define PHYSPEN		0
#define EMOTPEN		2
#define INTEPEN		3
#define PHYSICAL	23
#define EMOTIONAL	28
#define INTELLECTUAL	33
#define PMULT		3.660563691
#define EMULT		4.456338407
#define IMULT		5.252113122

SHORT Arrow1[8] = { 1,6, 21,1, 21,11, 1,6 };
USHORT Arrow2[8] = { 1,1, 21,6, 1,11, 1,1 };
USHORT ArrowBox[10] = { 0,0, 24,0, 24,14, 0,14, 0,0 };
USHORT BDateBox[10] = { 0,0, 93,0, 93,13, 0,13, 0,0 };
char *monthname[] = {"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December" };

char *aboutdata[] = { "Biorhythm Calculator v1.1",
		      "by Kevin Rahe",
		      "",
		      "Provided as an example of using",
		      "the Motorola FFP math functions",
		      "with Lattice C.  Hope you like it!",
		      "MAY BE FREELY DISTRIBUTED"
		     };

/******  function declarations  ******/
void drawtriangles();
void dogadgets();
void newgraphsize();
void drawgraph();
void displegend();
void newmonth();
void domenu();

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Window *MyWindow, *OpenWindow();
struct RastPort *rp;
struct Remember *RememberKey;
struct IntuiMessage *IMsg, *GetMsg();
struct TmpRas MyTmpRas;
PLANEPTR MyPlane;
struct IntuiText aboutarray[REQLINES];
struct TextFont *font, *OpenFont();
char datebuffer[11];		/*  buffer area for Birthdate string gadget  */
int dayspan = 0;		/*  # of days between birth & 1st of month  */
int month;			/*  current month  */
FFP numdays;			/*  number of days in current month  */
FFP pmult, emult, imult;	/*  FFP equivalents of #defined multipliers  */
struct JULDATE sysdate, birthdate;


/****************  TextAttr structure  ****************/
struct TextAttr eightyfont = {
	"topaz.font",		/*  ta_Name  */
	8,			/*  ta_YSize  */
	NULL,			/*  ta_Style  */
	NULL			/*  ta_Flags  */
	};


/******************************************************/
/******************  About Requester  *****************/
/******************************************************/

/**************  IntuiText for Requester  *************/
struct IntuiText texttemplate = {
	0, 1,		/*  FrontPen, BackPen  */
	JAM2,		/*  DrawMode  */
	NULL, NULL,	/*  LeftEdge, TopEdge	(set at runtime)  */
	&eightyfont,	/*  Font  */
	NULL,		/*  IText		(set at runtime)  */
	NULL		/*  NextText		(set at runtime)  */
	};

struct IntuiText OkText = {
	0, 1,		/*  FrontPen, BackPen  */
	JAM2,		/*  DrawMode  */
	6, 3,		/*  LeftEdge, TopEdge  */
	&eightyfont,	/*  Font  */
	"Ok",		/*  IText  */
	NULL		/*  NextText  */
	};


/******************************************************/
/*****************  Birthdate Gadget  *****************/
/******************************************************/

/***********  Border for BirthDate Gadget  ************/
struct Border BDateBorder = {
	-3, -3,		/*  LeftEdge, TopEdge  */
	3, 0,		/*  FrontPen, BackPen  */
	JAM1,		/*  drawing mode */
	5,		/*  DrawCount  */
	&BDateBox[0],	/*  vertice coordinates list  */
	NULL		/*  NextBorder  */
	};

/*************  StringInfo for BirthDate  *************/
struct StringInfo BDateInfo = {
	datebuffer,	/*  Buffer  */
	NULL,		/*  UndoBuffer  */
	0,		/*  BufferPos  */
	11,		/*  MaxChars  */
	0,		/*  DispPos  */
	NULL,		/*  UndoPos  */
	NULL,		/*  NumChars  */
	NULL,		/*  DispCount  */
	0, 0,		/*  CLeft, CTop  */
	NULL,		/*  LayerPtr  */
	0,		/*  LongInt  */
	NULL		/*  AltKeyMap  */
	};

/*************  IntuiText for BirthDate  *************/
struct IntuiText desc_text = {
	1, 0,		/*  FrontPen, BackPen  */
	JAM2,		/*  DrawMode  */
	0, -11,		/*  LeftEdge, TopEdge  */
	&eightyfont,	/*  Font  */
	"Birthdate",	/*  IText  */
	NULL		/*  NextText  */
	};

/***************  BirthDate Gadget data  **************/
#define BDATE	1
struct Gadget BirthDate = {
	NULL,		/*  NextGadget  */
	BPOSX, BPOSY,	/*  LeftEdge, TopEdge  */
	88, 8,		/*  Width, Height  */
	GADGHCOMP,	/*  Highlighting  */
	RELVERIFY,	/*  Activation  */
	STRGADGET,	/*  Gadget type  */
	(APTR)&BDateBorder,	/*  Gadget structure  */
	NULL,		/*  SelectRender  */
	&desc_text,	/*  GadgetText  */
	NULL,		/*  MutualExclude  */
	(APTR)&BDateInfo,	/*  SpecialInfo  */
	BDATE,		/*  GadgetID  */
	NULL		/*  UserData  */
	};


/***************************************************/
/*****************  Arrow Gadgets  *****************/
/***************************************************/

/*************  Arrow Gadgets Border  **************/
struct Border BoxBorder = {
	-1, -1,		/*  LeftEdge, TopEdge  */
	3, 0,		/*  FrontPen, BackPen  */
	JAM1,		/*  drawing mode */
	5,		/*  DrawCount  */
	&ArrowBox[0],	/*  vertice coordinates list  */
	NULL		/*  NextBorder  */
	};

#define RTARROW	2
struct Gadget RightArrow = {
	&BirthDate,	/*  NextGadget  */
	RPOSX, RPOSY,	/*  LeftEdge, TopEdge  */
	23, 13,		/*  Width, Height  */
	GADGHCOMP,	/*  Highlighting  */
	RELVERIFY,	/*  Activation  */
	BOOLGADGET,	/*  Gadget type  */
	(APTR)&BoxBorder,	/*  Gadget structure  */
	NULL,		/*  SelectRender  */
	NULL,		/*  GadgetText  */
	NULL,		/*  MutualExclude  */
	NULL,		/*  SpecialInfo  */
	RTARROW,	/*  GadgetID  */
	NULL		/*  UserData  */
	};

#define LTARROW	3
struct Gadget LeftArrow = {
	&RightArrow,	/*  NextGadget  */
	LPOSX, LPOSY,	/*  LeftEdge, TopEdge  */
	23, 13,		/*  Width, Height  */
	GADGHCOMP,	/*  Highlighting  */
	RELVERIFY,	/*  Activation  */
	BOOLGADGET,	/*  Gadget type  */
	(APTR)&BoxBorder,	/*  Gadget structure  */
	NULL,		/*  SelectRender  */
	NULL,		/*  GadgetText  */
	NULL,		/*  MutualExclude  */
	NULL,		/*  SpecialInfo  */
	LTARROW,	/*  GadgetID  */
	NULL		/*  UserData  */
	};


/***************************************************/
/****************  Menu Structures  ****************/
/***************************************************/
 
/**************  Intuitext for Menu Item  ************/
struct IntuiText menutext = {
	0, 1,			/*  FrontPen, BackPen  */
	JAM2,			/*  DrawMode  */
	5, 0,			/*  LeftEdge, TopEdge  */
	&eightyfont,		/*  Font  */
	"About",		/*  Text  */
	NULL			/*  NextText  */
	};

/**************  MenuItem Structure  *************/
struct MenuItem mymenuitem = {
	NULL,			/*  NextItem  */
	0, 0, 100, 8,		/*  LeftEdge, TopEdge, Width, Height  */
	ITEMTEXT | ITEMENABLED | HIGHCOMP,	/*  Flags  */
	NULL,			/*  MutualExclude  */
	(APTR)&menutext,	/*  ItemFill  */
	NULL,			/*  SelectFill  */
	NULL,			/*  Command  */
	NULL,			/*  SubItem  */
	NULL			/*  NextSelect  */
	};

/**************  Menu Structure  ***************/
struct Menu mymenu = {
	NULL,			/*  NextMenu  */
	0, 0, 100, 10,		/*  LeftEdge, TopEdge, Width, Height  */
	MENUENABLED,		/*  Flags  */
	"Project",		/*  MenuName  */
	&mymenuitem
	};

/************************************************/
/**************  Window Structure  **************/
/************************************************/

struct NewWindow nw = {
	20, 20,			/*  LeftEdge, TopEdge  */
	340, 120,		/*  Width, Height  */
	0, 1,			/*  DetailPen, BlockPen  */
	CLOSEWINDOW | GADGETUP | NEWSIZE |
	    SIZEVERIFY | MENUPICK,		/*  IDCMP Flags  */

	WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWSIZING |
	    WINDOWDRAG | WINDOWDEPTH | NOCAREREFRESH,	/*  WindowFlags  */

	&LeftArrow,		/*  FirstGadget  */
	NULL,			/*  CheckMark  */
	"Biorhythm Calculator v1.1",	/*  Title  */
	NULL, NULL,		/*  Screen, BitMap  */
	340, 130,		/*  MinWidth, MinHeight  */
	640, 440,		/*  MaxWidth, MaxHeight  */
	WBENCHSCREEN		/*  Type  */
	};


/*****  IMPORTANT!  *****

	We use _main() here because when you compile with Lattice and main(),
	a console window will be automatically opened when the program is
	run from Workbench.  Using _main() prevents this, and also saves LOTS
	of object code.  NOTE:  I gleaned this technique from the version of
	gfxmem on FISH disk 14.  I do not know the possible side effects of
	using _main(), and am not real clear on why it works in the first
	place, so use it with CAUTION.
*/

_main()
{
	ULONG class;		/*  IDCMP message class  */
	USHORT code;		/*  IDCMP message code   */
	int i;

	/**********  Open the Libraries needed  **********/

	if(!OpenMathLibs()) exit(FALSE);	/*  open Motorola FFP libs.  */
				/*  NOTE:  OpenMathLibs() is in  ffp.h  */

	if ((IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library", INTUITION_REV)) == NULL)
		{
		CloseMathLibs();
		exit(FALSE);
		}

	if ((GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0))
		== NULL)  
		{
		CloseMathLibs();
		CloseLibrary (IntuitionBase);
		exit(FALSE);
		}

	/**********  Open the Window  **********/
	 
	if ( (MyWindow = OpenWindow(&nw) ) == NULL)
		{
		CloseMathLibs();
		CloseLibrary(IntuitionBase);
		CloseLibrary(GfxBase);
		exit(FALSE);
		}

	rp = MyWindow->RPort;			/*  RastPort pointer  */

	if (font = OpenFont(&eightyfont))	/*  Set font to 80-columns  */
		{
		SetFont(rp, font);
		CloseFont(font);
		}
	
	SetMenuStrip(MyWindow, &mymenu);	/*  Activate pull-down menu  */

	/*****  set up About requester text  *****/

	for (i = 0; i <= REQLINES - 1; i++)
		{
		aboutarray[i] = texttemplate;
		aboutarray[i].IText = aboutdata[i];
		aboutarray[i].TopEdge = 6 + i * 9;
		aboutarray[i].LeftEdge = REQWIDTH / 2 - 11 -
					IntuiTextLength(&aboutarray[i]) / 2;
		}

	for (i = 0; i <= REQLINES - 2; i++)
		aboutarray[i].NextText = &aboutarray[i + 1];


	/*****  IEEE to FFP conversions  *****/
	pmult = makeFFP(PMULT);
	emult = makeFFP(EMULT);
	imult = makeFFP(IMULT);

	drawtriangles();			/*  Draw filled triangles  */
	displegend();				/*  Display graph identifiers */

	Get_Date(&sysdate);			/*  get system date  */
	sysdate.days = 1;			/*  set to 1st day of year  */
	birthdate = sysdate;			/*  set birthdate to sysdate  */
	newmonth();				/*  display graph  */

/*
 **************************  Main Program Loop  ***************************
*/
do
	{
	Wait( 1L << MyWindow->UserPort->mp_SigBit);   /*  Wait for an event  */

	while ( IMsg = GetMsg(MyWindow->UserPort) )
		{

		class = IMsg->Class;
		code  = IMsg->Code;

		switch (class)	{

		case GADGETUP : dogadgets( (struct Gadget *)IMsg->IAddress );
				break;

		case NEWSIZE  : drawgraph();
				break;

		case MENUPICK : if (MENUNUM(code) != MENUNULL)
					domenu(MENUNUM(code),ITEMNUM(code));
				break;

			}

		ReplyMsg(IMsg);		/*  tell IDCMP you got it  */
		}
	}
while (class != CLOSEWINDOW);		/*  do until user closes window  */


/*
				F I N I S H   U P


      ******  Free allocated memory, close open Libraries, and quit  ******
*/

	FreeRemember (&RememberKey, TRUE);	/*  free all allocated memory */

	FreeRaster(MyPlane,AREAX,AREAY);	/*  free allocated rasters  */
	CloseWindow(MyWindow);			/*  close window  */
	CloseLibrary(IntuitionBase);
	CloseLibrary(GfxBase);
	CloseMathLibs();			/*  close FFP Libraries  */
}


void drawtriangles()		/***  Draw interior of Arrow gadgets  ***/
{
	UBYTE *InitAreaPtr, *AllocRemember();
	struct AreaInfo MyAreaInfo;

	RememberKey = NULL;
	InitAreaPtr = AllocRemember(&RememberKey, MAXPOINTS * 5, MEMF_CHIP);
		/*  allocate enough memory for figures with MAXPOINTS pts.   */

	InitArea(&MyAreaInfo, InitAreaPtr, MAXPOINTS);	/* create work area  */
	rp->AreaInfo = &MyAreaInfo;

	MyPlane = (PLANEPTR) AllocRaster(AREAX,AREAY);
					/* reserve memory for drawing*/
	if (MyPlane == 0L) exit(FALSE);  /*  not able to allocate memory  */
	rp->TmpRas = (struct TmpRas *) InitTmpRas(&MyTmpRas,
						MyPlane, RASSIZE(AREAX,AREAY) );


	/*******  Going to draw 2 filled triangles  *******/

	SetAPen(rp, 2);		/*  going to use color #2  */
	SetBPen(rp, 0);		/*  use color #0 for bkgnd  */
	SetOPen(rp, 1);		/*  outline around triangles  */
	SetDrMd(rp, JAM1);	/*  Draw Mode  */

	AreaMove (rp, LPOSX+Arrow1[0],LPOSY+Arrow1[1]);		/* 1st vert. */
	AreaDraw (rp, LPOSX+Arrow1[2],LPOSY+Arrow1[3]);		/* 2nd vert. */
	AreaDraw (rp, LPOSX+Arrow1[4],LPOSY+Arrow1[5]);		/* 3rd vert. */
	AreaEnd (rp);	/*  finish and fill left triangle  */

	AreaMove (rp, RPOSX+Arrow2[0],RPOSY+Arrow2[1]);		/* 1st vert. */
	AreaDraw (rp, RPOSX+Arrow2[2],RPOSY+Arrow2[3]);		/* 2nd vert. */
	AreaDraw (rp, RPOSX+Arrow2[4],RPOSY+Arrow2[5]);		/* 3rd vert. */
	AreaEnd (rp);	/*  finish and fill right triangle  */
}


void displegend()	/********  Display key to graph  *********/
{
	SetDrMd(rp, JAM1);		/*  Drawing mode  */
	SetAPen(rp, BKGD);		/*  Background color of graph  */
	SetOPen(rp, 3);			/*  Outline for box  */
	RectFill(rp, 17, LEGY-8, 291, LEGY+3);

	SetAPen(rp, PHYSPEN);		/*  Color of Physical graph  */
	Move(rp, 20, LEGY);		/*  Move into position  */
	Text(rp, "Physical", 8);

	SetAPen(rp, EMOTPEN);		/*  Color of Emotional graph  */
	Move(rp, 105, LEGY);
	Text(rp, "Emotional", 9);

	SetAPen(rp, INTEPEN);		/*  Color of Intellectual graph  */
	Move(rp, 194, LEGY);
	Text(rp, "Intellectual", 12);
}


void drawgraph()	/********  Draw graph  *******/
{
	int ymax, y, ysize;
	int xmax, x, xsize;
	int rem, count, length, junk;
	char string[6];
	FFP daywidth, halfysize;
	FFP fy, inc, factor;

	xmax = MyWindow->Width - MyWindow->BorderRight - 8;
	ymax = MyWindow->Height - 21;		/*  need space at bottom  */
	ysize = ymax - GRAPHY;			/*  for dates		  */

	SetAPen(rp, 0);			/*  drawing color, 0 is background  */
	BNDRYOFF(rp);			/*  no boundary on this rectangle  */
	SetDrMd(rp, JAM1);		/*  drawing mode, just JAM it in  */
	RectFill (rp, 2, GRAPHY,
		MyWindow->Width - MyWindow->BorderRight, MyWindow->Height - 2);
		/**  clear graph area  **/

	SetAPen(rp, 1);			/*  pen color  */
	SetOPen(rp, 2);			/*  outline pen  */
	SetDrMd(rp, JAM1);		/*  drawing mode  */
	RectFill (rp, GRAPHX, GRAPHY, xmax, ymax);	/*  Draw box  */

	SetDrPt(rp, 0xf8f8);		/*  line-drawing pattern  */
	SetAPen(rp, 3);			/*  line color  */
	SetBPen(rp, 1);			/*  background color  */
	y = GRAPHY + (ysize / 2);	/*  Y position of dotted line  */
	Move(rp, GRAPHX + 1, y);
	Draw(rp, xmax - 1, y);		/*  draw dotted line  */

	SetAPen(rp, 3);
	Move(rp, 11, GRAPHY + 7);
	Text(rp, "Hi", 2);
	Move(rp, 19, y + 3);
	Text(rp, "0", 1);
	Move(rp, 3, ymax - 1);
	Text(rp, "Low", 3);

	xsize = xmax - 1 - GRAPHX;	/*  usable X area  */
	daywidth = SPDiv(numdays, SPFlt(xsize));

	/*****  Display line & date every 5th day  *****/
	/***  NOTE:  lines are at midnight (12:00am)  ***/
	SetDrPt(rp, 0x8888);		/*  sparse dotted line  */
	SetDrMd(rp, JAM1);		/*  drawing mode  */
	for (count = 5; count <= SPFix(numdays); count += 5)
		{
		SetAPen(rp, 0);			/*  line color  */
		x = GRAPHX + SPFix(SPMul(SPFlt(count-1),daywidth));
		Move(rp, x, GRAPHY + 1);
		Draw(rp, x, ymax - 1);
		SetAPen(rp, 1);			/*  text color  */
		length = stci_d(string, count, &junk);
		Move(rp, x - 8 * (length - 1), ymax + 9);  /* text placement */
		Text(rp, string, length);
		}

	y = MyWindow->Height - 4;		/*  position for month name  */
	SetAPen(rp, 2);
	SetBPen(rp, 1);
	SetDrMd(rp, JAM2);
	Move(rp, GRAPHX, y);
	count = strlen(monthname[month-1]);
	Text(rp, monthname[month-1], count);	/*  display month name  */
	Text(rp, " ", 1);
	length = stci_d(string, (long) sysdate.year, &junk);
	Text(rp, string, length);		/*  display year  */

	SetDrPt(rp, 0xffff);		/*  reset drawing pattern  */

	halfysize = SPFlt(ysize / 2 - 1);

	SetDrMd(rp, JAM1);		/*  drawing mode  */

	inc = SPDiv(daywidth, SPFlt(1));

	/***************  Draw Physical cycle  ***************/
	SetAPen(rp, PHYSPEN);
	rem = dayspan % PHYSICAL;
	factor = SPAdd(SPFlt(rem),inc);

	for ( x = 1; x <= xsize; x++ )
		{
		fy = SPMul(halfysize, SPNeg(SPSin(SPDiv(pmult,factor))));
		WritePixel(rp, GRAPHX +x, GRAPHY +2+SPFix(SPAdd(fy,halfysize)));
		factor = SPAdd(factor, inc);
		}

	/***************  Draw Emotional cycle  ***************/
	SetAPen(rp, EMOTPEN);
	rem = dayspan % EMOTIONAL;
	factor = SPAdd(SPFlt(rem),inc);

	for ( x = 1; x <= xsize; x++ )
		{
		fy = SPMul(halfysize, SPNeg(SPSin(SPDiv(emult,factor))));
		WritePixel(rp, GRAPHX +x, GRAPHY +2+SPFix(SPAdd(fy,halfysize)));
		factor = SPAdd(factor, inc);
		}

	/***************  Draw Intellectual cycle  ***************/
	SetAPen(rp, INTEPEN);
	rem = dayspan % INTELLECTUAL;
	factor = SPAdd(SPFlt(rem),inc);

	for ( x = 1; x <= xsize; x++ )
		{
		fy = SPMul(halfysize, SPNeg(SPSin(SPDiv(imult,factor))));
		WritePixel(rp, GRAPHX +x, GRAPHY +2+SPFix(SPAdd(fy,halfysize)));
		factor = SPAdd(factor, inc);
		}
}


void dogadgets(gad)	/********  gadget handling   *******/
struct Gadget *gad;
{
	switch (gad->GadgetID)	{

	case LTARROW  : sysdate.days -= 1;
			if (sysdate.days < 1)
				{
				sysdate.year -= 1;
				if (sysdate.year % 4)
					sysdate.days += 365;
				else
					sysdate.days += 366;
				}
			newmonth();		/*  calc. day & draw graph  */
			break;

	case RTARROW  : sysdate.days += SPFix(numdays);
			if (sysdate.days > 365 && sysdate.year % 4)
				{
				sysdate.year += 1;
				sysdate.days -= 365;
				}
			else
			if ( (sysdate.year % 4 == 0) && sysdate.days > 366)
				{
				sysdate.year += 1;
				sysdate.days -= 366;
				}
			newmonth();
			break;

	case BDATE    : Get_Date(&sysdate);		/*  get system date  */
			GregtoJul(&birthdate, datebuffer);
			if ( birthdate.year == 0 )	/*  invalid date  */
				{
				sysdate.days = 1;
				birthdate = sysdate;
				}
			newmonth();
			break;
	}
}


void newmonth()
{
	int daycount, curday;

	curday = Datestat(&sysdate, &daycount,&month);
	numdays = SPFlt(daycount);		    /*  no. of days in month  */
	sysdate.days = sysdate.days - curday + 1;
	dayspan = Date_Span(&birthdate, &sysdate);
	drawgraph();

	/***  NOTE:  sysdate no longer contains the  ***
	 ***  current date, but rather the date of   ***
	 ***  the 1st day of the month.              **/
}


/*
	NOTE:  This is a very simplistic way of handling menu events.  In all
	probability you would not want to handle large menus this way.  With
	only one menu option, I did not think it practical to write a formal
	procedure to handle it.
*/

void domenu(menu, item)
USHORT menu, item;
{
	switch (menu)  {

	case 0 :	switch (item)  {

			case 0 : AutoRequest(MyWindow, aboutarray, NULL,
					&OkText, NULL,NULL,REQWIDTH, REQHEIGHT);
			}
			break;
	}
}
