; /*
sc streq shortint strmerge link map timer
quit
*/

#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <devices/timer.h>
#include <libraries/gadtools.h>
#include <utility/date.h>
#include <intuition/intuitionbase.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/timer.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/utility.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define	AMIGAOS_V_37	37

extern struct Library *GadToolsBase;
extern struct Library *UtilityBase;
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;

struct Library *TimerBase;
struct MsgPort *timer_mp;
struct timerequest *timer_io;

static void DisposeTimerIO(int n) {
	switch(n) {
		case 0:  CloseDevice(timer_io);
		case 1:	DeleteIORequest(timer_io);
		case 2: 	DeleteMsgPort(timer_mp);
		default:	break;
	}
}

static int InitTimerIO(void) {
	if(timer_mp = CreateMsgPort()) {
		if(timer_io = CreateIORequest
		(timer_mp,sizeof(struct timerequest))) {
			if(OpenDevice(TIMERNAME,
			UNIT_VBLANK,timer_io,0L) == 0) {
				TimerBase = (struct Library*)
				timer_io->tr_node.io_Device;
				return(TRUE);
			} else DisposeTimerIO(1);
		} else DisposeTimerIO(2);
	} else DisposeTimerIO(3);
	return(FALSE);
}

#define	ID_START	1
#define	ID_STOP	2
#define	ID_RESET	3

void DisposeWindow(struct Window *w) {
	struct Gadget *g = w->FirstGadget;
	CloseWindow(w);
	FreeGadgets(g);
}

#define	G_WIDTH		100
#define	G_HEIGHT		30
#define	B_TOP(w)		((w)->BorderTop + 5 + G_HEIGHT)
#define	B_LEFT(w)	((w)->BorderLeft + 3)
#define	B_HEIGHT(w)	((w)->Height - (w)->BorderTop -\
							(w)->BorderBottom - G_HEIGHT - 7)
#define	B_WIDTH(w)	((w)->Width - (w)->BorderLeft -\
							(w)->BorderRight - 6)

struct TextAttr taPlain = {	"topaz.font",TOPAZ_EIGHTY	};

struct Window *InitWindow(void) {
	APTR vi;	struct NewGadget ng;
	struct Screen *ps;	struct Window *w;
	struct Gadget *gl,*gad = NULL;

	if(ps = LockPubScreen(NULL)) {
		if(vi = GetVisualInfoA(ps,NULL)) {
			if(gad = CreateContext(&gl)) {
				ng.ng_LeftEdge		= ps->WBorLeft + 3;
				ng.ng_TopEdge		= ps->BarHeight + 4;
				ng.ng_Width			= G_WIDTH;
				ng.ng_Height		= G_HEIGHT;
				ng.ng_VisualInfo	= vi;
				ng.ng_GadgetText	= "Start";
				ng.ng_TextAttr		= &taPlain;
				ng.ng_Flags			= PLACETEXT_IN;
				ng.ng_GadgetID		= ID_START;
				ng.ng_UserData		= NULL;
				gad = CreateGadget(BUTTON_KIND,gad,&ng,TAG_DONE);

				ng.ng_LeftEdge		= gad->LeftEdge + gad->Width + 1;
				ng.ng_GadgetText	= "Stop";
				ng.ng_Flags			= PLACETEXT_IN;
				ng.ng_GadgetID		= ID_STOP;
				gad = CreateGadget(BUTTON_KIND,gad,&ng,TAG_DONE);

				ng.ng_LeftEdge		= gad->LeftEdge + gad->Width + 1;
				ng.ng_GadgetText	= "Reset";
				ng.ng_Flags			= PLACETEXT_IN;
				ng.ng_GadgetID		= ID_RESET;
				gad = CreateGadget(BUTTON_KIND,gad,&ng,TAG_DONE);

				if(w = OpenWindowTags(NULL,
				WA_Flags,(ULONG)WFLG_CLOSEGADGET
				|WFLG_DEPTHGADGET|WFLG_DRAGBAR|WFLG_ACTIVATE,
				WA_IDCMP,(ULONG)IDCMP_GADGETUP|IDCMP_CLOSEWINDOW,
				WA_InnerHeight,(ULONG)G_HEIGHT + 40L,
				WA_InnerWidth,(ULONG)G_WIDTH * 3 + 8,
				WA_Gadgets,gl,
				WA_Title,"stop watch",
				TAG_DONE)) {

					GT_RefreshWindow(w,NULL);

					SetAPen(w->RPort,1);
					SetBPen(w->RPort,0);
					SetDrMd(w->RPort,JAM2);

					DrawBevelBox(w->RPort,B_LEFT(w),
					B_TOP(w),B_WIDTH(w),B_HEIGHT(w),
					GT_VisualInfo,vi,GTBB_Recessed,1L,TAG_DONE);
				}
				else FreeGadgets(gl);
			}
			FreeVisualInfo(vi);
		}
		UnlockPubScreen(NULL,ps);
	}
	return(w);
}

