/*
**	termPickFile.c
**
**	Simplified file selection routines
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

enum	{	GAD_LIST,GAD_USE,GAD_SELECT,GAD_CANCEL };

	/* CreateAllGadgets():
	 *
	 *	Get them gadgets goin'.
	 */

STATIC struct Gadget *
CreateAllGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,struct List *List,STRPTR Current,LONG *Index)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;
	struct Node		*Node,
				*Next;
	BYTE			 GotIt = FALSE;

	*Index = 0;

	Node = List -> lh_Head;

	while(!GotIt && (Next = Node -> ln_Succ))
	{
		if(!Stricmp(Node -> ln_Name,Current))
			GotIt = TRUE;
		else
			(*Index)++;

		Node = Next;
	}

	if(!GotIt)
		*Index = ~0;

	SZ_SizeSetup(Window -> WScreen,&UserFont,TRUE);

	memset(&NewGadget,0,sizeof(struct NewGadget));

	if(Gadget = CreateContext(GadgetList))
	{
		WORD ButtonWidth,ListWidth;

		SZ_ResetMaxWidth();

		SZ_UpdateMaxWidth(BUTTON_KIND,LocaleString(MSG_GLOBAL_USE_GAD),0,NULL);
		SZ_UpdateMaxWidth(BUTTON_KIND,LocaleString(MSG_TERMPICKFILE_SELECT_GAD),0,NULL);
		SZ_UpdateMaxWidth(BUTTON_KIND,LocaleString(MSG_GLOBAL_CANCEL_GAD),0,NULL);

		ButtonWidth = SZ_ResetMaxWidth();

		SZ_UpdateMaxWidth(LISTVIEW_KIND,NULL,40,NULL);

		ListWidth = SZ_ResetMaxWidth();

		if(ListWidth < 3 * ButtonWidth + 2 * InterWidth)
			ListWidth = 3 * ButtonWidth + 2 * InterWidth;
		else
			ButtonWidth = (ListWidth - 2 * InterWidth) / 3;

		SZ_SetWidth(ListWidth);

		NewGadget . ng_GadgetText	= LocaleString(MSG_TERMPICKFILE_FILE_LIST_GAD);
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_ABOVE;

		GadgetArray[Counter++] = Gadget = CreateGadget(LISTVIEW_KIND,Gadget,&NewGadget,
			SZ_Adjust,		TRUE,
			SZ_AutoWidth,		TRUE,
			SZ_Lines,		10,
			SZ_InterHeight,		0,

			GTLV_Labels,		List,
			GTLV_ShowSelected,	NULL,
			GTLV_Selected,		*Index,
		TAG_DONE);

		SZ_SetWidth(ButtonWidth);

		NewGadget . ng_GadgetText	= LocaleString(MSG_GLOBAL_USE_GAD);
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			SZ_Adjust,	TRUE,
			SZ_AutoWidth,	TRUE,
			SZ_AlignExtra,	TRUE,
			SZ_AlignLeft,	TRUE,
			SZ_AlignBottom,	TRUE,
			SZ_GroupCount,	3,
		TAG_DONE);

		NewGadget . ng_GadgetText	= LocaleString(MSG_TERMPICKFILE_SELECT_GAD);
		NewGadget . ng_GadgetID		= Counter;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			SZ_Adjust,	TRUE,
			SZ_AutoWidth,	TRUE,
			SZ_GroupNext,	TRUE,
		TAG_DONE);

		NewGadget . ng_GadgetText	= LocaleString(MSG_GLOBAL_CANCEL_GAD);
		NewGadget . ng_GadgetID		= Counter;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			SZ_Adjust,	TRUE,
			SZ_AutoWidth,	TRUE,
			SZ_GroupNext,	TRUE,
		TAG_DONE);
	}

	return(Gadget);
}

	/* AddFile(struct List *List,STRPTR Name):
	 *
	 *	Add another file to the list:
	 */

