//	CFolderCpm.c

#include "IC_Errors.h"
#include "CDesktop.h"
#include "CFileCpm.h"
#include "CDiskCpm.h"
#include "CFolderCpm.h"

OSErr		CFolderCpm::IFolderCpm(
	CDiskCpm		*cDiskCpm
) {
	OSErr				err = noErr;
	Cpm_BlockNum		block = Cpm_kDirStartBlock;
	Cpm_EntryIndex		directoryIndex = 0;
//	Boolean				skipB = FALSE;
	
	err = _inherited::IFolder(
		cDiskCpm, NULL, *(DiskLocSpecUnion *)&block, 
		directoryIndex, directoryIndex);

	if (!err) err = Cpm_ForEachEntry(Cpm_MakeNewEntryCB, NULL);

	return err;
}

void			CFolderCpm::Dispose(void)
{
	_inherited::Dispose();
}

OSErr			CFolderCpm::Cpm_ForEachEntry(
	Cpm_ForEachEntryCB	Cpm_ForEachUserCB, 
	void				*data)
{
	OSErr				err				= noErr;
	Cpm_DirEntry		*directoryP		= GetEntry(0);
	Cpm_EntryIndex		directoryIndex	= 0;
	Boolean				doneB			= FALSE;

	if (directoryP) for (
		directoryIndex = 0; 
		!doneB && !err && directoryIndex < Cpm_kMaxUseableDirEntries; 
		directoryIndex++
	) {		
		err = (*Cpm_ForEachUserCB)(
			this, &directoryP[directoryIndex], Cpm_kDirStartBlock, 
			directoryIndex, &doneB, data);
	}

	return err;
}

/*
	note on checking (entryP->userNumber == 31 && entryP->extent[0] == 128):
			
	from http://home.tiscali.se/pausch/apple2/Apple.CPM.ref.txt

	SoftCard CP/M ver 2.23 and higher uses a trick to allow the system
	tracks for data storage: a file called cp/m.sys is created in user
	area 31 as a dummy file allocated to the system tracks.  It is
	inaccessible from the CCP and unseen by the user.  The BIOS is
	written to recognize the system tracks as accessible data areas.
	COPY.COM has an option to create a "data diskette" where cp/m.sys is
	absent, which creates 3 more tracks for data storage.  Such a
	diskette cannot be warm booted, bit it is safe to use it in any other
	drive than A:		
*/

//	static
OSErr	CFolderCpm::Cpm_MakeNewEntryCB(
	CFolderCpm			*thiz, 
	Cpm_DirEntry		*entryP, 
	Cpm_BlockNum		blockNum, 
	Cpm_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data)
{
	OSErr				err			= noErr;
	Boolean				skipB;
	
	skipB = 
		entryP->userNumber == Cpm_kErasedSentinel
		|| entryP->extentNum > 0
		|| entryP->numRecords == 0
		|| GetRboShort(entryP->system) != 0
		|| (entryP->userNumber == 31 && entryP->extent[0] == 128);	/* see note above */
	
	if (!skipB) {
		err	= thiz->Cpm_MakeNewEntry(entryP, blockNum, entryIndex, entryIndex + 1, NULL);
	}
	
	return err;
}

OSErr			CFolderCpm::Cpm_MakeNewEntry(
	Cpm_DirEntry		*entry, 
	Cpm_BlockNum		blockNum, 
	Cpm_EntryIndex		entryIndex, 	//	within cur sector
	Cpm_EntryIndex		directoryIndex, //	within entire catalog
	CEntry				**newEntry0)
{
	OSErr		err = noErr;
	CFileCpm	*cFile;
	
	if (NewObject(cFile, CFileCpm, err)) {
	
		err = cFile->IFileCpm(
			i_cDisk.cpm, this, blockNum, 
			entryIndex, directoryIndex);

		if (err) {
			cFile->Dispose();
		} else {
			if (newEntry0) {
				*newEntry0 = cFile;
			}
		}
	}
	
	return err;
}

Cpm_DirEntry	*CFolderCpm::GetEntry(Cpm_EntryIndex entryIndex)
{
	return &((Cpm_DirEntry *)i_cDisk.cpm->GetBlock(Cpm_kDirStartBlock))[entryIndex];
}

