/*  Big example, perhaps something like what you'd use in a program :) */

/* Always included first */
#include <exec/types.h>

/* So we can use the functions from these libraries */
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/intuition.h>
#include <proto/preferences.h>
#include <proto/graphics.h>

/* Intuition stuff */
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>

/* Gadtools stuff */
#include <libraries/gadtools.h>

/* For the MAKE_ID macro */
#include <libraries/iffparse.h>

/* For the PrefsStruct structure */
#include <scalos/preferences.h>

/* some standard stuff */
#include <stdio.h>
#include <string.h>

#include <clib/alib_protos.h>

/* Library bases */
struct Library          *GadToolsBase;
struct IntuitionBase    *IntuitionBase;
struct Library          *PreferencesBase;
struct GfxBase          *GfxBase;

/* Font (TextAttr) to use for the gadtools gadgets, simply hardcoded for this simple example.
 * Pointers to each one of the gadgets, so it is easier to work with them after creation.
 */
struct TextAttr ta = {"topaz.font", 8, 0, 0};
struct Gadget   *gadgets[3] = {0};

/* Function Prototypes */
int             OpenLibs(void);
void            CloseLibs(void);
struct Gadget   *CreateGadList(struct Screen *scr, APTR vi, WORD ww, WORD wh);
void            FreeGads(struct Gadget *glist);
struct Window   *openwin(WORD wx, WORD wy, WORD ww, WORD wh);
struct Node     *AllocNode(STRPTR name);
void            FreeNode(struct Node *node);


