/** listreq.c
  *
  *   A GadTools List requester
  *
  *   AUTHOR/DATE: Willy Langeveld, Early 1992.
  *	  ============
  *
  *	CURRENT VERSION:
  *
  *	This version has been converted to SAS C 6.5 format. It has been modified
  *	for modern definition sequences for ANSI compilation. This no longer works
  *	with OS versions prior to 2.04.
  *
  *   AUTHOR/DATE:  Joanne Dow, jdow@bix.com, June 1998.
  *   ============
  *
  **/

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <proto/all.h>
#include "ralprotos.h"
#include <proto/rexxsyslib.h>
#include "rexxarplib.h"

char *rxf_getlist(long *, int, char **);

static struct List *AddList(struct List *, char *, long);
static struct List *NukeList(struct List *);
static struct List *GetRexxList(struct RexxMsg *, long);

static struct Node *ListRequest(struct Screen *, struct List *, long, long, long, long);
/*
 *   The layout of the requester
 */
#define MARGIN           10
#define GADGET_SPACING    5
/*
 *   The library we need
 */
extern struct Library *GadToolsBase;
/*
 *   Use Topaz-80 font.
 */
static struct TextAttr topaz80 = 
{
	"topaz.font", 8, 0, 0 
};

/*
 *   Prototype - for reference
 */
// struct Node *ListRequest(struct Screen *, struct List *, long, long, long, long); // defined above
/*
 *   quitflag values
 */
#define NOTDONE  0
#define NOCHANGE 1
#define CHANGE   2

extern struct Library *RexxSysBase;
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;

struct Library *GadToolsBase = NULL;

#define DO_SORT 1
#define NO_SORT 0


char *rxf_getlist( long *err, int n, char **s )
{
	long           sortflag;
	long           x = 0, y = 0, w = 0, h = 0;
	struct List    *list = NULL;
	struct Node    *result;
	char           *screenname = NULL, *flags = NULL, *ptr = NULL;
	struct Screen  *screen = NULL;
	struct RexxMsg *rmptr = (struct RexxMsg *) s;
	
	*err = 17L;
	if (n < 4 || n > 10)
		goto cleanup;
	
	*err = 14L;
	GadToolsBase = OpenLibrary("gadtools.library",0);
	if (GadToolsBase == 0L)
		goto cleanup;
	
	*err = 10L;
	if (CheckRexxMsg( (struct Message *) rmptr) == 0L)
		goto cleanup;
	
	if ((n = (rmptr->rm_Action & 0xFF)) < 3) 
	{
		*err = 17L;
	}
	else
	{
		if (n >= 4 && rmptr->rm_Args[4])
			x = atol(rmptr->rm_Args[4]);
		if (n >= 5 && rmptr->rm_Args[5])
			y = atol(rmptr->rm_Args[5]);
		if (n >= 6 && rmptr->rm_Args[6])
			w = atol(rmptr->rm_Args[6]);
		if (n >= 7 && rmptr->rm_Args[7])
			h = atol(rmptr->rm_Args[7]);
		if (n >= 8 && rmptr->rm_Args[8])
			screenname = rmptr->rm_Args[8];
		if (n >= 9 && rmptr->rm_Args[9])
			flags      = rmptr->rm_Args[9];
		
		sortflag = NO_SORT;
		if (flags && (stricmp(flags, "SORT") == 0))
			sortflag = DO_SORT;
		
		list = GetRexxList(rmptr, sortflag);
		
		if (list) 
		{
			if (screenname)
				screen = LockPubScreen(screenname);
			
			result = ListRequest(screen, list, x, y, w, h);
			
			if (screen)
				UnlockPubScreen(NULL, screen);
			
			if (result)
				ptr = CAS(result->ln_Name);
			else
				ptr = CAS("");
			
			list = NukeList(list);
			
			*err = 0L;
		}
		else
		{
			*err = 12L;
		}
	}
	
	cleanup:
	
	if (GadToolsBase)
		CloseLibrary(GadToolsBase);
	
	if (*err == 0L)
		return(ptr);
	else
		return(CAS(""));
}