int SendTimerIO(int type,ULONG secs,ULONG micro) {
	struct timerequest *tr;
	if(tr = (struct timerequest*)AllocMem
	(sizeof(struct timerequest),MEMF_PUBLIC)) {
		*tr = *timer_io;
		tr->tr_node.io_Command = type;
		tr->tr_time.tv_secs = secs;
		tr->tr_time.tv_micro = micro;
		SendIO(&tr->tr_node);
		return(1);
	}
	return(0);
}

void CloseLibs(int n) {
	switch(n) {
		case 0:	CloseLibrary(&GfxBase->LibNode);
		case 1:	CloseLibrary(UtilityBase);
		case 2:	CloseLibrary(GadToolsBase);
		case 3:	CloseLibrary(&IntuitionBase->LibNode);
		default:	break;
	}
}

int OpenLibs(void) {
	if(IntuitionBase = (struct IntuitionBase*)
	OpenLibrary("intuition.library",AMIGAOS_V_37)) {
		if(GadToolsBase = OpenLibrary
		("gadtools.library",AMIGAOS_V_37)) {
			if(UtilityBase = OpenLibrary
			("utility.library",AMIGAOS_V_37)) {
				if(GfxBase = (struct GfxBase*)
				OpenLibrary("graphics.library",AMIGAOS_V_37)) {
					return(1);
				} else CloseLibs(1);
			} else CloseLibs(2);
		} else CloseLibs(3);
	}
	return(0);
}

#define	DEF_TIMEOUT		50000L

void _main(char *line) {
	struct IntuiMessage *imsg;		struct timerequest *tr;
	struct Window *w;					struct ClockData cd0,cd1;
	int outReq = 0, done = FALSE, stpRun = FALSE;

	if(OpenLibs()) {

		if(w = InitWindow()) {

			if(InitTimerIO()) {

				outReq += SendTimerIO(TR_ADDREQUEST,0L,DEF_TIMEOUT);

				while(!done || outReq) {
					static struct timeval tv0,tv1;
					ULONG sigRcvd;

					sigRcvd = Wait(1L << timer_mp->mp_SigBit
					| 1L << w->UserPort->mp_SigBit);

					timer_io->tr_node.io_Command = TR_GETSYSTIME;
					DoIO(&timer_io->tr_node);

					if(sigRcvd & (1L << w->UserPort->mp_SigBit)) {
						ULONG im_code,im_class;
						APTR im_address;

						while(imsg = GT_GetIMsg(w->UserPort)) {
							im_class = imsg->Class;
							im_code = imsg->Code;
							im_address = imsg->IAddress;
							GT_ReplyIMsg(imsg);
							switch(im_class) {
								case IDCMP_GADGETUP:
									switch(((struct Gadget*)
									im_address)->GadgetID) {
										case ID_START:	stpRun = TRUE;		break;
										case ID_STOP:	stpRun = FALSE;	break;
										case ID_RESET:	tv0 = tv1 = timer_io->tr_time;
															SubTime(&tv1,&tv0);
															break;
										default:			break;
									}
									break;
								case CLOSEWINDOW:		done = TRUE;	break;
								default:					break;
							}
						}
					}
					if(stpRun == FALSE) {
						struct timeval tvdiff = timer_io->tr_time;
						SubTime(&tvdiff,&tv0);	SubTime(&tvdiff,&tv1);
						AddTime(&tv0,&tvdiff);
					}

					tv1 = timer_io->tr_time;
					SubTime(&tv1,&tv0);

					if(sigRcvd & (1L << timer_mp->mp_SigBit)) {
						static ULONG oldTSec = 0,oldSSec = 0;
						UBYTE t_string[32];

						while(tr = (struct timerequest*)
						GetMsg(timer_mp)) {
							outReq--;
							FreeMem((UBYTE*)tr,sizeof(struct timerequest));

							if(oldTSec < timer_io->tr_time.tv_secs
							|| oldSSec < tv1.tv_secs) {
								oldSSec = tv1.tv_secs;
								oldTSec = timer_io->tr_time.tv_secs;
								Amiga2Date(timer_io->tr_time.tv_secs,&cd0);
								Amiga2Date(tv1.tv_secs,&cd1);
								sprintf(t_string,
								"%02d:%02d:%02d - %02d:%02d:%02d",
								cd0.hour,cd0.min,cd0.sec,
								cd1.hour,cd1.min,cd1.sec);
								Move(w->RPort,B_LEFT(w) + (B_WIDTH(w)
								- TextLength(w->RPort,t_string,
								strlen(t_string))) / 2,
								B_TOP(w) + (B_HEIGHT(w)
								+ w->RPort->Font->tf_Baseline) / 2);
								Text(w->RPort,t_string,strlen(t_string));
							}
							if(!done) {
								outReq += SendTimerIO
								(TR_ADDREQUEST,0L,DEF_TIMEOUT);
							}
						}
					}
				}
				DisposeTimerIO(0);
			}
			DisposeWindow(w);
		}
		CloseLibs(0);
	}
}
