; /*
sc ignore=104,73 streq shortint strmerge link map task
quit
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <devices/timer.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <intuition/gadgetclass.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/gadtools.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/timer.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <clib/macros.h>

#define STACKSIZE		(4*1024)
#define TASKPRI		0
#define TASKNAME		"Sample_Task"

#define SIG_SHOWMSG	0
#define SIG_EXIT		1
#define SIG_REPLY		2
#define SIG_MAX		3

#define GAD_REPLY		0
#define GAD_MODE		1
#define GAD_MAX		2

struct MemTag {
	struct MemList mt_ML;
	struct MemEntry mt_ME;
};

#define	TFLGF_TIMERSET	0x0001
#define	TFLGF_WATCH		0x0002
#define	TFLGF_BUSYWAIT	0x0004

struct TaskTag {
	struct Task tt_Task;
	ULONG tt_flags;
	UBYTE volatile tt_SigBit[SIG_MAX];
	struct Task* volatile tt_SigTask[SIG_MAX];
	UBYTE* volatile tt_message;
	UWORD volatile tt_msglen;
	ULONG volatile tt_lcount;
	struct MsgPort *tt_timerport;
	struct timerequest *tt_timerequest,tt_cyclic;
	struct Library *tt_timerbase;
	struct timeval	volatile tt_tv0,tt_tvTskEntry,tt_tvUsed,tt_tvExpd;
	int volatile tt_cpu;
};

#define TSK_SHOWMSG(t) ((t)->tt_SigTask[SIG_SHOWMSG])
#define TSK_EXIT(t)    ((t)->tt_SigTask[SIG_EXIT])
#define TSK_REPLY(t)   ((t)->tt_SigTask[SIG_REPLY])

#define BIT_SHOWMSG(t) ((t)->tt_SigBit[SIG_SHOWMSG])
#define BIT_EXIT(t)    ((t)->tt_SigBit[SIG_EXIT])
#define BIT_REPLY(t)   ((t)->tt_SigBit[SIG_REPLY])

#define MSK_SHOWMSG(t) (1L << BIT_SHOWMSG(t))
#define MSK_EXIT(t)    (1L << BIT_EXIT(t))
#define MSK_REPLY(t)   (1L << BIT_REPLY(t))

#define	TimerBase (t->tt_timerbase)

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

void __interrupt Launch(void) {
	struct TaskTag *t = (struct TaskTag*)FindTask(NULL);

	if(t->tt_flags & TFLGF_TIMERSET) {
		t->tt_flags |= TFLGF_WATCH;
		t->tt_lcount++;
		GetSysTime(&t->tt_tvTskEntry);
	}
}

#define	MILLION	1000000L

void __interrupt Switch(void) {
	struct TaskTag *t = (struct TaskTag*)FindTask(NULL);

	if(t->tt_flags & TFLGF_WATCH) {
		t->tt_flags &= ~TFLGF_WATCH;
		GetSysTime(&t->tt_timerequest->tr_time);
		t->tt_tvExpd = t->tt_timerequest->tr_time;
		SubTime(&t->tt_tvExpd,&t->tt_tv0);
		SubTime(&t->tt_timerequest->tr_time,&t->tt_tvTskEntry);
		AddTime(&t->tt_tvUsed,&t->tt_timerequest->tr_time);

		t->tt_cpu =	100L * (t->tt_tvUsed.tv_secs * MILLION
						+ t->tt_tvUsed.tv_micro) / (t->tt_tvExpd.tv_secs
						* MILLION + t->tt_tvExpd.tv_micro);
	}
}

void __interrupt 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 Gadget *wg[GAD_MAX];

struct Window* __interrupt 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)) {
				static UBYTE *cycle_labels[] = {
					"Signal Wait","Busy Wait",NULL
				};

				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	= "Reply";
				ng.ng_TextAttr		= ps->Font;
				ng.ng_Flags			= PLACETEXT_IN;
				ng.ng_GadgetID		= 0;
				ng.ng_UserData		= NULL;
				wg[GAD_REPLY] = gad = CreateGadget(BUTTON_KIND,
				gad,&ng,GA_Disabled,1L,TAG_DONE);

				ng.ng_LeftEdge		= gad->LeftEdge + gad->Width + 1;
				ng.ng_Width			= G_WIDTH * 2 + 1;
				ng.ng_GadgetText	= NULL;
				ng.ng_GadgetID		= 1;
				wg[GAD_MODE] = gad = CreateGadget(CYCLE_KIND,gad,&ng,
				GTCY_Labels,cycle_labels,
				GTCY_Active,0L,TAG_DONE);

				if(w = OpenWindowTags(NULL,
				WA_Flags,(ULONG)WFLG_DEPTHGADGET|WFLG_DRAGBAR,
				WA_IDCMP,(ULONG)IDCMP_GADGETUP,
				WA_InnerHeight,(ULONG)G_HEIGHT + 80L,
				WA_InnerWidth,(ULONG)G_WIDTH * 3 + 8,
				WA_Gadgets,gl,
				WA_Title,TASKNAME,
				WA_PubScreen,ps,
				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);
}

void __interrupt print(struct Window *w,UBYTE *text,int top,int len) {
	SetAPen(w->RPort,0);
	RectFill(w->RPort,B_LEFT(w) + 2,top -
	w->RPort->Font->tf_Baseline,B_WIDTH(w) + 4,
	top + w->RPort->Font->tf_YSize - w->RPort->Font->tf_Baseline);
	Move(w->RPort,B_LEFT(w) + (B_WIDTH(w) - TextLength
	(w->RPort,text,len)) / 2,top);
	SetAPen(w->RPort,1);
	Text(w->RPort,text,len);
}


void __interrupt ShowCPU(struct Window *w,struct TaskTag *t) {
	UBYTE cpu_string[32];
	sprintf(cpu_string,"Sw/s=%2ld, CPU (%ld/%ld) %2d%%",
	t->tt_lcount / MAX(t->tt_tvExpd.tv_secs,1),
	t->tt_tvUsed.tv_secs,t->tt_tvExpd.tv_secs,t->tt_cpu);
	print(w,cpu_string,B_HEIGHT(w) / 2 -
	w->RPort->Font->tf_Baseline + B_TOP(w),strlen(cpu_string));
}

#define	AMIGAOS_V_37	37

void __saveds __interrupt SampleTask(void) {
	struct TaskTag *t;
	struct Window *w;

	t = (struct TaskTag*)FindTask(NULL);

	if(GfxBase = (struct GfxBase*)
	OpenLibrary("graphics.library",AMIGAOS_V_37)) {

		if(IntuitionBase = (struct IntuitionBase*)
		OpenLibrary("intuition.library",AMIGAOS_V_37)) {

			if(GadToolsBase = OpenLibrary("gadtools.library",AMIGAOS_V_37)) {

				if(w = InitWindow()) {

					if(t->tt_timerport = CreateMsgPort()) {
						if(t->tt_timerequest = (struct timerequest*)
						CreateIORequest(t->tt_timerport,sizeof(struct timerequest))) {
							if(OpenDevice(TIMERNAME,UNIT_VBLANK,
							t->tt_timerequest,0L) == 0) {
								t->tt_cyclic = *t->tt_timerequest;
								t->tt_timerbase = (struct Library*)
								t->tt_timerequest->tr_node.io_Device;
								GetSysTime(&t->tt_tv0);
								t->tt_flags |= TFLGF_TIMERSET;

								t->tt_cyclic.tr_node.io_Command = TR_ADDREQUEST;
								t->tt_cyclic.tr_time.tv_secs = 0L;
								t->tt_cyclic.tr_time.tv_micro = 400000L;
								SendIO(&t->tt_cyclic.tr_node);

								if((BIT_SHOWMSG(t) = AllocSignal(-1L)) != -1) {
									TSK_SHOWMSG(t) = FindTask(NULL);
									if((BIT_EXIT(t) = AllocSignal(-1L)) != -1) {
										TSK_EXIT(t) = FindTask(NULL);
										Signal(TSK_REPLY(t),MSK_REPLY(t));
										while(1) {
											ULONG sigset;

											if(t->tt_flags & TFLGF_BUSYWAIT) {
												sigset = SetSignal(0L,MSK_EXIT(t)
												| MSK_SHOWMSG(t)
												| 1L << t->tt_timerport->mp_SigBit
												| 1L << w->UserPort->mp_SigBit);
											}
											else {
												sigset = Wait(MSK_EXIT(t)
												| MSK_SHOWMSG(t)
												| 1L << t->tt_timerport->mp_SigBit
												| 1L << w->UserPort->mp_SigBit);
											}

											if(sigset & 1L << t->tt_timerport->mp_SigBit) {
												t->tt_cyclic.tr_node.io_Command = TR_ADDREQUEST;
												t->tt_cyclic.tr_time.tv_secs = 0L;
												t->tt_cyclic.tr_time.tv_micro = 400000L;
												SendIO(&t->tt_cyclic.tr_node);
												ShowCPU(w,t);
											}

											if(sigset & MSK_EXIT(t)) {
												break;
											}

											if(sigset & MSK_SHOWMSG(t)) {

												GT_SetGadgetAttrs(wg[GAD_REPLY],w,NULL,
												GA_Disabled,0L,TAG_DONE);
												RefreshGList(wg[0],w,NULL,1);

												print(w,t->tt_message,
												B_TOP(w) + B_HEIGHT(w) / 2 +
												w->RPort->Font->tf_Baseline,t->tt_msglen);
											}
											if(sigset & 1L << w->UserPort->mp_SigBit) {
												struct IntuiMessage *imsg;

												while(imsg = GT_GetIMsg(w->UserPort)) {
													ULONG im_code,im_class;
													APTR im_address;

													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 GAD_REPLY:
																	GT_SetGadgetAttrs(wg[GAD_REPLY],
																	w,NULL,GA_Disabled,1L,TAG_DONE);
																	RefreshGList(wg[0],w,NULL,1);
																	Signal(TSK_REPLY(t),MSK_REPLY(t));
																	break;
																case GAD_MODE:
																	switch(im_code) {
																		case 0:
																			t->tt_flags &= ~TFLGF_BUSYWAIT;
																			break;
																		case 1:
																			t->tt_flags |= TFLGF_BUSYWAIT;
																			break;
																		default:
																			break;
																	}
																	break;
																default:
																	break;
															}
															break;
														default:
															break;
													}
												}
											}
										}
										AbortIO(&t->tt_cyclic.tr_node);
										WaitIO(&t->tt_cyclic.tr_node);
										TSK_EXIT(t) = NULL;
										FreeSignal(BIT_EXIT(t));
									}
									TSK_SHOWMSG(t) = NULL;
									FreeSignal(BIT_SHOWMSG(t));
								}
								t->tt_flags &= ~(TFLGF_TIMERSET|TFLGF_WATCH);
								CloseDevice(t->tt_timerequest);
							}
							DeleteIORequest(t->tt_timerequest);
						}
						DeleteMsgPort(t->tt_timerport);
					}
					DisposeWindow(w);
				}
				CloseLibrary(GadToolsBase);
			}
			CloseLibrary(&IntuitionBase->LibNode);
		}
		CloseLibrary(&GfxBase->LibNode);
	}
	Forbid();
	Signal(TSK_REPLY(t),MSK_REPLY(t));
	Wait(0L); /* Wait forever... */
}