static struct List *GetRexxList( struct RexxMsg *rmptr, long sortflag )
{
	long left, right, i, error;
	char buffer[256];
	STRPTR value;
	struct List *list = NULL;
	/*
	 *   Get left and right bounds of string array to be sorted
	 */
	left  = atol(rmptr->rm_Args[1]);
	right = atol(rmptr->rm_Args[2]);
	/*
	 *   Copy RexxVariable array into strings
	 */
	for (i = left; i <= right; i++) 
	{
		/*
		 *   Put name of stem in buffer
		 */
		sprintf(buffer, "%s.%ld", rmptr->rm_Args[3], i);
		/*
		 *   Now get the string from the program
		 */
		error = GetRexxVar( (struct Message *) rmptr, buffer, &value); // !!!!, 256);
		if (error) 
		{
			list = NukeList(list);
			break;
		}
		list = AddList(list, (char *) value, sortflag);
	}
	return(list);
}


/**
 *
 *   Some list utility functions.
 *   AddList adds a string to an Exec list. If the list doesn't exist
 *   yet, it is first created. If sortflag is true, the list is sorted
 *   using insertion sort.
 *   NukeList deletes an entire list created by AddList.
 *
**/
#define LSIZE ((long) sizeof(struct List))
#define NSIZE ((long) sizeof(struct Node))
	
static struct List *AddList( struct List *list, char *s, long sortflag )
{
	struct Node *n, *nt;
	char *ptr;
	
	if (list == NULL) 
	{
		list = AllocMem(LSIZE, MEMF_CLEAR);
		if (list == NULL)
			return(NULL);
		NewList(list);
	}
	
	if (s) 
	{
		n = AllocMem(NSIZE, MEMF_CLEAR);
		if (n == NULL)
			return(NukeList(list));
		
		ptr = AllocMem( (ULONG) strlen(s) + 1L, 0L);
		if (ptr == NULL)
			return(NukeList(list));
		
		strcpy(ptr, s);
		n->ln_Name = ptr;
		
		if (sortflag) 
		{
			nt = list->lh_Head;
			while (nt->ln_Succ) 
			{
				if (stricmp(nt->ln_Name, ptr) >= 0)
					break;
				nt = nt->ln_Succ;
			}
			Insert(list, n, nt->ln_Pred);
		}
		else
		{
			AddTail(list, n);
		}
	}
	
	return(list);
}

static struct List *NukeList( struct List *list )
{
	struct Node *n, *nn;
	char *ptr;
	
	if (list) 
	{
		if (list != (struct List *) list->lh_TailPred) 
		{
			n = list->lh_Head;
			while (nn = n->ln_Succ) 
			{
				Remove(n);
				if (ptr = n->ln_Name)
					FreeMem(ptr, (ULONG) strlen(ptr) + 1L);
				FreeMem(n, NSIZE);
				n = nn;
			}
		}
		FreeMem(list, LSIZE);
	}
	return(NULL);
}


struct Window *OpenWindowTagsX(struct NewWindow *nw, unsigned long tags, ...)
{
	return(OpenWindowTagList(nw, (struct TagItem *) &tags));
}

struct Gadget *CreateGadgetX(long type, struct Gadget *gad, struct NewGadget *ng, unsigned long tags, ...)
{
	return(CreateGadgetA(type, gad, ng, (struct TagItem *) &tags));
}