int main(void)
{
	struct Screen   *scr;           /* screen we want to open on */
	struct Window   *win;           /* Pointer to window */
	struct Gadget   *gad;           /* Pointer to gadtools gadgetlist */
	APTR            vi;             /* visualinfo for the screen */

	/* Window position and sizes */
	WORD    wx = 170;
	WORD    wy = 100;
	WORD    ww = 300;
	WORD    wh = 200;

	int             quit = 0;       /* flag to show when to quit program */

	struct IntuiMessage *imsg;      /* For communicating with intuition */
	ULONG               cls;        /* class of window event */
	UWORD               evc;        /* eventcode */
	struct Gadget       *gadhit;    /* for gadgetup events */
	UWORD               lasthit;    /* last item in listview hit */

	struct List         textlist;
	struct Node         *ptr;       /* working pointer for traversing list */

	APTR                prefhandle; /* handle for accessing our preferences */
	char                temp[256];  /* temporary storage for when reading string prefs */

	NewList(&textlist);             /* Initialise list */

	/* Open all required libraries and exit if any were failed to open */
	if(!OpenLibs())
	{
		CloseLibs();
		return(20);     
	}


	/* Try to read in some preferences */
	if(NULL != (prefhandle = AllocPrefsHandle("Example")) )
	{
		/* First, try to read the file that the preferences were stored in */
		ReadPrefsHandle(prefhandle, "example.prefs");

		/* Read window position preferences. If the tags do not exist (i.e. if they
		 * were not in the preferences file, or the preferences file was not loaded)
		 * then nothing will be copied into the destination memory. This means that
		 * the current contents of the positions/sizes will not be corrupted.
		 * We use the tag values for OpenWindowTagList for the window positions/sizes
		 * for the simple reason is that it makes it clearer what we are doing - plus it
		 * saves us some thinking to create our own tag values (which of course can be
		 * anything) :)
		 */
		GetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Left, &wx, 2);
		GetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Top, &wy, 2);
		GetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Width, &ww, 2);
		GetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Height, &wh, 2);

		/* Read a list of strings from the preferences that have been stored.
		 * If we try to read an item from the list which does not exist, we have
		 * reached the end of the list (remember that it is a list, not an array and
		 * that inserting at position 2000 may not insert at exactly position 2000 -
		 * if the list has onoly one entry, then it will get inserted after that
		 * and effectively be inserted at position 2
		 */
		evc = 0;    /* Treat this as the entry number */
		while(GetEntry(prefhandle, MAKE_ID('T','E','X','T'), 1, temp, 256, evc))
		{
			ptr = AllocNode((STRPTR)temp); if(ptr) AddTail(&textlist, ptr);
			evc++;
		}
	}

	/* Get default public screen for our use */
	if(NULL != (scr = LockPubScreen(NULL)) )
	{
		/* Get visual info for that screen (needed for creating gadtools gadgets)
		 * and then create the gadgets
		 */
		vi = GetVisualInfoA(scr, NULL);
		if(NULL != (gad = CreateGadList(scr, vi, ww, wh)) )
		{
			/* Set the list of text items for the Listview to display and try
			 * to open our window
			 */
			GT_SetGadgetAttrs(gadgets[0], NULL, NULL, GTLV_Labels, &textlist, TAG_DONE);
			if(NULL != (win = openwin(wx, wy, ww, wh)) )
			{
				/* Add gadget list to window. Refresh the gadget list (causing it to be
				 * drawn. Refresh the window with special processing for gadtools gadgets
				 */
				AddGList(win, gad, -1, -1, 0);
				RefreshGList(gad, win, 0, -1);
				GT_RefreshWindow(win, 0);

				/* window opened, do stuff */
				while(!quit)
				{
					/* Wait for messages on the window's IDCMP port */
					Wait(1 << win->UserPort->mp_SigBit);

					/* while messages can be read from the window's IDCMP port */
					while(NULL != (imsg = GT_GetIMsg(win->UserPort)) )
					{
						/* Take a copy of the information we are interested in and the reply the
						 * message
						 */
						cls = imsg->Class;
						evc = imsg->Code;
						gadhit = (struct Gadget *)imsg->IAddress;
						GT_ReplyIMsg(imsg);
						switch(cls)
						{
							case IDCMP_CLOSEWINDOW:
								/* window close gadget pressed, set flag to quit program */
								quit = 1;
								break;

							case IDCMP_GADGETUP:
								/* gadget has been released, do processing depending on what gadget was hit */
								switch(gadhit->GadgetID)
								{
									case 1:
										/* listview. take a copy of the number of the item that was pressed.
										 * move to that item in the list attached to the listview and
										 * set the string in the string gadget (and yes, I know that I could
										 * have used GTLV_ShowSelected with the pointer to the string gadget,
										 * but I didn't OK).
										 */
										lasthit = evc;
										ptr = textlist.lh_Head;
										while(evc-- && ptr->ln_Succ)
										{
											ptr = ptr->ln_Succ;
										}
										if(ptr->ln_Succ) GT_SetGadgetAttrs(gadgets[1], win, NULL, GTST_String, ptr->ln_Name, TAG_DONE);
										break;

									case 2:
										/* string gadget. detach the list from the listview and then try to add
										 * a new node to the list which contains the string in the string gadget.
										 * re-attach the list to the listview
										 */
										GT_SetGadgetAttrs(gadgets[0], win, NULL, GTLV_Labels, ~0, TAG_DONE);
										ptr = AllocNode((STRPTR)((struct StringInfo *)gadhit->SpecialInfo)->Buffer); if(ptr) AddTail(&textlist, ptr);
										GT_SetGadgetAttrs(gadgets[0], win, NULL, GTLV_Labels, &textlist, TAG_DONE);
										break;

									case 3:
										/* button. detach list from listview, move to position in list for the
										 * last item that was pressed in the listview. remove item from list
										 * and free the memory. re-attach the list to the listview
										 */
										GT_SetGadgetAttrs(gadgets[0], win, NULL, GTLV_Labels, ~0, TAG_DONE);
										ptr = textlist.lh_Head;
										while(lasthit-- && ptr->ln_Succ)
										{
											ptr = ptr->ln_Succ;
										}
										if(ptr->ln_Succ)
										{
											Remove(ptr);
											FreeNode(ptr);
										}
										GT_SetGadgetAttrs(gadgets[0], win, NULL, GTLV_Labels, &textlist, TAG_DONE);
										break;
								}
								break;

							case IDCMP_NEWSIZE:
								/* window has been resized. Take a copy of the new window sizes. Remove and free
								 * the gadgets from the window and try to re-create them with the new sizes.
								 */
								ww = win->Width;
								wh = win->Height;
								RemoveGList(win, gad, -1);
								FreeGads(gad);
								if(NULL == (gad = CreateGadList(scr, vi, ww, wh)) )
								{
									/* Gadgets were not created, so we just set the flag to quit the program */
									quit = 1;
								}
								else
								{
									/* gadgets were created OK, so attach the list to the listview. add the gadgets
									 * to the window, clear the inside of the window (not borders). refresh the
									 * window borders, then the gadgets with special processing for gadtools ones
									 */
									GT_SetGadgetAttrs(gadgets[0], NULL, NULL, GTLV_Labels, &textlist, TAG_DONE);
									AddGList(win, gad, -1, -1, 0);
									SetAPen(win->RPort, 0);
									RectFill(win->RPort, win->BorderLeft, win->BorderTop, win->Width-win->BorderRight, win->Height-win->BorderBottom);
									RefreshWindowFrame(win);
									RefreshGList(gad, win, 0, -1);
									GT_RefreshWindow(win, 0);
								}
								break;
						}
					} /* while get message */

				} /* while !quit */

				/* take copy of final x/y position of window. don't need to take the sizes,
				 * as we already have them from the IDCMP_NEWSIZE messages. Remove gadgets from window
				 * and close the window.
				 */
				wx = win->LeftEdge;
				wy = win->TopEdge;
				RemoveGList(win, gad, -1);
				CloseWindow(win);
			}
			/* Free gadgets */
			FreeGads(gad);

		} /* if opened window */
		/* free visual info for screen and unlock the public screen */
		FreeVisualInfo(vi);
		UnlockPubScreen(NULL, scr);
	} /* if opened default pubscreen */

	/* Save prefs. If we do not have a valid PrefsHandle, try to allocate one here. */
	if(!prefhandle) prefhandle = AllocPrefsHandle("Example");
	if(prefhandle)
	{
		/* Set all the new window positions/sizes */
		SetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Left, &wx, 2);
		SetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Top, &wy, 2);
		SetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Width, &ww, 2);
		SetPreferences(prefhandle, MAKE_ID('W','N','D','O'), WA_Height, &wh, 2);

		/* Clear the previous list preferences so that we are sure to only save what we add using SetEntry */
		while(RemEntry(prefhandle, MAKE_ID('T','E','X','T'), 1, 0)){;}

		/* go through the list that was attached to the listview and store each string
		 * as an item in a multiple preference per tag item.
		 */
		evc = 0;    /* Treat this as the entry number */
		ptr = textlist.lh_Head;
		while(ptr->ln_Succ)
		{
			if(ptr->ln_Name) SetEntry(prefhandle, MAKE_ID('T','E','X','T'), 1, ptr->ln_Name, strlen(ptr->ln_Name)+1, evc);
			evc++;
			ptr = ptr->ln_Succ;
		}

		/* Save the preferences and then free them */
		WritePrefsHandle(prefhandle, "example.prefs");
		FreePrefsHandle(prefhandle);
	}

	/* Finally, free all memory for the list */
	while(NULL != (ptr = RemHead(&textlist)) )
	{
		FreeNode(ptr);
	}

	CloseLibs();
}