void main(void) {
	struct TaskTag *ptt; struct MemList *pml;
	struct MemTag aml; UBYTE SigBit; APTR stk;

	aml.mt_ML.ml_Node.ln_Type = NT_MEMORY;
	aml.mt_ML.ml_Node.ln_Pri  = 0;
	aml.mt_ML.ml_Node.ln_Name = TASKNAME;
	aml.mt_ML.ml_NumEntries   = 2;
	aml.mt_ML.ml_ME[0].me_Un.meu_Reqs = MEMF_PUBLIC|MEMF_CLEAR;
	aml.mt_ML.ml_ME[0].me_Length      = sizeof(struct TaskTag);
	aml.mt_ML.ml_ME[1].me_Un.meu_Reqs = MEMF_CLEAR;
	aml.mt_ML.ml_ME[1].me_Length      = STACKSIZE;

	pml = AllocEntry(&aml.mt_ML);
	if(!((ULONG)pml & (1L << 31))) {

		ptt = (struct TaskTag*) pml->ml_ME[0].me_Un.meu_Addr;
		stk = (APTR)            pml->ml_ME[1].me_Un.meu_Addr;;

		ptt->tt_Task.tc_Node.ln_Pri  = TASKPRI;
		ptt->tt_Task.tc_Node.ln_Type = NT_TASK;
		ptt->tt_Task.tc_Node.ln_Name = TASKNAME;

		ptt->tt_Task.tc_SPLower = stk;
		ptt->tt_Task.tc_SPReg   =
		ptt->tt_Task.tc_SPUpper = (APTR)((ULONG)stk + STACKSIZE);

		NewList(&ptt->tt_Task.tc_MemEntry);
		AddHead(&ptt->tt_Task.tc_MemEntry,&pml->ml_Node);

		if((SigBit = AllocSignal(-1L)) != -1) {

			BIT_REPLY(ptt) = SigBit;
			TSK_REPLY(ptt) = FindTask(NULL);

			AddTask(&ptt->tt_Task,(APTR)SampleTask,0L);

			Disable();
			ptt->tt_Task.tc_Launch  = Launch;
			ptt->tt_Task.tc_Switch  = Switch;
			ptt->tt_Task.tc_Flags  |= (TF_LAUNCH|TF_SWITCH);
			Enable();

			Wait(MSK_REPLY(ptt));

			while(1) {
				int len;
				UBYTE msg_buf[128];
				Write(Output(),"Parent: ",8);
				len = Read(Input(),msg_buf,128);
				if(*msg_buf == '\n') break;
				ptt->tt_message = msg_buf;
				ptt->tt_msglen = len - 1;
				Signal(TSK_SHOWMSG(ptt),MSK_SHOWMSG(ptt));
				Wait(MSK_REPLY(ptt));
			}

			Signal(TSK_EXIT(ptt),MSK_EXIT(ptt));
			Wait(MSK_REPLY(ptt));
			RemTask(&ptt->tt_Task);
			FreeSignal(SigBit);
		}
	}
	exit(0);
}