static	OSErr	Cpm_GetEmptyEntryCB(
	CFolderCpm			*thiz, 
	Cpm_DirEntry		*entryP, 
	Cpm_BlockNum		blockNum, 
	Cpm_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data
) {
	Cpm_EntryIndex		*entryIndexP = (Cpm_EntryIndex *)data;

	if (entryP->userNumber == Cpm_kErasedSentinel) {
		*entryIndexP	= entryIndex;
		*done			= TRUE;
	}

	return noErr;
}

Boolean			CFolderCpm::GetEmptyEntry(Cpm_EntryIndex *entryIndexP)
{
	OSErr		err = noErr;
	
	*entryIndexP = 0;
	
	err	= Cpm_ForEachEntry(Cpm_GetEmptyEntryCB, entryIndexP);
	
	return err == noErr;
}

static	OSErr	Cpm_GetUniqueNameCB(
	CFolderCpm			*thiz, 
	Cpm_DirEntry		*entryP, 
	Cpm_BlockNum		blockNum, 
	Cpm_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data
) {
	if (
		entryP->userNumber != Cpm_kErasedSentinel
		&& entryP->extentNum != 0
	) {
		Gen_GetUniqueNameRec	*nameRec = (Gen_GetUniqueNameRec *)data;
		Cpm_FileNameStr			entryName;
		Cpm_FileTypeStr			entryType;
		Cpm_FileNameAndTypeStr	nameTypeAC;
		
		Cpm_GetName(entryP, entryName);
		Cpm_GetDescription(entryP, entryType);
		sprintf(nameTypeAC, "%s.%s", entryName, entryType);
		
		if (strcmp(nameTypeAC, nameRec->name) == 0) {
			nameRec->unique	= FALSE;
			*done			= TRUE;
		}
	}

	return noErr;
}

static	void		Cpm_BreakUpName(
	Cpm_FileNameAndTypeStr	typeNameZ, 
	Cpm_FileNameStr			newNameZ, 
	Cpm_FileNameStr			newTypeZ
) {
	char		*dotP;
	short		strLenS;
	
	dotP = strrchr(typeNameZ, '.');
	
	//	no dot? you're binary buddy!
	
	if (dotP == NULL) {
		strcpy(newTypeZ, "BIN");
	} else {
		strcpy(newTypeZ, &dotP[1]);
		strLenS	= strlen(newTypeZ);
		ASSERT(strLenS > 0 && strLenS <= Cpm_kTypeLength);

		*dotP = 0;
	}
	
	strcpy(newNameZ, typeNameZ);
	strLenS	= strlen(typeNameZ);
	ASSERT(strLenS > 0 && strLenS <= Cpm_kNameLength);
}

Boolean			CFolderCpm::GetUniqueName(char *fileName)
{
	OSErr					err		= noErr;
	Boolean					done	= FALSE;
	Gen_GetUniqueNameRec	nameRec;
	Cpm_FileNameAndTypeStr	nameTypeAC;
	Cpm_FileNameStr			nameAC;
	Cpm_FileTypeStr			typeAC;
	
	Cpm_BreakUpName(fileName, nameAC, typeAC);
	Cpm_SanitizeName(nameAC);
	Cpm_SanitizeName(typeAC);
	
	sprintf(fileName, "%s.%s", nameAC, typeAC);
	
	strcpy(nameTypeAC, fileName);
	nameRec.name	= nameTypeAC;
	nameRec.unique	= TRUE;
	
	err	= Cpm_ForEachEntry(Cpm_GetUniqueNameCB, &nameRec);
	
	if (err) {
		nameRec.unique	= FALSE;
	} else if (!nameRec.unique) {
		short					loop;
		
		nameAC[Cpm_kNameLength - 3] = 0;
		
		for (loop = 1; !done && loop <= 99; loop++) {
			nameRec.unique	= TRUE;
			sprintf(nameTypeAC, "%s_%.2d.%s", nameAC, loop, typeAC);
			err	= Cpm_ForEachEntry(Cpm_GetUniqueNameCB, &nameRec);

			if (err) {
				done			= TRUE;
				nameRec.unique	= FALSE;
			} else if (nameRec.unique) {
				done = TRUE;
				strcpy(fileName, nameTypeAC);
			}
		}
	}
	
	return nameRec.unique;
}

