/* JPClock v1.2 - © 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. */

/* Big bug fixed - thanks to Torsten Lohr for reporting 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 <string.h>
#include <dos.h>

extern void bye();

/* 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[10], DateBuffer[10], AlarmBuffer[10]="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,10,0};
struct StringInfo DateStringInfo={
	DateBuffer,NULL,0,10,0};
struct StringInfo AlarmStringInfo={
	AlarmBuffer,NULL,0,10,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,6,2,NULL,(UBYTE *)"Next Screen",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|GADGETUP,
	WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG|SMART_REFRESH|NOCAREREFRESH|RMBTRAP,
	NULL,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,
		StayFront=0,Alarm=0,a,b,c,USA=0,Swap=0,j,x,y,AlarmSet=0,
		DTime=1,DDay=1,DDate=1,DFast=1,DChip=1,DTotal=1,
		class,code,oldx,oldy;
	char d[8],Date[9],Time[9],buf[15],ch;
	char *Day[7]={ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
	char *alarmstring;
	struct Gadget *Gad;

	/* was I run from CLI? */
	if (argc>0) {
	/* 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           : Auto. swap to next 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);
			/* 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 have a look at our args */
		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; alarmstring=argv[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;
			}
		}
	}

	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) strcat(TextBuffer,"         ");
	if (DDay) strcat(TextBuffer,"    ");
	if (DDate) strcat(TextBuffer,"         ");
	if (DFast) strcat(TextBuffer,"          ");
	if (DChip) strcat(TextBuffer,"          ");
	if (DTotal) strcat(TextBuffer,"           ");
	/* now what was that alarm time ? */
	if (AlarmSet) {
		a=0; b=0; c=0;
		sscanf(alarmstring,"-at%d:%d:%d",&a,&b,&c);
		sprintf(AlarmBuffer,"%02d:%02d:%02d",a,b,c);
	}
	/* find out how wide window needs to be */
	IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",0);
	ClockWindow.Screen=(struct Screen *) IntuitionBase->FirstScreen;
	ClockWindow.Width=(strlen(TextBuffer)*8)+80;
	if (ClockWindow.LeftEdge>(ClockWindow.Screen->Width)-ClockWindow.Width)
		ClockWindow.LeftEdge=ClockWindow.Screen->Width-ClockWindow.Width;
	if (ClockWindow.TopEdge>(ClockWindow.Screen->Height-10))
		ClockWindow.TopEdge=ClockWindow.Screen->Height-10;
	if ((Window=(struct Window *) OpenWindow(&ClockWindow))==NULL) bye();
	/* if default font is too big, use topaz 8 */
	if (Window->IFont->tf_YSize>8)
		TextPrint.ITextFont=&TopazText;
	if ((TimerPort=(struct MsgPort *) CreatePort("Timer Port",0))==NULL) bye();
	if (OpenDevice(TIMERNAME,UNIT_VBLANK,&TimeReq,0)!=NULL) 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)
		if (nextscreen()) sizeflag=0;

	/* de main loop */
	FOREVER {
		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);
		strcpy(buf,""); strcpy(TextBuffer,""); /* clear both buffers */
		if (DTime) { strcat(TextBuffer," "); strcat(TextBuffer,Time); }
		if (DDay) { strcat(TextBuffer," "); strcat(TextBuffer,Day[(int)d[0]]); }
		if (DDate) { strcat(TextBuffer," "); strcat(TextBuffer,Date); }
		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)
				ringalarm(); /* alarm time! */
		/* wait for a message */
		Wait (1<<Window->UserPort->mp_SigBit | 1<<TimerPort->mp_SigBit);
		
		if (Msg=(struct IntuiMessage *) GetMsg(Window->UserPort)) {
			class=Msg->Class; code=Msg->Code;
			if (class==GADGETUP) {
				Gad=(struct Gadget *) Msg->IAddress;
				GadgetID=Gad->GadgetID;
			}
			ReplyMsg((struct Message *)Msg);
			switch (class) {
				case CLOSEWINDOW: /* bye de bye */
					bye();
					break;

				case MOUSEBUTTONS:
					if (code==MENUUP) {
						/* hit de right mouse button; resize */
						if (sizeflag==0) {
							/* make it big */
							ScreenHeight=Window->WScreen->Height;
							ScreenWidth=Window->WScreen->Width;
							oldx=Window->LeftEdge; oldy=Window->TopEdge;
							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);
							AddGList(Window,&SwapScreensGadget,-1,6,NULL);
							RefreshGList(&SwapScreensGadget,Window,NULL,6);
							sizeflag=1;
						}
						else {
							/* make it small */
							RemoveGList(Window,&SwapScreensGadget,6);
							SizeWindow(Window,-(504-ClockWindow.Width),-35);
							if (oldx+ClockWindow.Width>Window->WScreen->Width)
								oldx=Window->WScreen->Width-ClockWindow.Width;
							if (oldy+10>Window->WScreen->Height)
								oldy=Window->WScreen->Height-10;
							MoveWindow(Window,oldx-Window->LeftEdge,oldy-Window->TopEdge);
							sizeflag=0;
						}
					}
					break;

				case GADGETUP:
					/* a gadget! */
					switch (GadgetID) {
						case SWAPSCREENS: /* go to next screen */
							if (nextscreen()) 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; ch='\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 */
								strcpy(TimeBuffer,"");
								RefreshGList(&TimeStringGadget,Window,NULL,1);
								break;
							}
							strcpy(TimeBuffer,"");
							RefreshGList(&TimeStringGadget,Window,NULL,1);
							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) {
								strcpy(DateBuffer,"");
								RefreshGList(&DateStringGadget,Window,NULL,1);
								break;
							}
							strcpy(DateBuffer,"");
							RefreshGList(&DateStringGadget,Window,NULL,1);
							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; ch='\0';
							sscanf(AlarmBuffer,"%d:%d:%d",&a,&b,&c);
							sprintf(AlarmBuffer,"%02d:%02d:%02d",a,b,c);
							RefreshGList(&AlarmStringGadget,Window,NULL,1);
							break;
					}
					break;
			}
		}
		if (GetMsg(TimerPort)) {
			TimeReq.tr_time.tv_secs=0;
			TimeReq.tr_time.tv_micro=delay;
			SendIO((char *) &TimeReq.tr_node);
		}
	}
}

