#include<dos/dos.h>
#include<dos/exall.h>
#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>

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

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

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

#define MYFONTSIZE		(8)

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

/* 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		("Files")
#define MYGAD_ID			(0)

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

static struct List mylist;

void addNode(char* name)
{
	if(name)
	{
		struct Node* node = AllocVec(sizeof(struct Node), MEMF_PUBLIC | MEMF_CLEAR);
		if(node)
		{
			if(node->ln_Name = AllocVec(strlen(name)+1, MEMF_PUBLIC))
				strcpy(node->ln_Name, name);
			AddTail(&mylist, node);
		}
	}
}

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 EABUFF_NUM  (30)
#define EABUFF_SIZE (EABUFF_NUM*sizeof(struct ExAllData))

/* Our buffer for ExAll() */
static struct ExAllData EABuff[EABUFF_NUM];

int fillList(char* dir)
{
	int success = FALSE;
	/* Get a lock on the directory */
	BPTR lock = Lock(dir, ACCESS_READ);
	if(lock)
	{
		/* Allocate ExAll control object */
		struct ExAllControl* eac = AllocDosObject(DOS_EXALLCONTROL,NULL);
		if(eac)
		{
			int going = TRUE;
			/* Must initialise LastKey to zero before calling ExAll() */
			eac->eac_LastKey = 0;
			/* If we got this far we're OK */
			success = TRUE;
			while(going)
			{
				/* Fill the buffer with directory entries */
				going = ExAll(lock, EABuff, EABUFF_SIZE, ED_NAME, eac);
				/* It's only an error if ExAll() returns FALSE and IoErr() signals */
				/* something other than running out of directory entries */
				if(!going)
					success = (IoErr() == ERROR_NO_MORE_ENTRIES);
				if(success)
				{
					if(eac->eac_Entries != 0)
					{
						/* Run through a buffer load of entries */
						struct ExAllData* ead = EABuff;
						while(ead)
						{
							addNode(ead->ed_Name);
							ead = ead->ed_Next;
						}
					}
				}
			}
			/* Print an error report if necessary */
			if(!success)
				PrintFault(IoErr(), "Error");
			FreeDosObject(DOS_EXALLCONTROL,eac);
		}
		else
			printf("Error: could not create ExAll control object\n");
		UnLock(lock);
	}
	else
		printf("Error: could not lock directory \"%s\"\n", dir);
	return success;
}

/* The start of the program */
void main()
{
	/* Open libraries... */
	if(IntuitionBase = OpenLibrary("intuition.library",37))
	{
		if(GadToolsBase = OpenLibrary("gadtools.library",37))
		{
			if(DosBase = OpenLibrary("dos.library",37))
			{
				/* Now do the real work */
				NewList(&mylist);
				if(fillList("sys:"))
					setupWindow();
				freeList();
				CloseLibrary(DosBase);
			}
			else
				printf("Error: could not open dos.library\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;
			struct Gadget* listgad;
			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;
				/* 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);
			}
			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)
{
	struct Window* win;
	/* Open our window */
	if(win = OpenWindowTags(NULL,
													WA_InnerWidth,	MYWIN_WIDTH,
													WA_InnerHeight,	MYWIN_HEIGHT,
													WA_Title,		"Directory List",
													WA_Flags,		WFLG_CLOSEGADGET | WFLG_DRAGBAR,
													WA_IDCMP,		IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | LISTVIEWIDCMP,
													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;
	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);
		}
	}
}

