/* JPClock - © Copyright 1989 by Jonathan Potter

   This is my first, fully functional program written under Lattice C v5.02

   Anyone ever tried converting Manx code to Lattice? yuk... */

/* This program is Public Domain; do with it what you will, but if you choose

   to sell it, at least leave my name in it. */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <exec/types.h>
#include <intuition/intuitionbase.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <devices/audio.h>
#include <devices/timer.h>
#include <exec/tasks.h>
#include <proto/all.h>
#include <pragma/all.h>
#include <string.h>
#include <dos.h>
#include "JPClock.i"

/* lsr info */
long _stack=4000;
char *_procname="JPClock";
long _priority=0;
long _BackGroundIO=1;
extern BPTR _Backstdout;

/* gadget ids */
#define SWAPSCREENS 1
#define STAYFRONT 2
#define ALARM 3
#define TIME 4
#define ALARMTIME 5
#define DATE 6

char TextBuffer[56];
char TimeBuffer[9], DateBuffer[9], AlarmBuffer[9]="00:00:00";

/* the font we'll use if default > 8 point */
struct TextAttr TopazText={
	"topaz.font",8,0,0};

/* where all the text goes */
struct IntuiText TextPrint={
	1,0,JAM2,0,0,NULL,TextBuffer,NULL};

short SXYBorder[]={
	-1,-1,72,-1,72,8,-1,8,-1,-1};
struct Border SBorder={
	0,0,3,0,JAM1,5,SXYBorder,NULL};

struct StringInfo TimeStringInfo={
	TimeBuffer,NULL,0,9,0};
struct StringInfo DateStringInfo={
	DateBuffer,NULL,0,9,0};
struct StringInfo AlarmStringInfo={
	AlarmBuffer,NULL,0,9,0};
struct IntuiText TimeStringText={
	1,0,JAM2,-35,0,NULL,(UBYTE *)"TIME",NULL};
struct IntuiText AlarmStringText={
	1,0,JAM2,-43,0,NULL,(UBYTE *)"ALARM",NULL};
struct IntuiText DateStringText={
	1,0,JAM2,-35,0,NULL,(UBYTE *)"DATE",NULL};
struct Gadget TimeStringGadget={
	NULL,123,15,72,8,GADGHCOMP,TOGGLESELECT|RELVERIFY,STRGADGET,
	(APTR)&SBorder,NULL,&TimeStringText,NULL,(APTR)&TimeStringInfo,TIME,NULL};
struct Gadget DateStringGadget={
	&TimeStringGadget,238,15,72,8,GADGHCOMP,TOGGLESELECT|RELVERIFY,STRGADGET,
	(APTR)&SBorder,NULL,&DateStringText,NULL,(APTR)&DateStringInfo,DATE,NULL};
struct Gadget AlarmStringGadget={
	&DateStringGadget,361,15,72,8,GADGHCOMP,TOGGLESELECT|RELVERIFY,STRGADGET,
	(APTR)&SBorder,NULL,&AlarmStringText,NULL,(APTR)&AlarmStringInfo,ALARMTIME,NULL};

short GXYBorder[]={
	-1,-1,100,-1,100,11,-1,11,-1,-1};
struct Border GBorder={
	0,0,3,0,JAM1,5,GXYBorder,NULL};
struct IntuiText AlarmText={
	1,0,JAM2,14,2,NULL,(UBYTE *)"Alarm On",NULL};
struct Gadget AlarmGadget={
	&AlarmStringGadget,325,30,100,11,GADGHCOMP,RELVERIFY|TOGGLESELECT,BOOLGADGET,
	(APTR)&GBorder,NULL,&AlarmText,
	NULL,NULL,ALARM,NULL};
struct IntuiText StayFrontText={
	1,0,JAM2,10,2,NULL,(UBYTE *)"Stay Front",NULL};
struct Gadget StayFrontGadget={
	&AlarmGadget,215,30,100,11,GADGHCOMP,RELVERIFY|TOGGLESELECT,BOOLGADGET,
	(APTR)&GBorder,NULL,&StayFrontText,
	NULL,NULL,STAYFRONT,NULL};
struct IntuiText SwapScreensText={
	1,0,JAM2,2,2,NULL,(UBYTE *)"Swap Screens",NULL};