STATIC VOID __regargs
AddFile(struct List *List,STRPTR Name)
{
	struct Node *NewNode;

	if(NewNode = CreateNode(Name))
	{
		struct Node	*Node,
				*Next;
		BYTE		 GotIt = FALSE;
		LONG		 Result;

		Node = List -> lh_Head;

		while(Next = Node -> ln_Succ)
		{
			if((Result = Stricmp(Name,Node -> ln_Name)) < 0)
			{
				Insert(List,NewNode,Node -> ln_Pred);

				GotIt = TRUE;

				break;
			}
			else
			{
					/* Don't add two identical names. */

				if(!Result)
				{
					FreeVec(NewNode);

					return;
				}
			}

			Node = Next;
		}

		if(!GotIt)
			AddTail(List,NewNode);
	}
}

	/* FileMultiScan(struct List *FileList,STRPTR Directory,STRPTR Pattern):
	 *
	 *	Scan an assignment, also handles multipath assignments.
	 */

STATIC VOID __regargs
FileMultiScan(struct List *FileList,STRPTR Directory,STRPTR Pattern)
{
	struct DevProc		*DevProc	= NULL;
	struct MsgPort		*FileSysTask	= GetFileSysTask();
	struct FileInfoBlock	*FileInfo;
	UBYTE			 MatchBuffer[MAX_FILENAME_LENGTH];

	if(ParsePatternNoCase(Pattern,MatchBuffer,MAX_FILENAME_LENGTH) != -1)
	{
			/* Allocate the fileinfo data. */

		if(FileInfo = (struct FileInfoBlock *)AllocDosObjectTags(DOS_FIB,TAG_DONE))
		{
				/* Loop until all assignments are
				 * processed.
				 */

			do
			{
					/* Get the default filesystem task
					 * in case we stumble upon NULL
					 * directory locks.
					 */

				if(DevProc = GetDeviceProc(Directory,DevProc))
				{
						/* Set the default filesystem task. */

					SetFileSysTask(DevProc -> dvp_Port);

						/* Check the object type. */

					if(Examine(DevProc -> dvp_Lock,FileInfo))
					{
							/* Is it really a directory? */

						if(FileInfo -> fib_DirEntryType > 0)
						{
								/* Scan the directory... */

							while(ExNext(DevProc -> dvp_Lock,FileInfo))
							{
									/* Did we find a file? */

								if(FileInfo -> fib_DirEntryType < 0)
								{
										/* Does the name match the template? */

									if(MatchPatternNoCase(MatchBuffer,FileInfo -> fib_FileName))
										AddFile(FileList,FileInfo -> fib_FileName);
								}
							}
						}
					}
				}
				else
					break;
			}
			while(DevProc && (DevProc -> dvp_Flags & DVPF_ASSIGN));

				/* Free the fileinfo data. */

			FreeDosObject(DOS_FIB,FileInfo);
		}
	}

		/* Reset the default filesystem task. */

	SetFileSysTask(FileSysTask);

		/* Free device process data. */

	if(DevProc)
		FreeDeviceProc(DevProc);
}

	/* FreeFileList(struct List *List):
	 *
	 *	Free a list and list contents.
	 */

STATIC VOID __regargs
FreeFileList(struct List *List)
{
	struct Node	*Node,
			*Next;

	Node = List -> lh_Head;

	while(Next = Node -> ln_Succ)
	{
		FreeVec(Node);

		Node = Next;
	}

	FreeVec(List);
}

	/* IsAssign(STRPTR Name):
	 *
	 *	Does a name refer to an assignment?
	 */