OSErr			CFolderCpm::NewFile(Boolean isFolderB, CEntry **entryH)
{
	OSErr					err = noErr;
	O_CTopic				*topic		= NULL;
	CEntry					*newEntry	= NULL;
	Cpm_EntryIndex			entryIndex;	
	Cpm_DirEntry			*entryP;
	Cpm_FileNameStr			nameAC;
	Cpm_FileTypeStr			typeAC;
	Cpm_FileNameAndTypeStr	typeNameAC;
	Cpm_FileNameAndTypeStr	fileNameAC;
	Cpm_AccessBits			cpmBits = { 0 };
	
	*entryH = NULL;
	
	if (isFolderB) {
		err = IC_Err_NOT_REALLY_A_FOLDER;
		goto new_file_done;
	}

	strcpy(fileNameAC, "FILE.BIN");
	if (!GetUniqueName(fileNameAC)) {
		ReportError(err = IC_Err_FILE_ALREADY_EXISTS);
		goto new_file_done;
	}

	if (!GetEmptyEntry(&entryIndex)) {
		ReportError(err = IC_Err_NO_DIR_ENTRY);
		goto new_file_done;
	}
	
	entryP = GetEntry(entryIndex);

	if (entryP == NULL) {
		ReportError(err = IC_Err_READ_ILLEGAL_TRACK_SECTOR);
		goto new_file_done;
	}
	
	entryP->userNumber = Cpm_kUserNumber;
	Cpm_BreakUpName(fileNameAC, nameAC, typeAC);
	Cpm_GetTypeName(nameAC, typeAC, &cpmBits, typeNameAC);
	memcpy(entryP->name, typeNameAC, Cpm_kNameAndTypeLength);
	entryP->extentNum = 0;
	entryP->system = SetRboShort(0);
	entryP->numRecords = 0;
	entryP->extent[0] = Cpm_kErasedSentinel;

	err = i_cDisk.cpm->SetBlock();
	if (err) goto new_file_error;
	
	if (GetParentFolder()) {
		topic = i_topicRef.cTopic;
	} else {
		topic = i_cDisk.cpm->i_topicRef.cTopic;
	}
	
	err = topic->O_SetRecent();
	if (err) goto new_file_error;
	
	newEntry = GetTopicEntry(topic);
	if (!newEntry) goto new_file_error;
	
	newEntry->i_addTopicIndex = entryIndex - 1;
	newEntry = NULL;

	err = Cpm_MakeNewEntry(
		entryP, Cpm_kDirStartBlock, 
		entryIndex, entryIndex + 1, &newEntry);

	if (err) goto new_file_error;
	
	goto new_file_done;

	//	if error, try to back out, ignore any other errors
	new_file_error:
	newEntry = NULL;

	entryP->userNumber = Cpm_kErasedSentinel;
	(void)i_cDisk.cpm->SetBlock();

	new_file_done:
	*entryH = newEntry;
	
	return err;
}

OSErr		CFolderCpm::Cpm_DeleteEntry(Cpm_EntryIndex entryIndex)
{
	OSErr				err = noErr;

	err = i_cDisk.cpm->FlushMemDisk(FALSE);
	
	if (!err) {
		OSErr				err2;
//		Boolean				doneB = FALSE;
		Cpm_DirEntry		*entryP;

		entryP = GetEntry(entryIndex);
		if (entryP == NULL) err = IC_Err_ENTRY_NOT_FOUND;

		if (entryP->userNumber == Cpm_kErasedSentinel) {
			err = IC_Err_FILE_ALREADY_DELETED;
			ReportError(err);
		}

		if (!err) {
			Cpm_DirEntry	*nextEntryP, origEntry	= *entryP;
			Boolean			doneB					= FALSE;
			
			do {
				//	mark entry as deleted
				entryP->userNumber = Cpm_kErasedSentinel;
				
				nextEntryP	= &(entryP[1]);
				doneB = !Cpm_MatchName(&origEntry, nextEntryP);
				
				if (!doneB) {
					entryP = nextEntryP;
				}
			} while (!doneB);
		}
		
		err2 = i_cDisk.cpm->FlushMemDisk(TRUE);
		if (!err) err = err2;
	}
	
	UnCacheFolderSizes();

	return err;
}

OSErr			CFolderCpm::Delete(Boolean warnB, CDialogCopy *copyP0)
{
	return DeleteFolderContents(warnB, copyP0);
}

OSErr			CFolderCpm::UnDelete(Boolean recursiveB, CDialogCopy *copyP0)
{
//	OSErr	err = noErr;
	
	return noErr;
}
