#include<exec/libraries.h>
#include<exec/memory.h>
#include<graphics/text.h>
#include<intuition/intuition.h>
#include<intuition/screens.h>
#include<libraries/gadtools.h>
#include<utility/tagitem.h>

#include<string.h>
#include<stdio.h>

/* The following few includes assume the PatchLibV6.lha archive */
/* has been unarchived to the same directory as this project */
#include "patchlib/include/patch.h"
#include "patchlib/include/patchtags.h"
#include "patchlib/include/clib/patch_protos.h"

#include<clib/alib_protos.h>
#include<clib/exec_protos.h>
#include<clib/gadtools_protos.h>
#include<clib/intuition_protos.h>

/* The library base global variables */
struct Library* IntuitionBase;
struct Library* GadToolsBase;
struct Library* PatchBase;

/* Need to give prototypes for our functions */
void handleIDCMP(struct Window*);
void setupWindow();
void createWindow(struct Gadget*);

#define MYFONTSIZE		(8)

/* Some constants for the size of the window */
#define MYWIN_WIDTH		(410)
#define MYWIN_HEIGHT	(210)

/* Some constants for the position and size of our gadget */
#define MYGAD_LEFT		(10)
#define MYGAD_TOP			(10+MYFONTSIZE)
#define MYGAD_WIDTH		(MYWIN_WIDTH-MYGAD_LEFT*2)
#define MYGAD_HEIGHT	(MYWIN_HEIGHT-MYGAD_TOP*2+MYFONTSIZE)
#define MYGAD_TEXT		("Result    Task                 Library          ")
#define MYGAD_ID			(0)

/* Initialised structure declaration: describes standard Topaz font */
static struct TextAttr topazFont = { "topaz.font", MYFONTSIZE, 0, 0, };

static struct Window* win;
static struct Gadget* listgad;
static struct List mylist;
static struct SignalSemaphore ready;

/* The library offset of the OpenLibrary() call */
/* (See <pragmas/exec_pragmas.h> for LVOs) */
#define LVO_OPENLIBRARY (-0x228)

void addNode(char* name, struct Library* lib)
{
	struct Node* node = AllocVec(sizeof(struct Node), MEMF_PUBLIC | MEMF_CLEAR);
	if(node && name)
	{
		struct Task* task = FindTask(NULL);
		char* taskname = task->tc_Node.ln_Name;
		int size = strlen(taskname);
		if(size < 20)
			size = 20;
		size += 10+1+strlen(name)+1;
		if(node->ln_Name = AllocVec(size, MEMF_PUBLIC))
			sprintf(node->ln_Name, "$%08lx %-20s %s", lib, taskname, name);
		GT_SetGadgetAttrs(listgad, win, NULL, GTLV_Labels, ~0, TAG_DONE);
		AddHead(&mylist, node);
		GT_SetGadgetAttrs(listgad, win, NULL, GTLV_Labels, &mylist, TAG_DONE);
	}
}

void freeNode(struct Node* node)
{
	FreeVec(node->ln_Name);
	FreeVec(node);
}

void freeList()
{
	struct Node* work;
	struct Node* next = mylist.lh_Head;
	while(next->ln_Succ)
	{
		work = next;
		next = next->ln_Succ;
		freeNode(work);
	}
}

#define USEXRES

#ifdef USEXRES
struct PatchXResult* __saveds newf(register __a1 STRPTR name,
																	 register __d0 struct Library* result)
{
	struct PatchXResult *xresult;
	if (xresult = (struct PatchXResult*)PatchAlloc(PS_TYPE_XRESULT))
	{
		if(AttemptSemaphore(&ready))
		{
			addNode(name, result);
			ReleaseSemaphore(&ready);
		}
		xresult->pxr_RegPattern = PATREG_D0;
		xresult->pxr_RegD0 = (ULONG)result;
	}
	return xresult;
}
#else
struct Library* __saveds newf(register __a1 STRPTR name,
															register __d0 struct Library* result)
{
	if(AttemptSemaphore(&ready))
	{
		addNode(name, result);
		ReleaseSemaphore(&ready);
	}
	return result;
}
#endif