STATIC BYTE __regargs
IsAssign(STRPTR Name)
{
	WORD NameLen	= strlen(Name) - 1;
	BYTE Result	= FALSE;

		/* Does it end with a colon? */

	if(Name[NameLen] == ':')
	{
		struct DosList *DosList;

			/* Lock the list of assignments for reading. */

		if(DosList = AttemptLockDosList(LDF_ASSIGNS | LDF_READ))
		{
			STRPTR AssignName;

				/* Scan the list... */

			while(DosList = NextDosEntry(DosList,LDF_ASSIGNS))
			{
					/* Convert the name from icky
					 * BCPL to `C' style string.
					 */

				AssignName = (STRPTR)BADDR(DosList -> dol_Name);

					/* Does the name length match? */

				if(AssignName[0] == NameLen)
				{
						/* Does the name itself match? */

					if(!Strnicmp(&AssignName[1],Name,NameLen))
					{
						Result = TRUE;

						break;
					}
				}
			}

				/* Unlock the list of assignments. */

			UnLockDosList(LDF_ASSIGNS | LDF_READ);
		}
	}

		/* Return the result. */

	return(Result);
}

	/* BuildFileList(STRPTR Directory,STRPTR Pattern):
	 *
	 *	Build a list of files in an assigned directory
	 *	matching a certain pattern.
	 */

STATIC struct List * __regargs
BuildFileList(STRPTR Directory,STRPTR Pattern)
{
	APTR		 OldPtr = ThisProcess -> pr_WindowPtr;
	struct List	*FileList = NULL;
	BPTR		 NewDir;

		/* No DOS requesters, please! */

	ThisProcess -> pr_WindowPtr = (APTR)-1;

		/* Is the assignment present? */

	if(NewDir = Lock(Directory,ACCESS_READ))
	{
			/* Allocate space for the new list. */

		if(FileList = (struct List *)AllocVec(sizeof(struct List),MEMF_ANY))
		{
				/* Initialize the list. */

			NewList(FileList);

				/* Will we have to deal with
				 * an assignment or a volume?
				 */

			if(IsAssign(Directory))
				FileMultiScan(FileList,Directory,Pattern);
			else
			{
				struct FileInfoBlock	*FileInfo;
				UBYTE			 MatchBuffer[MAX_FILENAME_LENGTH];

				if(ParsePatternNoCase(Pattern,MatchBuffer,MAX_FILENAME_LENGTH) != -1)
				{
						/* Allocate space for a fileinfo block. */

					if(FileInfo = (struct FileInfoBlock *)AllocDosObjectTags(DOS_FIB,TAG_DONE))
					{
							/* Take a look at the assignment. */

						if(Examine(NewDir,FileInfo))
						{
								/* Does it really refer to a directory? */

							if(FileInfo -> fib_DirEntryType > 0)
							{
									/* Examine the whole directory. */

								while(ExNext(NewDir,FileInfo))
								{
										/* Is it a file? */

									if(FileInfo -> fib_DirEntryType < 0)
									{
										if(MatchPatternNoCase(MatchBuffer,FileInfo -> fib_FileName))
											AddFile(FileList,FileInfo -> fib_FileName);
									}
								}
							}
						}

							/* Free the fileinfo data. */

						FreeDosObject(DOS_FIB,FileInfo);
					}
				}
			}

				/* Does the list contain any entries? */

			if(!FileList -> lh_Head -> ln_Succ)
			{
				FreeVec(FileList);

				FileList = NULL;
			}
		}

			/* Release the lock on the directory. */

		UnLock(NewDir);
	}

		/* Enable DOS requesters again. */

	ThisProcess -> pr_WindowPtr = OldPtr;

		/* Return the file name list. */

	return(FileList);
}

	/* PickFile(STRPTR Directory,STRPTR Pattern,STRPTR Prompt,STRPTR Name):
	 *
	 *	Your nice file selection routine. No need to hunt for
	 *	a library/device by checking all assignments by hand.
	 */