/**
 *
 *   List requester
 *
**/
static struct Node *ListRequest( struct Screen *scr, struct List *list, LONG x, LONG y, LONG width, LONG height )
{
	struct VisualInfo *vi = NULL;
	struct Gadget *glist = NULL, *gad, *gadget;
	struct NewGadget ng;
	struct Window *w = NULL;
	long top, quitflag = NOTDONE;
	ULONG class;
	WORD code, selected;
	struct IntuiMessage *msg;
	struct Node *n = NULL;
	/*
	 *   Check args
	 */
	if (width < 80)
		width = 80;
	if (height < 70)
		height = 70;
	/*
	 *   Get screen info
	 */
	if (scr) 
	{
		if (((scr->Flags & SCREENTYPE) == PUBLICSCREEN) ||
		((scr->Flags & SCREENTYPE) == CUSTOMSCREEN)   ) 
		{
			vi = GetVisualInfoA(scr, NULL);
		}
	}
	/*
	 *   Otherwise get access to the WorkBench screen
	 */
	if (vi == NULL) 
	{
		scr = LockPubScreen(NULL);
		if (scr == NULL)
			goto cleanup;
		
		vi = GetVisualInfoA(scr, NULL);
		UnlockPubScreen(NULL, scr);
	}
	
	if (vi == NULL)
		goto cleanup;
	/*
	 *   Now open up the window
	 */
	w = OpenWindowTagsX(NULL,
	WA_Left,         (ULONG) x,
	WA_Top,          (ULONG) y,
	WA_Width,        (ULONG) width,
	WA_Height,       (ULONG) height,
	WA_IDCMP,        (ULONG) LISTVIEWIDCMP | CLOSEWINDOW,
	WA_Flags,        (ULONG) WINDOWCLOSE  | SMART_REFRESH |
	WINDOWDRAG   | WINDOWDEPTH   |
	ACTIVATE,
	WA_Title,        (ULONG) "Select item:",
	WA_CustomScreen, (ULONG) scr,
	TAG_DONE);
	
	if (w == NULL)
		goto cleanup;
	/*
	 *   Create the GadTools context
	 */
	gad = CreateContext(&glist);
	if (gad == NULL)
		goto cleanup;
	/*
	 *   A palette gadget
	 */
	top = w->BorderTop + GADGET_SPACING;
	
	ng.ng_LeftEdge   = MARGIN;
	ng.ng_TopEdge    = top;
	ng.ng_Width      = width - 2 * MARGIN;
	ng.ng_Height     = height - top - GADGET_SPACING;
	ng.ng_GadgetText = NULL;
	ng.ng_GadgetID   = 1;
	ng.ng_Flags      = 0;
	ng.ng_TextAttr   = &topaz80;
	ng.ng_VisualInfo = vi;
	gad = CreateGadgetX(LISTVIEW_KIND, gad, &ng,
	GTLV_Labels,       (ULONG) list,
	GTLV_ScrollWidth,  (ULONG) 18,
	TAG_DONE);
	/*
	 *   Add the gadget list to the window.
	 */
	AddGList(w, glist, -1, -1, NULL);
	RefreshGList(glist, w, NULL, -1);
	GT_RefreshWindow(w, NULL);
	/*
	 *   Wait for the user to do things
	 */
	while (quitflag == NOTDONE) 
	{
		WaitPort(w->UserPort);
		while (msg = GT_GetIMsg(w->UserPort)) 
		{
			class  = msg->Class;
			code   = (WORD) msg->Code;
			gadget = (struct Gadget *) msg->IAddress;
			
			GT_ReplyIMsg(msg);
			
			if (class == GADGETUP) 
			{
				selected = code;
				quitflag = CHANGE;
			}
			else
			if (class == CLOSEWINDOW) 
			{
				quitflag = NOCHANGE;
			}
		}
	}
	/*
	 *   This sequence shuts down the list requester
	 */
	cleanup:
	if (w)
		CloseWindow(w);
	if (glist)
		FreeGadgets(glist);
	if (vi)
		FreeVisualInfo(vi);
	
	if (quitflag == CHANGE) 
	{
		if (list) 	// Compiler squawk here is "probably safe", I think. JDow
		{
			n = list->lh_Head;
			while (selected--)
				n = n->ln_Succ;
		}
	}
	
	return(n);
}



