/*
 * MKSoft Mini Clock Face
 *
 * The DoClock() routine is 100% reentrant.  However, it needs
 * GfxBase and IntuitionBase set up for it.
 */

#include	<exec/types.h>
#include	<exec/memory.h>
#include	<devices/timer.h>
#include	<graphics/gfx.h>
#include	<graphics/rastport.h>
#include	<intuition/intuition.h>

#include	<proto/exec.h>
#include	<proto/intuition.h>
#include	<proto/graphics.h>
#include	<proto/timer.h>

/* MKS_WINDOW is the special version that puts the clock exactly where I want it... */
/* #define	MKS_WINDOW	*/

/* DETATCH_CLOCK is used to add in the Detatch data structures */
/* #define	DETATCH_CLOCK	*/

/* When testing, I set this to 1 so that I can see the hands move faster */
/* It should be 60 for any real work... */
#define	SECONDS_PER_MINUTE	(60)

static short SinArray[16]={0,105,208,309,407,500,588,669,743,809,866,914,951,978,995,1000};

void AdjustXY(short Minutes,short *x1,short *y1,short Radius_X,short Radius_Y)
{
register	long	x;
register	long	y;
register	short	tmp;
register	short	tmp1;

	tmp1=(15-(tmp=(Minutes % 15)));

	if (Minutes<15)
	{
		x=SinArray[tmp];	y=-SinArray[tmp1];
	}
	else if (Minutes<30)
	{
		x=SinArray[tmp1];	y=SinArray[tmp];
	}
	else if (Minutes<45)
	{
		x=-SinArray[tmp];	y=SinArray[tmp1];
	}
	else
	{
		x=-SinArray[tmp1];	y=-SinArray[tmp];
	}

	x*=Radius_X;
	y*=Radius_Y;

	(*x1)+=(short)((x+((x<0) ? -500 : 500))/1000);
	(*y1)+=(short)((y+((y<0) ? -500 : 500))/1000);
}

void DrawArm(struct RastPort *rp,short Minutes,short Radius_X,short Radius_Y,short Center_X,short Center_Y)
{
#ifdef	THIN_LINE

	Move(rp,Center_X,Center_Y);
	AdjustXY(Minutes,&Center_X,&Center_Y,Radius_X,Radius_Y);
	Draw(rp,Center_X,Center_Y);

#else	/* THIN_LINE */

register	short	loop;
		short	x,y;
		short	x1,y1;

	x=Center_X;
	y=Center_Y;

	AdjustXY(Minutes,&x,&y,Radius_X,Radius_Y);

	Move(rp,x,y);

	Radius_X=1+(Radius_X >> 4);
	Radius_Y=1+(Radius_Y >> 4);

	for (loop=0;loop<3;loop++)
	{
		x1=Center_X;
		y1=Center_Y;
		Minutes=(Minutes+15)%60;
		AdjustXY(Minutes,&x1,&y1,Radius_X,Radius_Y);
		Draw(rp,x1,y1);
	}

	Draw(rp,x,y);

#endif	/* THIN_LINE */
}

void DrawHands(struct RastPort *rp,short Hours,short Minutes,short Center_X,short Center_Y)
{
	DrawArm(rp,(Hours*5)+(Minutes/12),Center_X-5-(Center_X >> 2),Center_Y-5-(Center_Y >> 2),Center_X,Center_Y);
	DrawArm(rp,Minutes,Center_X-4,Center_Y-4,Center_X,Center_Y);
}