BYTE
PickFile(STRPTR Directory,STRPTR Pattern,STRPTR Prompt,STRPTR Name)
{
	struct Window		*PanelWindow;
	struct Gadget		*GadgetList = NULL;
	struct Gadget		*GadgetArray[GAD_CANCEL + 1];
	UBYTE		 	 DummyBuffer[MAX_FILENAME_LENGTH],
				*DummyChar;
	struct FileRequester	*FileRequest;
	struct List		*FileList;
	LONG			 Index;
	BYTE			 Result = FALSE;

	if(!(FileList = BuildFileList(Directory,Pattern)))
	{
		strcpy(DummyBuffer,Name);

		DummyChar = PathPart(DummyBuffer);

		*DummyChar = 0;

		if(FileRequest = GetFile(Prompt,DummyBuffer,FilePart(Name),DummyBuffer,Pattern,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_SELECT_TXT),TRUE))
		{
			if(!Stricmp(FileRequest -> rf_Dir,Directory))
				strcpy(Name,FileRequest -> rf_File);
			else
				strcpy(Name,DummyBuffer);

			FreeAslRequest(FileRequest);

			return(TRUE);
		}

		return(FALSE);
	}

	if(CreateAllGadgets(GadgetArray,&GadgetList,FileList,Name,&Index))
	{
		if(PanelWindow = OpenWindowTags(NULL,
			WA_Left,	GetScreenLeft(Window) + (GetScreenWidth(Window) - SZ_GetWindowWidth()) / 2,
			WA_Top,		GetScreenTop(Window) + (GetScreenHeight(Window) - SZ_GetWindowHeight())	/ 2,
			WA_Width,	SZ_GetWindowWidth(),
			WA_Height,	SZ_GetWindowHeight(),

			WA_Activate,		TRUE,
			WA_DragBar,		TRUE,
			WA_DepthGadget,		TRUE,
			WA_RMBTrap,		TRUE,
			WA_CloseGadget,		TRUE,
			WA_CustomScreen,	Window -> WScreen,
			WA_NoCareRefresh,	TRUE,

			WA_IDCMP,		IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_ACTIVEWINDOW | LISTVIEWIDCMP | BUTTONIDCMP,

			WA_Title,		Prompt,
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 IClass,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			PushWindow(PanelWindow);

			AddGList(PanelWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,PanelWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(PanelWindow,NULL);

			if(Index != ~0)
			{
				GT_SetGadgetAttrs(GadgetArray[GAD_LIST],PanelWindow,NULL,
					GTLV_Top,		Index,
					GTLV_MakeVisible,	Index,
				TAG_DONE);
			}

			ClrSignal(SIG_BREAK);

			while(!Terminated)
			{
				if(Wait(PORTMASK(PanelWindow -> UserPort) | SIG_BREAK) & SIG_BREAK)
					break;

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(PanelWindow -> UserPort)))
				{
					IClass	= Massage -> Class;
					Code	= Massage -> Code;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					KeySelect(GadgetArray,GAD_CANCEL,Code,PanelWindow,&Gadget,&IClass,&Code);

					if(IClass == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(IClass == IDCMP_GADGETUP)
					{
						switch(Gadget -> GadgetID)
						{
							case GAD_USE:

								if(Index != ~0)
								{
									struct Node *Node;

									if(Node = GetListNode(Index,FileList))
									{
										strcpy(Name,Node -> ln_Name);

										Result = TRUE;
									}
								}

								Terminated = TRUE;
								break;

							case GAD_CANCEL:

								Terminated = TRUE;
								break;

							case GAD_LIST:

								Index = Code;
								break;

							case GAD_SELECT:

								strcpy(DummyBuffer,Name);

								BlockWindow(PanelWindow);

								if(FileRequest = GetFile(Prompt,Directory,Name,DummyBuffer,Pattern,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_SELECT_TXT),TRUE))
								{
									if(!Stricmp(FileRequest -> rf_Dir,Directory))
										strcpy(Name,FileRequest -> rf_File);
									else
										strcpy(Name,DummyBuffer);

									FreeAslRequest(FileRequest);

									Terminated = Result = TRUE;
								}

								ReleaseWindow(PanelWindow);

								break;
						}
					}
				}
			}

			RemoveGList(PanelWindow,GadgetList,(UWORD)-1);

			PopWindow();

			CloseWindow(PanelWindow);
		}
	}

	FreeFileList(FileList);

	FreeGadgets(GadgetList);

	return(Result);
}