nextscreen()
{
	struct Screen *scr;
	scr=Window->WScreen->NextScreen;
	if (!scr || scr->Width<ClockWindow.Width || scr->Height<45) return(FALSE);
	ScreenToBack(Window->WScreen);
	ScreenToFront(scr);
	ClockWindow.LeftEdge=Window->LeftEdge;
	ClockWindow.TopEdge=Window->TopEdge;
	ClockWindow.Screen=scr;
	if (ClockWindow.LeftEdge+ClockWindow.Width>scr->Width)
		ClockWindow.LeftEdge=scr->Width-ClockWindow.Width;
	if (ClockWindow.TopEdge+10>scr->Height)
		ClockWindow.TopEdge=scr->Height-10;
	RemoveGList(Window,&SwapScreensGadget,6);
	CloseWindow(Window);
	if (!(Window=(struct Window *) OpenWindow(&ClockWindow))) bye();
	return(TRUE);
}

void bye()
{
	/* outahere! */
	GetMsg(TimerPort);
	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);
}

ringalarm()
{
	int i;
	for (i=0;i<10;i++) {
		DisplayBeep(NULL);
		beep();
		Delay(5);
	}
	return(0);
}

UBYTE allocationMap[] = { 1, 8, 2, 4 };

beep()
{
	struct IOAudio *audioIOB;
	struct MsgPort *replyPort;
	UBYTE *beepWave;

	replyPort=(struct MsgPort *) CreatePort(0,0);
	beepWave=(UBYTE *)AllocMem(10,MEMF_CHIP|MEMF_CLEAR);
	if (beepWave!=0) {
		beepWave[0]=100;
		audioIOB=(struct IOAudio *)	CreateExtIO(replyPort,sizeof(struct IOAudio));
		if (audioIOB!=NULL) {
			audioIOB->ioa_Request.io_Message.mn_Node.ln_Pri=85;
			audioIOB->ioa_Data=allocationMap;
			audioIOB->ioa_Length=sizeof(allocationMap);
			if (OpenDevice("audio.device",0,audioIOB,0)==0) {
				audioIOB->ioa_Request.io_Command=CMD_WRITE;
				audioIOB->ioa_Request.io_Flags=ADIOF_PERVOL;
				audioIOB->ioa_Data=beepWave;
				audioIOB->ioa_Length=10;
				audioIOB->ioa_Period=3579545/10000;
				audioIOB->ioa_Volume=64;
				BeginIO(audioIOB);
				Delay(50);
				CloseDevice(audioIOB);
			}
			DeleteExtIO(audioIOB);
		}
		FreeMem(beepWave,10);
	}
	DeletePort(replyPort);
	return(0);
}
