//	ProFileTypes.c

#include "ADFS_Menus.h"
#include "IC_Errors.h"
#include "MemUtils.h"
#include "CDialogCopy.h"
#include "FSUtils.h"
#include "CCopyFile_FsSpec.h"
#include "ADFS_LogFile.h"
#include "Utils.h"
#include "ProStructs.h"
#include "ProFileTypes.h"
#include <stdio.h>
#include <string.h>

long				gMaxTypes;
long				gCurMaxTypes;
Pro_FileTypeRec		*gFileTypeArray;
Pro_FileTypeRec		**gFileTypeH;

OSErr				Pro_LoadFileTypes(Boolean appFileTypes, FSSpec *fileSpecP);
void				InsertSorted(Pro_FileTypeRec *typeRec);

Boolean		Pro_InitFileTypes(void)
{
	OSErr		err = noErr;
	
	err = Pro_LoadFileTypes(TRUE, NULL);
	if (err) {
		ReportErrorStr(err, "Error Loading 'ftyp' resource.");
		return FALSE;
	}
	
	if (!err) {
		ulong				numObjects, curIndex;
		FSSpec				folderFSSpec, *curFsSpecP;
		FInfo				info;
		CCopyFile_FsSpec	*fsSpecIterP;
		
		err = FSpThisApp(&folderFSSpec);

		//	return immediately, since error is already reported
		if (err) return FALSE;
		
		if (!err) {
			err = FSMakeFSSpec(
				folderFSSpec.vRefNum, 
				folderFSSpec.parID, 
				"\pFTypes", &folderFSSpec);
			
			//	no problem if we don't even find the folder
			if (err == fnfErr) return TRUE;
		}

		if (err) {
			ReportErrorStr(err, "Error making FSSpec.");
			return FALSE;
		}

		fsSpecIterP = new (CCopyFile_FsSpec);
		if (fsSpecIterP == NULL) {
			ReportError(err = IC_Err_OUT_OF_MEMORY);
			return FALSE;
		}
		
		err = fsSpecIterP->ICopyFile_FsSpec(NULL, (Ptr)&folderFSSpec);
		if (err) {
			ReportErrorStr(err, "Error making FSSpec Folder Iterator.");
			return FALSE;
		}
		
		err = fsSpecIterP->CountFilesInFolder(&numObjects);
		if (err) {
			ReportErrorStr(err, "Error counting folder objects.");
			goto errorDone;
		}

		if (!err) for (
			curIndex = 0;
			!err && curIndex < numObjects;
			curIndex++
		) {
			err = fsSpecIterP->GetIndFileInFolder(curIndex, &(Ptr)curFsSpecP);
			if (err == IC_Err_INVISIBLE_FILE) {
				err = noErr;
			} else {
				if (err) {
					ReportErrorStr((short)numObjects, "Error getting indexed file");
					goto errorDone;
				}
				
				if (!err) err = FSpGetCatFInfo(curFsSpecP, &info);
				
				if (err) {
					char	errStr[256];
					
					Concat("\pError getting file info for .", curFsSpecP->name, (uchar *)errStr);
					CopyPascalStringToC((uchar *)errStr, errStr);
					ReportErrorStr((short)numObjects, errStr);
					goto errorDone;
				}

				if (!err) {
					if ((info.fdType & 0xFFFF0000) == 0x70420000) {
						err = Pro_LoadFileTypes(FALSE, curFsSpecP);

						if (err) {
							ReportErrorStr(err, "Error loading FType.xxx file.");
							goto errorDone;
						}
					}
				}
			}
		}
		
		errorDone:
		fsSpecIterP->Dispose();
	}
	
	if (err) {
		ReportErrorStr(err, "Some other file type error");	//Error initting ProDOS file types.");
	} else {
		Pro_BuildFileTypeMenu();
	}

	return err == noErr;
}