/* The start of the program */
void main()
{
	/* Open libraries... */
	if(IntuitionBase = OpenLibrary("intuition.library",37))
	{
		if(GadToolsBase = OpenLibrary("gadtools.library",37))
		{
			if(PatchBase = OpenLibrary("patch.library",5))
			{
				struct Patch* patch;
				/* Set up our semaphore and lock it */
				InitSemaphore(&ready);
				ObtainSemaphore(&ready);
				patch = InstallPatchTags((APTR)&newf, LVO_OPENLIBRARY,
																 PATT_LibraryName,	"exec.library",
																 PATT_PatchName,		"MyOLPatch",
																 PATT_Priority,			-5,
																 PATT_StackSize,		1500,
#ifdef USEXRES
																 PATT_UseXResult,		TRUE,
#endif
																 TAG_DONE);
				/* Now do the real work */
				setupWindow();
				RemovePatchTags(patch, PATT_TimeOut, 800, TAG_DONE);
				CloseLibrary(PatchBase);
			}
			else
				printf("Error: could not open patch.library V5+\n");
			CloseLibrary(GadToolsBase);
		}
		else
			printf("Error: could not open gadtools.library\n");
		CloseLibrary(IntuitionBase);
	}
	else
		printf("Error: could not open intuition.library\n");
}

/* Setup the window -- do the GadTools stuff */
void setupWindow()
{
	struct Screen* scr;
	/* We'll copy the visual information for the default public screen */
  /* (usually, this is the Workbench screen) */
	if(scr = LockPubScreen(NULL))
	{
		APTR vinfo;
		/* Get the visual info so GadTools can render the gadgets nicely */
		if(vinfo = GetVisualInfo(scr, TAG_DONE))
		{
			struct Gadget* glist = NULL;
			if(listgad = CreateContext(&glist))
			{
				struct NewGadget newgad;
				/* The offsets of our window borders */
				int offleft = scr->WBorLeft;
				int offtop = scr->WBorTop + (scr->Font->ta_YSize + 1);
				/* Setup our first gadget */
				newgad.ng_TextAttr 		= &topazFont;
				newgad.ng_VisualInfo 	= vinfo;
				newgad.ng_LeftEdge 		= MYGAD_LEFT + offleft;
				newgad.ng_TopEdge 		= MYGAD_TOP + offtop;
				newgad.ng_Width 			= MYGAD_WIDTH;
				newgad.ng_Height 			= MYGAD_HEIGHT;
				newgad.ng_GadgetText	= MYGAD_TEXT;
				newgad.ng_GadgetID		= MYGAD_ID;
				newgad.ng_Flags				= 0;
				NewList(&mylist);
				/* Now create it and add it to our list */
				if(listgad = CreateGadget(LISTVIEW_KIND, listgad, &newgad,
																	GTLV_Labels, &mylist,
																	TAG_DONE))
					createWindow(glist);
				else
					printf("Error: could not create gadget(s)\n");
				/* Free the gadget */
				FreeGadgets(glist);
				freeList();
			}
			else
				printf("Error: could not create GadTools context\n");
			FreeVisualInfo(vinfo);
		}
		else
			printf("Error: could not get visual info\n");
		UnlockPubScreen(NULL, scr);
	}
	else
		printf("Error: could not lock public screen\n");
}

/* Actually open the window, in the normal way */
void createWindow(struct Gadget* glist)
{
	/* Open our window */
	if(win = OpenWindowTags(NULL,
													WA_InnerWidth,	MYWIN_WIDTH,
													WA_InnerHeight,	MYWIN_HEIGHT,
													WA_Flags,		WFLG_CLOSEGADGET | WFLG_DRAGBAR,
													WA_IDCMP,		IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
													WA_Gadgets,	glist,
													TAG_DONE,		0))
	{
		/* Let GadTools refresh its bits of the window */
		GT_RefreshWindow(win, NULL);
		/* Now handle messages */
		handleIDCMP(win);
		CloseWindow(win);
	}
	else
		printf("Error: could not open window\n");
}

/* Our message handling code */
void handleIDCMP(struct Window* win)
{
	int going = TRUE;
	/* Let the patch work */
	ReleaseSemaphore(&ready);
	while(going)
	{
		struct IntuiMessage* intuimsg;
		/* Wait for messages to arrive */
		WaitPort(win->UserPort);
		/* Messages have arrived: loop through all of them */
		while(intuimsg = GT_GetIMsg(win->UserPort))
		{
			/* Act on this message... */
			switch(intuimsg->Class)
			{
			case IDCMP_CLOSEWINDOW:
				going = FALSE;
				break;
			case IDCMP_REFRESHWINDOW:
				/* You *MUST* remember to ask for and handle these refresh messages */
				GT_BeginRefresh(win);
				GT_EndRefresh(win, TRUE);
				break;
			}
			/* Reply when finished with message */
			GT_ReplyIMsg(intuimsg);
		}
	}
	/* Stop the patched function working */
	ObtainSemaphore(&ready);
}