struct Gadget SwapScreensGadget={
	&StayFrontGadget,105,30,100,11,GADGHCOMP,RELVERIFY,BOOLGADGET,
	(APTR)&GBorder,NULL,&SwapScreensText,
	NULL,NULL,SWAPSCREENS,NULL};

struct NewWindow ClockWindow={
	86,0,504,10,-1,-1,CLOSEWINDOW|MOUSEBUTTONS|NEWSIZE|GADGETUP|INACTIVEWINDOW,
	WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG|SMART_REFRESH|NOCAREREFRESH|RMBTRAP,
	&SwapScreensGadget,NULL,"",NULL,NULL,0,0,0,110,CUSTOMSCREEN};

struct IntuitionBase *IntuitionBase;
struct IntuiMessage *Msg;
struct Window *Window;
struct timerequest TimeReq;
struct MsgPort *TimerPort;

void main(argc,argv)
int argc;
char *argv[];
{
	/* heaps of variables, aren't there ? */
	register short Chip, Fast, Total;
	int dy, mn, yr, sizeflag=0, GadgetID, ScreenHeight, ScreenWidth, delay=1, al,
		StayFront=0, Alarm=0, a, b, c, USA=0, Swap=0, j, x, y, AlarmSet=0, AE,
		DTime=1, DDay=1, DDate=1, DFast=1, DChip=1, DTotal=1, TwelveHour=0, message;
	char d[8], Date[9], Time[9], buf[15], ch;
	char *Day[7]={ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
	struct Gadget *Gad;
	struct Screen *NextScreen;

	/* was I run from CLI? */
	if (_Backstdout) {
	/* yup */
		if ((strncmp(argv[1],"?",1))==0) {
			/* need some help */
			Write(_Backstdout,"\n",-1);
			Write(_Backstdout,"\x9B;0;1;2mJPClock\x9B;0;1m © Copyright 1989 by \x9B;0;1;33;4mJonathan Potter\x9B;0;1m (08-293-2788)\x9B;0m\n",-1);
			Write(_Backstdout,"\n\x9B;0;33mThe ultimate Workbench clock!\x9B;0m\n\n",-1);
			Write(_Backstdout,"Usage : JPClock [FLAGS]\n\nFlags are:\n\n",-1);
			Write(_Backstdout,"        -a           : Set Alarm on\n",-1);
			Write(_Backstdout,"        -atALARMTIME : Set Alarm time to ALARMTIME (HH:MM:SS or H:MM:SSC)\n",-1);
			Write(_Backstdout,"        -dSECONDS    : Set Delay between updates to SECONDS seconds\n",-1);
			Write(_Backstdout,"        -f           : Set Stay Front on\n",-1);
			Write(_Backstdout,"        -ot          : Turn time display off\n",-1);
			Write(_Backstdout,"        -oy          : Turn day display off\n",-1);
			Write(_Backstdout,"        -od          : Turn date display off\n",-1);
			Write(_Backstdout,"        -of          : Turn fast memory display off\n",-1);
			Write(_Backstdout,"        -oc          : Turn chip memory display off\n",-1);
			Write(_Backstdout,"        -ol          : Turn total memory display off\n",-1);
			Write(_Backstdout,"        -s           : Swap to second screen\n",-1);
			Write(_Backstdout,"        -u           : USA format; MM-DD-YY instead of DD-MM-YY\n",-1);
			Write(_Backstdout,"        -xX          : Open window with x-coordinate X\n",-1);
			Write(_Backstdout,"        -yY          : Open window with y-coordinate Y\n",-1);
			Write(_Backstdout,"        -12          : Set time to 12 hours format\n",-1);
			/* close our output channel */
			Close(_Backstdout);
			/* and get outahere */
			exit(0);
		}

		/* close output channel.. if we don't, the cli window can never close */
		Close(_Backstdout);
		/* let's 'ave a bo peep at our args then */
		for (dy=1;dy<argc;dy++) {
			/* delay */
			if ((strncmp(argv[dy],"-d",2))==0)
				if ((sscanf(argv[dy],"-d%d",&delay))<1 || delay<0) delay=1;
			/* stay front */
			if ((strncmp(argv[dy],"-f",2))==0) {
				StayFront=1;
				StayFrontGadget.Flags=GADGHCOMP|SELECTED;
			}
			/* alarm */
			if ((strncmp(argv[dy],"-a",2))==0) {
				Alarm=1;
				AlarmGadget.Flags=GADGHCOMP|SELECTED;
			}
			/* set alarm time */
			if ((strncmp(argv[dy],"-at",3))==0) {
				AlarmSet=1; AE=dy;
			}
			if ((strncmp(argv[dy],"-ot",3))==0) DTime=0; /* time off */
			if ((strncmp(argv[dy],"-oy",3))==0) DDay=0; /* day off */
			if ((strncmp(argv[dy],"-od",3))==0) DDate=0; /* date off */
			if ((strncmp(argv[dy],"-of",3))==0) DFast=0; /* fast mem off */
			if ((strncmp(argv[dy],"-oc",3))==0) DChip=0; /* chip mem off */
			if ((strncmp(argv[dy],"-ol",3))==0) DTotal=0; /* total mem off */
			if ((strncmp(argv[dy],"-u",2))==0) USA=1; /* usa backwards dates */
			if ((strncmp(argv[dy],"-s",2))==0) Swap=1; /* auto go to 2nd screen */
			/* new x position */
			if ((strncmp(argv[dy],"-x",2))==0) {
				sscanf(argv[dy],"-x%d",&x);
				if (x<0) x=0;
				ClockWindow.LeftEdge=x;
			}
			/* new y position */
			if ((strncmp(argv[dy],"-y",2))==0) {
				sscanf(argv[dy],"-y%d",&y);
				if (y<0) y=0;
				ClockWindow.TopEdge=y;
			}
			/* 12 hour time */
			if ((strncmp(argv[dy],"-12",3))==0) {
				TwelveHour=1;
				sprintf(AlarmBuffer,"0:00:00A");
			}
		}
	}
	if (delay>10) delay=10; delay*=1000000;
	/* can't have no display; if asked for, at least show time */
	if (DTime==0 && DDay==0 && DDate==0 && DFast==0 && DChip==0 && DTotal==0) DTime=1;
	if (DTime) { sprintf(buf,"         "); strcat(TextBuffer,buf); }
	if (DDay) { sprintf(buf,"    "); strcat(TextBuffer,buf); }
	if (DDate) { sprintf(buf,"         "); strcat(TextBuffer,buf); }
	if (DFast) { sprintf(buf,"          "); strcat(TextBuffer,buf); }
	if (DChip) { sprintf(buf,"          "); strcat(TextBuffer,buf); }
	if (DTotal) { sprintf(buf,"           "); strcat(TextBuffer,buf); }
	/* now what was that alarm time ? */
	if (AlarmSet) {
		a=0; b=0; c=0;
		if (TwelveHour) {
			sscanf(argv[AE],"-at%d:%d:%d%c",&a,&b,&c,&ch);
			sprintf(AlarmBuffer,"%1d:%02d:%02d%1c",a,b,c,toupper(ch));
		}
		else {
			sscanf(argv[AE],"-at%d:%d:%d",&a,&b,&c);
			sprintf(AlarmBuffer,"%02d:%02d:%02d",a,b,c);
		}
	}
	/* find out how wide window needs to be */
	ClockWindow.Width=(strlen(TextBuffer)*8)+80;
	if (ClockWindow.LeftEdge>640-ClockWindow.Width) ClockWindow.LeftEdge=640-ClockWindow.Width;
	IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",0);
	ClockWindow.Screen=(struct Screen *) IntuitionBase->FirstScreen;
	if (ClockWindow.TopEdge>(ClockWindow.Screen->Height-10)) ClockWindow.TopEdge=ClockWindow.Screen->Height-10;
	if ((Window=(struct Window *) OpenWindow(&ClockWindow))==NULL) goto bye;
	/* if default font is too big, use topaz 8 */
	if (Window->IFont->tf_YSize>8)
		TextPrint.ITextFont=&TopazText;
	if ((TimerPort=CreatePort("Timer Port",0))==NULL) goto bye;
	if (OpenDevice(TIMERNAME,UNIT_VBLANK,&TimeReq,0)!=NULL) goto bye;
	/* set up timer device request */
	TimeReq.tr_node.io_Message.mn_ReplyPort=TimerPort;
	TimeReq.tr_node.io_Command=TR_ADDREQUEST;
	TimeReq.tr_node.io_Flags=0;
	TimeReq.tr_node.io_Error=0;
	TimeReq.tr_time.tv_secs=0;
	TimeReq.tr_time.tv_micro=delay;
	SendIO((char *) &TimeReq.tr_node);
	/* lets go to next screen */
	if (Swap==1) {
		NextScreen=(struct Screen *) Window->WScreen->NextScreen;
		if (NextScreen==NULL || NextScreen->Width<ClockWindow.Width ||
			NextScreen->Height<45)
			NextScreen=(struct Screen *) Window->WScreen;
		ClockWindow.Screen=NextScreen;
		CloseWindow(Window);
		Window=(struct Window *) OpenWindow(&ClockWindow);
		ScreenToFront(NextScreen);
	}
	/* de main loop */
	FOREVER {
		loop:
		if (StayFront==1) WindowToFront(Window); /* shove to front if asked for */
		Chip=AvailMem(MEMF_CHIP)>>10; /* combien memory? */
		Fast=AvailMem(MEMF_FAST)>>10;
		Total=Chip+Fast;
		getclk(d); /* neat lattice function */
		stpdate(Date,3,&d[1]);
		stptime(Time,2,&d[4]);
		sscanf(Date,"%d-%d-%d",&mn,&dy,&yr);
		if (USA==0) sprintf(Date,"%02d-%02d-%02d",dy,mn,yr); /* backwards :-) */
		else sprintf(Date,"%02d-%02d-%02d",mn,dy,yr);
		if (TwelveHour) {
			ch='A'; j=d[4]; if (j>12) { j=j-12; ch='P'; }
			sprintf(Time,"%1d:%02d:%02d%1c",j,d[5],d[6],ch);
		}
		sprintf(buf,""); /* clear both buffers */
		sprintf(TextBuffer,"");
		if (DTime) { sprintf(buf," %s",Time); strcat(TextBuffer,buf); }
		if (DDay) { sprintf(buf," %s",Day[(int)d[0]]); strcat(TextBuffer,buf); }
		if (DDate) { sprintf(buf," %s",Date); strcat(TextBuffer,buf); }
		if (DFast) { sprintf(buf," FAST:%-4d",Fast); strcat(TextBuffer,buf); }
		if (DChip) { sprintf(buf," CHIP:%-4d",Chip); strcat(TextBuffer,buf); }
		if (DTotal) { sprintf(buf," TOTAL:%-4d",Total); strcat(TextBuffer,buf); }
		/* print it out! */
		PrintIText(Window->RPort,&TextPrint,28,1);
		/* check for alarm if asked */
		if (Alarm==1) {
			if ((strncmp(Time,AlarmBuffer,8))==0) { /* alarm time! */
				for (al=0;al<10;al++) {
					DisplayBeep(NULL);
					Delay(1);
				}
			}
		}
		/* got an intuimessage */
		if (Msg=(struct IntuiMessage *) GetMsg(Window->UserPort)) goto gotone;
		/* wait for a message */
		message=Wait (1<<Window->UserPort->mp_SigBit | 1<<TimerPort->mp_SigBit);
		/* timer message? */
		if (message&1<<TimerPort->mp_SigBit) {
			(void) GetMsg(TimerPort);
			TimeReq.tr_time.tv_secs=0;
			TimeReq.tr_time.tv_micro=delay;
			SendIO(&TimeReq.tr_node);
			goto loop;
		}
		/* nup, it was intui */
		Msg=(struct IntuiMessage *) GetMsg(Window->UserPort);
		gotone:
		ReplyMsg((struct Message *)Msg);
		switch (Msg->Class) {
			case CLOSEWINDOW: /* bye de bye */
				goto bye;
				break;

			case MOUSEBUTTONS:
				if (Msg->Code!=MENUDOWN) break;
				/* hit de right mouse button; resize */
				if (sizeflag==0) {
					/* make it big */
					ScreenHeight=Window->WScreen->Height;
					ScreenWidth=Window->WScreen->Width;
					if (Window->TopEdge+45>ScreenHeight)
						MoveWindow(Window,0,-((Window->TopEdge)-(ScreenHeight-45)));
					if (Window->LeftEdge+504>ScreenWidth)
						MoveWindow(Window,-((Window->LeftEdge)-(ScreenWidth-504)),0);
					SizeWindow(Window,(504-ClockWindow.Width),35);
					sizeflag=1;
					WaitForNewSize(); /* stupid how you have to do this */
					break;
				}
				else {
					/* make it small */
					SizeWindow(Window,-(504-ClockWindow.Width),-35);
					sizeflag=0;
					WaitForNewSize();
				}
				break;

			case GADGETUP:
				/* a gadget! */
				Gad=(struct Gadget *) Msg->IAddress;
				GadgetID=Gad->GadgetID;
				switch (GadgetID) {
					case SWAPSCREENS: /* go to next screen */
						NextScreen=(struct Screen *) Window->WScreen->NextScreen;
						/* if next screen can't fit us, don't */
						if (NextScreen==NULL || NextScreen->Width<ClockWindow.Width ||
							NextScreen->Height<45)
							NextScreen=(struct Screen *) Window->WScreen;
						ClockWindow.Screen=NextScreen;
						CloseWindow(Window);
						Window=(struct Window *) OpenWindow(&ClockWindow);
						ScreenToFront(NextScreen); /* and bring new screen to front */
						sizeflag=0;
						break;

					case STAYFRONT:
						/* toggle */
						if (StayFront==0) StayFront=1; else StayFront=0;
						break;

					case ALARM:
						/* toggle */
						if (Alarm==0) Alarm=1; else Alarm=0;
						break;

					case TIME:
						/* set the new time */
						a=0; b=0; c=0;
						if ((sscanf(TimeBuffer,"%d:%d:%d",&a,&b,&c))!=3 &&
								(sscanf(TimeBuffer,"%d:%d",&a,&b))!=2 &&
								(sscanf(TimeBuffer,"%d",&a))!=1) {
							/* didn't work; not entered properly */
							sprintf(TimeBuffer,"");
							RefreshGadgets(&TimeStringGadget,Window,NULL);
							break;
						}
						sprintf(TimeBuffer,"");
						RefreshGadgets(&TimeStringGadget,Window,NULL);
						d[4]=a; d[5]=b; d[6]=c;
						chgclk(d); /* another nifty lattice thingo */
						break;

					case DATE:
						/* new date */
						if (USA==0) { a=d[3]; b=d[2]; c=d[1]+80; }
						else { a=d[2]; b=d[3]; c=d[1]+80; }
						if ((sscanf(DateBuffer,"%d-%d-%d",&a,&b,&c))!=3 &&
								(sscanf(DateBuffer,"%d-%d",&a,&b))!=2 &&
								(sscanf(DateBuffer,"%d",&a))!=1) {
							sprintf(DateBuffer,"");
							RefreshGadgets(&DateStringGadget,Window,NULL);
							break;
						}
						sprintf(DateBuffer,"");
						RefreshGadgets(&DateStringGadget,Window,NULL);
						if (c<80) break;
						if (USA==0)	{ d[1]=c-80; d[2]=b; d[3]=a; }
						else { d[1]=c-80; d[2]=a; d[3]=b; }
						chgclk(d);
						break;

					case ALARMTIME:
						/* new al. time */
						a=0; b=0; c=0;
						if (TwelveHour) {
							sscanf(AlarmBuffer,"%d:%d:%d%c",&a,&b,&c,&ch);
							ch=toupper(ch);
							sprintf(AlarmBuffer,"%1d:%02d:%02d%1c",a,b,c,ch);
						}
						else {
							sscanf(AlarmBuffer,"%d:%d:%d",&a,&b,&c);
							sprintf(AlarmBuffer,"%02d:%02d:%02d",a,b,c);
						}
						RefreshGadgets(&AlarmStringGadget,Window,NULL);
						break;

					default:
						break;
				}
				break;

			default:
				break;
		}
	}
	bye:
	/* outahere! */
	if (TimeReq.tr_node.io_Message.mn_ReplyPort) CloseDevice(&TimeReq);
	if (TimerPort) DeletePort(TimerPort);
	if (Window) CloseWindow(Window);
	if (IntuitionBase) CloseLibrary((struct Library *) IntuitionBase);
	OpenWorkBench(); /* don't see how it could be closed, but.. */
	exit(0);
}

WaitForNewSize()
{
	while (TRUE) {
		WaitPort(Window->UserPort);
		Msg=(struct IntuiMessage *) GetMsg(Window->UserPort);
		if (Msg->Class==INACTIVEWINDOW) {
			ReplyMsg((struct Message *) Msg);
			return(0);
		}
		if (Msg->Class!=NEWSIZE) {
			ReplyMsg((struct Message *) Msg);
			continue;
		}
		ReplyMsg((struct Message *) Msg);
		break;
	}
}