OSErr		Pro_LoadFileTypes(Boolean appFileTypesB, FSSpec *fileSpecP)
{
	OSErr				err		= noErr;
	short				refNum	= 0;
	Handle				ftypeH	= NULL;
	char				*arrayP	= NULL;
	Pro_FTypesHeader	*ftypeHeaderP;
	ushort				maxTypes;
	
	if (appFileTypesB) {
		ftypeH = GetResource('ftyp', 128);
		err = ResError();
		
		if (!err) {
			HLockHi(ftypeH);
			arrayP = *ftypeH;

			ftypeHeaderP	= (Pro_FTypesHeader *)arrayP;
			maxTypes		= GetRboShort(ftypeHeaderP->numEntries);

			gFileTypeH = (Pro_FileTypeRec **)TrackNewHandle("file types array", sizeof(Pro_FileTypeRec) * (long)maxTypes);
			err = MemError();
			
			if (!err) {
				HLock((Handle)gFileTypeH);
				err = MemError();
			}
			
			if (!err) {
				gMaxTypes		= maxTypes;
				gCurMaxTypes	= maxTypes;
				gFileTypeArray	= *gFileTypeH;
			}
		}
	} else {
		long		eof, bytesRead;
		
		err = FSpOpenDF(fileSpecP, fsRdPerm, &refNum);
		if (!err) {
			OSErr		err2 = noErr;
			
			err = GetEOF(refNum, &eof);

			if (!err) {
				arrayP		= TrackNewPtr("FSType buf", eof);
				err			= MemError();
			}
			
			if (!err) {
				bytesRead = eof;
				err = FSRead(refNum, &bytesRead, arrayP);
				if (err) {
					if (bytesRead < eof) {
						err = eofErr;
					}
				}
			}
			
			err2 = FSClose(refNum);
			if (!err) err = err2;

			if (!err) {
				HUnlock((Handle)gFileTypeH);
				err = MemError();
			}
			
			if (!err) {
				ftypeHeaderP	= (Pro_FTypesHeader *)arrayP;
				maxTypes		= GetRboShort(ftypeHeaderP->numEntries);

				SetHandleSize((Handle)gFileTypeH, 
					sizeof(Pro_FileTypeRec) * ((long)gMaxTypes + (long)maxTypes));

				err = MemError();
			}
			
			if (!err) {
				HLock((Handle)gFileTypeH);
				err = MemError();
			}
				
			if (!err) {
				gMaxTypes		+= maxTypes;
				gFileTypeArray	= *gFileTypeH;
			}
		}
	}
	
	if (!err) {
		Pro_FTypeEntry		*ftypeEntryP;
		ushort				curEntry;
		Pro_FileTypeRec		curRec;
		uchar				*pstrP;
						
		for (
			curEntry = 0, ftypeEntryP = (Pro_FTypeEntry *)&(arrayP[GetRboShort(ftypeHeaderP->offsetToIndex)]);
			curEntry < maxTypes;
			curEntry++, ftypeEntryP++
		) {
			curRec.fileType	= GetRboShort(ftypeEntryP->fileType);
			curRec.auxType	= (ushort)GetRboLong(ftypeEntryP->auxType);
			
			pstrP = (uchar *)&(arrayP[GetRboShort(ftypeEntryP->offsetToEntry)]);
			
			//	fix bug in corrupted "FType.GEOS" file
			if (
				curRec.fileType == 0x8F
				&& curRec.auxType == 0
				&& pstrP[0] == 69
			) {
				pstrP = (uchar *)&(arrayP[GetRboShort(ftypeEntryP->offsetToEntry) - 2]);
			}
				
			CopyPascalStringToC(pstrP, curRec.descStr);
				
			if (curRec.fileType == Pro_FileType_BINA) {
				strcat(curRec.descStr, " (Binary)");
			}
			
			if (appFileTypesB) {
				gFileTypeArray[curEntry] = curRec;
			} else {
				InsertSorted(&curRec);
			}
		}
	}

	if (appFileTypesB) {
		if (ftypeH) {
			HUnlock(ftypeH);
			ReleaseResource(ftypeH);
		}
	} else {
		if (arrayP) {
			TrackDisposePtr(arrayP);
		}
	}
	
	return err;
}

void		InsertSorted(Pro_FileTypeRec *typeRec)
{
	ushort				curEntry;
	ulong				insType, curType;
	
	insType = (((ulong)typeRec->fileType) << 16) | ((ulong)typeRec->auxType);
	
	for (
		curEntry = 0;
		curEntry < gCurMaxTypes;
		curEntry++
	) {
		curType = (((ulong)gFileTypeArray[curEntry].fileType) << 16) | ((ulong)gFileTypeArray[curEntry].auxType);
	
		if (insType > curType) {
			// nothing, just move on
		} else if (insType == curType) {
			//	collision, we're done
			gMaxTypes--;
			return;
		} else {
			//	we've found where we should be inserted
			
			memmove(
				&gFileTypeArray[curEntry + 1], 
				&gFileTypeArray[curEntry], 
				sizeof(Pro_FileTypeRec) * (gCurMaxTypes - curEntry));
			
			break;
		}
	}

	gCurMaxTypes++;
	gFileTypeArray[curEntry] = *typeRec;
}