void DoClock(short LeftEdge,short TopEdge,short Size_X,short Size_Y,UBYTE Face_Pen,UBYTE Hand_Pen,short DragFlag)
{
register	struct	MsgPort		*myport;
register	struct	Window		*window;
register	struct	RastPort	*rp;
register		PLANEPTR	raster;
register		short		ExitFlag=FALSE;
register		short		loop;
register		ULONG		winSig;
register		ULONG		timSig;
register		short		Minutes=0;
register		short		Hours=0;
register		ULONG		tmp;
register		short		Center_X;
register		short		Center_Y;
		struct	timerequest	Time_Req;
		struct	TmpRas		tmpras;
		struct	AreaInfo	AreaInfo;
		struct	NewWindow	NewWin;
#ifndef	MKS_WINDOW
		struct	Gadget		Gad;
#endif	/* MKS_WINDOW */
			short		AreaBuffer[10];

	Center_X=((Size_X - 1) >> 1);
	Center_Y=((Size_Y - 1) >> 1);

#ifndef	MKS_WINDOW
	Gad.NextGadget=NULL;
	Gad.LeftEdge=0;
	Gad.TopEdge=0;
	Gad.Width=Size_X;
	Gad.Height=Size_Y;
	Gad.Flags=GADGHNONE;
	Gad.Activation=RELVERIFY;
	Gad.GadgetType=WDRAGGING;
	Gad.GadgetRender=NULL;
	Gad.SelectRender=NULL;
	Gad.GadgetText=NULL;
	Gad.MutualExclude=NULL;
	Gad.SpecialInfo=NULL;
#endif	/* MKS_WINDOW */

	NewWin.LeftEdge=LeftEdge;
	NewWin.TopEdge=TopEdge;
	NewWin.Width=Size_X;
	NewWin.Height=Size_Y;
	NewWin.IDCMPFlags=VANILLAKEY;
	NewWin.Flags=BORDERLESS|NOCAREREFRESH|RMBTRAP;

	NewWin.FirstGadget=NULL;
#ifndef	MKS_WINDOW
	if (DragFlag) NewWin.FirstGadget=&Gad;
#endif	/* MKS_WINDOW */

	NewWin.CheckMark=NULL;
	NewWin.Title=NULL;
	NewWin.Screen=NULL;
	NewWin.BitMap=NULL;
	NewWin.Type=WBENCHSCREEN;

	if (myport=CreatePort(NULL,NULL))
	{
		timSig=(1L << (myport->mp_SigBit));
		if (!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)&Time_Req,NULL))
		{
			Time_Req.tr_node.io_Message.mn_ReplyPort=myport;

			if (raster=AllocRaster(Size_X,Size_Y))
			{
				InitTmpRas(&tmpras,(APTR)raster,RASSIZE((ULONG)Size_X,(ULONG)Size_Y));
				if (window=OpenWindow(&NewWin))
				{
					winSig=(1L << (window->UserPort->mp_SigBit));

					/* Get RastPort and set draw modes... */
					rp=window->RPort;
					rp->TmpRas=&tmpras;
					SetAPen(rp,Face_Pen);
					SetDrMd(rp,JAM1);

					/* Draw the circle */
					for (loop=0;loop<10;AreaBuffer[loop++]=0);
					InitArea(rp->AreaInfo=&AreaInfo,(APTR)AreaBuffer,4);
					AreaEllipse(rp,Center_X,Center_Y,Center_X,Center_Y);
					AreaEnd(rp);

					/* Draw the hash marks */
					SetAPen(rp,Hand_Pen);
					Move(rp,Center_X,1);		Draw(rp,Center_X,3);
					Move(rp,1,Center_Y);		Draw(rp,3,Center_Y);
					Move(rp,Center_X,Size_Y-2);	Draw(rp,Center_X,Size_Y-4);
					Move(rp,Size_X-2,Center_Y);	Draw(rp,Size_X-4,Center_Y);

					while (!ExitFlag)
					{
						/* Get current time... */
						Time_Req.tr_node.io_Command=TR_GETSYSTIME;
						Time_Req.tr_node.io_Flags=IOF_QUICK;
						DoIO(&Time_Req.tr_node);

						/* Erase old hands... */
						SetAPen(rp,Face_Pen);
						DrawHands(rp,Hours,Minutes,Center_X,Center_Y);

						/* Calculate new time... */
						tmp=(Time_Req.tr_time.tv_secs)/SECONDS_PER_MINUTE;
						Minutes=tmp % 60;
						Hours=(tmp / 60) % 12;

						/* Draw hands new...*/
						SetAPen(rp,Hand_Pen);
						DrawHands(rp,Hours,Minutes,Center_X,Center_Y);

						/* Clear possible old timer signal */
						if ((FindTask(NULL)->tc_SigRecvd)&timSig) Wait(timSig);

						/* Set sleep time... */
						Time_Req.tr_node.io_Command=TR_ADDREQUEST;
						Time_Req.tr_time.tv_secs=SECONDS_PER_MINUTE;
						Time_Req.tr_time.tv_micro=0;
						SendIO(&Time_Req.tr_node);

						if (Wait(winSig|timSig)&winSig)
						{
						register	struct	IntuiMessage	*msg;

							while (msg=(struct IntuiMessage *)GetMsg(window->UserPort))
							{
								if ((msg->Class==VANILLAKEY)&&(msg->Code==0x1B)) ExitFlag=TRUE;
								ReplyMsg((struct Message *)msg);
							}
							AbortIO((struct IORequest *)&Time_Req);
						}
						WaitIO((struct IORequest *)&Time_Req);
					}
					CloseWindow(window);
				}
				FreeRaster(raster,Size_X,Size_Y);
			}
			CloseDevice((struct IORequest *)&Time_Req);
		}
		DeletePort(myport);
	}
}

int CXBRK(VOID) { return(0); }