/* Opens all required libraries for this program and returns true (for success) or
 * false (for failure)
 */
int             OpenLibs(void)
{
	GadToolsBase = OpenLibrary("gadtools.library", 36);
	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36);
	PreferencesBase = OpenLibrary("preferences.library", 39);
	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 36);
	if(GadToolsBase && IntuitionBase && PreferencesBase && GfxBase) return(1);
	return(0);
}


/* closes all opened libraries */
void            CloseLibs(void)
{
	if(GadToolsBase) CloseLibrary(GadToolsBase);
	if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	if(PreferencesBase) CloseLibrary(PreferencesBase);
	if(GfxBase) CloseLibrary((struct Library *)GfxBase);
}


/* creates gadget list */
struct Gadget   *CreateGadList(struct Screen *scr, APTR vi, WORD ww, WORD wh)
{
	struct Gadget       *gad, *glist;
	struct NewGadget    ng[] = {{8,20,0,0,NULL,&ta,1,0,0,0},
								{8,0,0,14,NULL,&ta,2,0,0,0},
								{0,0,40,14,"Del",&ta,3,16,0,0}};

	ng[0].ng_Width = ww - 40;
	ng[0].ng_Height = wh - 60;
	ng[0].ng_VisualInfo = vi;

	ng[1].ng_TopEdge = 20 + wh - 60;
	ng[1].ng_Width = ww - 80;
	ng[1].ng_VisualInfo = vi;