Boolean		Pro_GetFileType(Pro_FileTypeRec *typeRec)
{
	long		curType;
	Boolean		done, found = FALSE;
	
	for (done = FALSE, curType = 0; !done && curType < gMaxTypes; curType++) {
		if (gFileTypeArray[curType].fileType == typeRec->fileType) {
			done	= TRUE;
			found	= TRUE;
			
			strcpy(typeRec->descStr, gFileTypeArray[curType].descStr);
			
			while (gFileTypeArray[++curType].fileType == typeRec->fileType) {
				if (typeRec->auxType == gFileTypeArray[curType].auxType) {
					strcpy(typeRec->descStr, gFileTypeArray[curType].descStr);
				}
			}
		}
	}
	
	return found;
}

MenuRef		g_proMenu = NULL;

MenuRef		Pro_BuildFileTypeMenu(void)
{
	if (!g_proMenu) {
		Str255				theStr;
		char				*theCStr = (char *)&theStr, str2[256];
		char				recentZ[256];
		long				curType;
		CDialogCopy			*copyDialogP = ShowCopyDialog();
		Boolean				updateNextB = TRUE;
		
		ADFS_Log("Building ProDOS File Types menu\n");
		
		g_proMenu = FindMenu(206);
		
		HNoPurge((Handle)g_proMenu);

		copyDialogP->SetTitle("Get ProDOS File Types");

		copyDialogP->SetAsProgress(TRUE, -gMaxTypes);
		copyDialogP->SetDelayTicks(120);	//	2 seconds

		ClearMenu(g_proMenu);

		for (curType = 0x00; curType < gMaxTypes; curType++) {
			if (gFileTypeArray[curType].auxType == 0) {
				sprintf(str2, "0x%.2hX", (int)gFileTypeArray[curType].fileType);
				sprintf(theCStr, "%s %s", str2, gFileTypeArray[curType].descStr);
				strcpy(recentZ, theCStr);
			} else {
				sprintf(str2, "0x%.4hX", (int)gFileTypeArray[curType].auxType);
				sprintf(theCStr, "ΚΚΚΚ%s %s", str2, gFileTypeArray[curType].descStr);
			}
			
			//	only update every 16
			if ((curType & 0x0F) == 0) {
				copyDialogP->i_itemRemainUL -= 15;
				copyDialogP->i_curBytesUL	+= 15;				
				copyDialogP->IncrementProgress(TRUE, recentZ);
			}

			CopyCStringToPascal(theCStr, (unsigned char *)theStr);
			AppendMenu(g_proMenu, "\p ");
			SetMenuItemText(g_proMenu, curType + 1, theStr);
		}

		copyDialogP->HideCopyDialog();
	}
	
	return g_proMenu;
}

void	Pro_MenuItemToFileAndAux(MenuHandle theMenu, short menuItem, short *fileType, ushort *auxType)
{
	Str255		itemText;
	char		*itemCText = (char *)&itemText;
	int			fileTypeInt;
	
	*fileType = sizeof(int);
	*auxType = 0;

	GetMenuItemText(theMenu, menuItem, itemText);

	if (itemText[1] != '0') {
		int		auxTypeInt = 0;
		
		CopyPascalStringToC(itemText, (char *)itemText);
		itemCText[10] = 0;
		sscanf(&(itemCText[4]), "%i", &auxTypeInt);
		*auxType = (ushort)auxTypeInt;
		
		while (itemText[1] != '0') {
			menuItem--;
			GetMenuItemText(theMenu, menuItem, itemText);
		}
	}

	CopyPascalStringToC(itemText, (char *)itemText);
	itemCText[4] = 0;
	sscanf(itemCText, "%i", &fileTypeInt);
	*fileType = (short)fileTypeInt;
}

short		Pro_FileTypeToIndex(Byte fileType, ushort auxType)
{
	short	foundType = 0;
	short	curType;
	
	for (curType = fileType; curType < gMaxTypes; curType++) {
		if (gFileTypeArray[curType].fileType == fileType) {
			foundType = curType;
			break;
		}
	}
	
	if (curType != gMaxTypes) {
		for (curType++; curType < gMaxTypes; curType++) {
			if (gFileTypeArray[curType].fileType == fileType) {
				if (gFileTypeArray[curType].auxType == auxType) {
					foundType = curType;
					break;
				}
			} else {
				break;
			}
		}
	}

	foundType++;
	
	return foundType;
}
