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

#include "termGlobal.h"

#include <exec/resident.h>

enum	{	GAD_LIST=1,GAD_USE,GAD_SELECT,GAD_CANCEL };

BOOLEAN __regargs
ValidateFile(STRPTR FileName,LONG Type,STRPTR RealName)
{
	BOOLEAN	Valid = FALSE;
	BPTR	Segment;

	if(Segment = LoadSeg(FileName))
	{
		ULONG	*SegmentData;
		BPTR	 SegPtr = Segment;
		UWORD	*Match;
		BOOLEAN	 GotIt = FALSE;
		LONG	 Size;

		do
		{
			SegmentData = (ULONG *)BADDR(SegPtr);

			SegPtr	= (BPTR)SegmentData[0];
			Size	= SegmentData[-1] - 2 * sizeof(ULONG) - sizeof(struct Resident);
			Match	= (UWORD *)(SegmentData + 1);

			while(!GotIt && Size > 0)
			{
				if(*Match == RTC_MATCHWORD)
				{
					struct Resident *Resident = (struct Resident *)Match;

					if(Resident -> rt_MatchTag == Resident)
					{
						if(Resident -> rt_Type == Type && Resident -> rt_Name)
						{
							if(!Stricmp(FilePart(FileName),Resident -> rt_Name))
							{
								if(RealName)
									strcpy(RealName,Resident -> rt_Name);

								Valid = TRUE;
							}
						}

						GotIt = TRUE;
					}
				}

				Match	+= 2;
				Size	-= sizeof(ULONG);
			}
		}
		while(!GotIt && SegPtr);

		UnLoadSeg(Segment);
	}

	return(Valid);
}

	/* 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)
				{
					FreeVecPooled(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,LONG Type)
{
	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))
									{
										if(Type == -1)
											AddFile(FileList,FileInfo -> fib_FileName);
										else
										{
											UBYTE LocalBuffer[MAX_FILENAME_LENGTH],RealName[MAX_FILENAME_LENGTH];

											strcpy(LocalBuffer,Directory);

											if(AddPart(LocalBuffer,FileInfo -> fib_FileName,MAX_FILENAME_LENGTH))
											{
												if(ValidateFile(LocalBuffer,Type,RealName))
													AddFile(FileList,RealName);
											}
										}
									}
								}
							}
						}
					}
				}
				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)
	{
		FreeVecPooled(Node);

		Node = Next;
	}

	FreeVecPooled(List);
}

	/* 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,LONG Type)
{
	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 *)AllocVecPooled(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,Type);
			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))
										{
											if(Type == -1)
												AddFile(FileList,FileInfo -> fib_FileName);
											else
											{
												UBYTE LocalBuffer[MAX_FILENAME_LENGTH],RealName[MAX_FILENAME_LENGTH];

												strcpy(LocalBuffer,Directory);

												if(AddPart(LocalBuffer,FileInfo -> fib_FileName,MAX_FILENAME_LENGTH))
												{
													if(ValidateFile(LocalBuffer,Type,RealName))
														AddFile(FileList,RealName);
												}
											}
										}
									}
								}
							}
						}

							/* Free the fileinfo data. */

						FreeDosObject(DOS_FIB,FileInfo);
					}
				}
			}

			if(Type == NT_DEVICE)
			{
				UBYTE MatchBuffer[MAX_FILENAME_LENGTH];

				if(ParsePatternNoCase(Pattern,MatchBuffer,MAX_FILENAME_LENGTH) != -1)
				{
					struct Node *Node;

					Forbid();

					for(Node = SysBase -> DeviceList . lh_Head ; Node -> ln_Succ ; Node = Node -> ln_Succ)
					{
						if(MatchPatternNoCase(MatchBuffer,Node -> ln_Name))
							AddFile(FileList,Node -> ln_Name);
					}

					Permit();
				}
			}

			if(Type == NT_LIBRARY)
			{
				UBYTE MatchBuffer[MAX_FILENAME_LENGTH];

				if(ParsePatternNoCase(Pattern,MatchBuffer,MAX_FILENAME_LENGTH) != -1)
				{
					struct Node *Node;

					Forbid();

					for(Node = SysBase -> LibList . lh_Head ; Node -> ln_Succ ; Node = Node -> ln_Succ)
					{
						if(MatchPatternNoCase(MatchBuffer,Node -> ln_Name))
							AddFile(FileList,Node -> ln_Name);
					}

					Permit();
				}
			}

				/* Does the list contain any entries? */

			if(!FileList -> lh_Head -> ln_Succ)
			{
				FreeVecPooled(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 __regargs
PickFile(struct Window *Window,STRPTR Directory,STRPTR Pattern,STRPTR Prompt,STRPTR Name,LONG Type)
{
	UBYTE		 	 DummyBuffer[MAX_FILENAME_LENGTH],
				*DummyChar;
	struct FileRequester	*FileRequest;
	struct List		*FileList;
	BYTE			 Result = FALSE;

	if(FileList = BuildFileList(Directory,Pattern,Type))
	{
		struct LayoutHandle *Handle;

		if(Handle = LT_CreateHandleTags(Window -> WScreen,
			LH_LocaleHook,	&LocaleHook,
		TAG_DONE))
		{
			struct Window	*PanelWindow;
			ULONG		 Index = (ULONG)~0,i;
			struct Node	*Node;

			for(Node = FileList -> lh_Head, i = 0 ; Node -> ln_Succ ; Node = Node -> ln_Succ, i++)
			{
				if(!Stricmp(Node -> ln_Name,Name))
				{
					Index = i;

					break;
				}
			}

			LT_New(Handle,
				LA_Type,	VERTICAL_KIND,
			TAG_DONE);
			{
				LT_New(Handle,
					LA_Type,	VERTICAL_KIND,
//					LA_LabelID,	MSG_V36_1269,
				TAG_DONE);
				{
					LONG MaxWidth,MaxHeight;

					MaxWidth = MaxHeight = 0;

					if(FileList)
					{
						struct Node *Node;
						LONG Len;

						for(Node = FileList -> lh_Head ; Node -> ln_Succ ; Node = Node -> ln_Succ)
						{
							Len = strlen(Node -> ln_Name);

							if(Len > MaxWidth)
								MaxWidth = Len;

							MaxHeight++;
						}
					}

					if(MaxWidth < 30)
						MaxWidth = 30;

					if(MaxHeight < 10)
						MaxHeight = 10;
					else
					{
						if(MaxHeight > 20)
							MaxHeight = 20;
					}

					LT_New(Handle,
						LA_Type,	LISTVIEW_KIND,
/*						LA_LabelID,	MSG_TERMPICKFILE_FILE_LIST_GAD,*/
						LA_Chars,	30,
						LA_ID,		GAD_LIST,
						LA_Lines,	10,
						LALV_MaxGrowX,	MaxWidth,
						LALV_MaxGrowY,	MaxHeight,
						LALV_ResizeY,	TRUE,
						GTLV_Labels,	FileList,
						GTLV_Selected,	Index,
						LALV_Link,	NIL_LINK,
						LALV_CursorKey,	TRUE,
					TAG_DONE);

					LT_EndGroup(Handle);
				}

				LT_New(Handle,
					LA_Type,VERTICAL_KIND,
				TAG_DONE);
				{
					LT_New(Handle,
						LA_Type,	XBAR_KIND,
						LAXB_FullSize,	TRUE,
					TAG_DONE);

					LT_EndGroup(Handle);
				}

				LT_New(Handle,LA_Type,HORIZONTAL_KIND,
					LAGR_SameSize,	TRUE,
					LAGR_Spread,	TRUE,
				TAG_DONE);
				{
					LT_New(Handle,
						LA_Type,	BUTTON_KIND,
						LA_LabelID,	MSG_GLOBAL_USE_GAD,
						LA_ID,		GAD_USE,
						LABT_ReturnKey,	TRUE,
						LABT_ExtraFat,	TRUE,
					TAG_DONE);

					LT_New(Handle,
						LA_Type,	BUTTON_KIND,
						LA_LabelID,	MSG_TERMPICKFILE_SELECT_GAD,
						LA_ID,		GAD_SELECT,
						LABT_ExtraFat,	TRUE,
					TAG_DONE);

					LT_New(Handle,
						LA_Type,	BUTTON_KIND,
						LA_LabelID,	MSG_GLOBAL_CANCEL_GAD,
						LA_ID,		GAD_CANCEL,
						LABT_EscKey,	TRUE,
						LABT_ExtraFat,	TRUE,
					TAG_DONE);

					LT_EndGroup(Handle);
				}

				LT_EndGroup(Handle);
			}

			if(PanelWindow = LT_Build(Handle,
				LAWN_TitleText,		Prompt,
				LAWN_IDCMP,		IDCMP_CLOSEWINDOW,
				LAWN_HelpHook,		&GuideHook,
				LAWN_Parent,		Window,
				WA_DepthGadget,		TRUE,
				WA_CloseGadget,		TRUE,
				WA_DragBar,		TRUE,
				WA_RMBTrap,		TRUE,
				WA_Activate,		TRUE,
				WA_SimpleRefresh,	TRUE,
			TAG_DONE))
			{
				struct IntuiMessage	*Message;
				BOOLEAN			 Done = FALSE;
				ULONG			 MsgClass;
				UWORD			 MsgCode;
				struct Gadget		*MsgGadget;

				LT_ShowWindow(Handle,TRUE);

				PushWindow(PanelWindow);

				do
				{
					if(Wait(PORTMASK(PanelWindow -> UserPort) | SIG_BREAK) & SIG_BREAK)
						break;

					while(Message = (struct IntuiMessage *)LT_GetIMsg(Handle))
					{
						MsgClass	= Message -> Class;
						MsgCode		= Message -> Code;
						MsgGadget	= (struct Gadget *)Message -> IAddress;

						LT_ReplyIMsg(Message);

						if(MsgClass == IDCMP_CLOSEWINDOW)
							Done = TRUE;

						if(MsgClass == IDCMP_GADGETUP)
						{
							switch(MsgGadget -> GadgetID)
							{
								case GAD_USE:

									if(Index != (ULONG)~0)
									{
										struct Node *Node = GetListNode(Index,FileList);

										if(Node)
										{
											strcpy(Name,Node -> ln_Name);

											Result = TRUE;
										}
									}

									Done = TRUE;
									break;

								case GAD_CANCEL:

									Done = TRUE;
									break;

								case GAD_LIST:

									Index = MsgCode;
									break;

								case GAD_SELECT:

									SplitFileName(Name,&DummyChar,DummyBuffer);

									if(FileRequest = GetFile(PanelWindow,Prompt,DummyBuffer,DummyChar,DummyBuffer,Pattern,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_SELECT_TXT),TRUE))
									{
										STRPTR FileName;

										if(FileRequest -> fr_NumArgs > 1 && FileRequest -> fr_ArgList)
											FileName = FileRequest -> fr_ArgList -> wa_Name;
										else
											FileName = FileRequest -> fr_File;

										if(!Stricmp(FileRequest -> fr_Drawer,Directory))
											strcpy(Name,FileName);
										else
											strcpy(Name,DummyBuffer);

										if(Type != -1)
										{
											UBYTE LocalBuffer[MAX_FILENAME_LENGTH];

											if(!Stricmp(FileRequest -> fr_Drawer,Directory))
											{
												strcpy(LocalBuffer,Directory);

												if(AddPart(LocalBuffer,Name,MAX_FILENAME_LENGTH))
													ValidateFile(LocalBuffer,Type,Name);
											}
											else
											{
												if(ValidateFile(DummyBuffer,Type,LocalBuffer))
												{
													strcpy(Name,DummyBuffer);

													DummyChar = PathPart(Name);

													*DummyChar = 0;

													AddPart(Name,LocalBuffer,MAX_FILENAME_LENGTH);
												}
											}
										}

										FreeAslRequest(FileRequest);

										Done = Result = TRUE;
									}

									break;
							}
						}

						if(MsgClass == IDCMP_IDCMPUPDATE)
						{
							struct Node *Node = GetListNode(Index = MsgCode,FileList);

							if(Node)
							{
								strcpy(Name,Node -> ln_Name);

								Done = Result = TRUE;

								LT_PressButton(Handle,GAD_USE);
							}
						}
					}
				}
				while(!Done);

				PopWindow();
			}

			LT_DeleteHandle(Handle);
		}

		DeleteList(FileList);
	}
	else
	{
		SplitFileName(Name,&DummyChar,DummyBuffer);

		if(FileRequest = GetFile(Window,Prompt,DummyBuffer,DummyChar,DummyBuffer,Pattern,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_SELECT_TXT),TRUE))
		{
			STRPTR FileName;

			if(FileRequest -> fr_NumArgs > 1 && FileRequest -> fr_ArgList)
				FileName = FileRequest -> fr_ArgList -> wa_Name;
			else
				FileName = FileRequest -> fr_File;

			if(!Stricmp(FileRequest -> fr_Drawer,Directory))
				strcpy(Name,FileName);
			else
				strcpy(Name,DummyBuffer);

			if(Type != -1)
			{
				UBYTE LocalBuffer[MAX_FILENAME_LENGTH];

				if(!Stricmp(FileRequest -> fr_Drawer,Directory))
				{
					strcpy(LocalBuffer,Directory);

					if(AddPart(LocalBuffer,Name,MAX_FILENAME_LENGTH))
						ValidateFile(LocalBuffer,Type,Name);
				}
				else
				{
					if(ValidateFile(DummyBuffer,Type,LocalBuffer))
					{
						strcpy(Name,DummyBuffer);

						DummyChar = PathPart(Name);

						*DummyChar = 0;

						AddPart(Name,LocalBuffer,MAX_FILENAME_LENGTH);
					}
				}
			}

			FreeAslRequest(FileRequest);

			return(TRUE);
		}
	}

	return(Result);
}