	ng[2].ng_LeftEdge = 8 + ww - 80;
	ng[2].ng_TopEdge = 20 + wh - 60;
	ng[2].ng_VisualInfo = vi;

	if(NULL == (gad = CreateContext(&glist)) ) return(NULL);

	if(NULL == (gad = CreateGadget(LISTVIEW_KIND, gad, &ng[0], GTLV_Labels, NULL, TAG_DONE)) )
	{
		FreeGads(glist);
		return(NULL);
	}
	gadgets[0] = gad;

	if(NULL == (gad = CreateGadget(STRING_KIND, gad, &ng[1], GTST_MaxChars, 256, TAG_DONE)) )
	{
		FreeGads(glist);
		return(NULL);
	}
	gadgets[1] = gad;

	if(NULL == (gad = CreateGadgetA(BUTTON_KIND, gad, &ng[2], NULL)) )
	{
		FreeGads(glist);
		return(NULL);
	}
	gadgets[2] = gad;

	return(glist);
}


/* frees gadget list if it exists */
void            FreeGads(struct Gadget *glist)
{
	if(glist) FreeGadgets(glist);
}


/* open window (just takes the huge mess of the taglist out of the main function */
struct Window   *openwin(WORD wx, WORD wy, WORD ww, WORD wh)
{
	return(OpenWindowTags(NULL,  WA_Left, wx,
								 WA_Top, wy,
								 WA_Width, ww,
								 WA_Height, wh,
								 WA_Flags, WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_SIZEGADGET|WFLG_ACTIVATE|WFLG_NOCAREREFRESH,
								 WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|IDCMP_GADGETUP,
								 WA_MinWidth, 150,
								 WA_MinHeight, 150,
								 WA_MaxWidth, 640,
								 WA_MaxHeight, 480,
								 WA_AutoAdjust, 1,
								 WA_PubScreen, NULL,
								 TAG_DONE));
}


/* allocates a Node and some memory for the ln_Name and copies the specified name into it */
struct Node     *AllocNode(STRPTR name)
{
	struct  Node    *node;

	if(NULL != (node = AllocVec(sizeof(struct Node), MEMF_PUBLIC|MEMF_CLEAR)) )
	{
		if(name)
		{
			if(NULL != (node->ln_Name = AllocVec(strlen(name)+1, MEMF_PUBLIC|MEMF_CLEAR)) )
			{
				CopyMem(name, node->ln_Name, strlen(name));
			}
		}
	}
	return(node);
}


/* Frees a Node and also the name */
void            FreeNode(struct Node *node)
{
	if(node)
	{
		if(node->ln_Name) FreeVec(node->ln_Name);
		FreeVec(node);
	}
}