#ifdef	MKS_WINDOW

#define	FACE_SIZE_X	(55)
#define	FACE_SIZE_Y	(45)

void main(void)
{
#else	/* MKS_WINDOW */

#include	<proto/dos.h>
#include	<string.h>
#include	<stdlib.h>

#define	FACE_SIZE_X	(45)
#define	FACE_SIZE_Y	(39)
#define	FACE_SIZE_MIN	(19)

#define	USAGE_SIZE	102

#ifdef	DETATCH_CLOCK

/* For CBack.o */
char procname[]="MKSoft Mini-Clock";
long priority=1;
long stack=4000;

#else	/* DETATCH_CLOCK */

/* Help line.  Only if not DETATCH_CLOCK... */
static char Usage[USAGE_SIZE+1]="Usage: MyClock [LEFT x] [TOP y] [WIDTH x] [HEIGHT y]\n               [FACE c] [HANDS c] [DRAG|NODRAG]\n\n";

#endif	/* DETATCH_CLOCK */

void main(int argc,char *argv[])
{
register	short	Drag_Flag=TRUE;
register	short	loop;
register	short	error=FALSE;
	struct	Screen	Screen;
		short	LeftEdge;
		short	TopEdge;
		short	Size_X=0;
		short	Size_Y=0;
		short	Face_Pen;
		short	Hand_Pen;
#endif	/* MKS_WINDOW */

	if (IntuitionBase=OpenLibrary("intuition.library",33L))
	{
		if (GfxBase=OpenLibrary("graphics.library",33L))
		{
#ifdef	MKS_WINDOW
			DoClock(656-((FACE_SIZE_X-1)>>1),434-FACE_SIZE_Y,FACE_SIZE_X,FACE_SIZE_Y,2,1,FALSE);
#else	/* MKS_WINDOW */
			GetScreenData((CPTR)&Screen,sizeof(struct Screen),WBENCHSCREEN,NULL);
			Size_X=FACE_SIZE_X;
			Size_Y=(Screen.Height < 300) ? FACE_SIZE_MIN : FACE_SIZE_Y;
			LeftEdge=Screen.Width;
			TopEdge=Screen.Height;
			Face_Pen=Screen.BlockPen;
			Hand_Pen=Screen.DetailPen;

			if (argc)
			{
			register	char	*c;
			register	short	*change;

				for (loop=1;loop<argc;loop++)
				{
					c=argv[loop];
					if (!stricmp(c,"DRAG")) Drag_Flag=TRUE;
					else if (!stricmp(c,"NODRAG")) Drag_Flag=FALSE;
					else if ((loop+1)<argc)
					{
						if (!stricmp(c,"LEFT")) change=&LeftEdge;
						else if (!stricmp(c,"TOP")) change=&TopEdge;
						else if (!stricmp(c,"WIDTH")) change=&Size_X;
						else if (!stricmp(c,"HEIGHT")) change=&Size_Y;
						else if (!stricmp(c,"FACE")) change=&Face_Pen;
						else if (!stricmp(c,"HANDS")) change=&Hand_Pen;
						else change=NULL;
						if (change)
						{
							loop++;
							*change=(short)atol(argv[loop]);
						}
						else error=TRUE;
					}
					else error=TRUE;
				}
			}

			if (error)
			{
#ifndef	DETATCH_CLOCK
			register	BPTR	out;

				if (out=Output()) Write(out,Usage,USAGE_SIZE);
#endif	/* DETATCH_CLOCK */
			}
			else
			{
				if (Size_X<FACE_SIZE_MIN) Size_X=FACE_SIZE_MIN;
				if (Size_Y<FACE_SIZE_MIN) Size_Y=FACE_SIZE_MIN;

				if (Size_X>Screen.Width) Size_X=Screen.Width;
				if (Size_Y>Screen.Height) Size_Y=Screen.Height;

				if (LeftEdge<0) LeftEdge=0;
				if ((LeftEdge+Size_X)>Screen.Width) LeftEdge=Screen.Width-Size_X;

				if (TopEdge<0) TopEdge=0;
				if ((TopEdge+Size_Y)>Screen.Height) TopEdge=Screen.Height-Size_Y;

				if (Face_Pen==Hand_Pen)
				{
					Face_Pen=1;
					Hand_Pen=0;
				}

				DoClock(LeftEdge,TopEdge,Size_X,Size_Y,(UBYTE)Face_Pen,(UBYTE)Hand_Pen,Drag_Flag);
			}
#endif	/* MKS_WINDOW */
			CloseLibrary((struct Library *)GfxBase);
		}
		CloseLibrary((struct Library *)IntuitionBase);
	}
}
