//	CFolderDos.c

#include "DosCatalog.h"
#include "IC_FileIO.h"
#include "IC_Errors.h"
#include "CDesktop.h"
#include "CFileDos.h"
#include "CDiskDos.h"
#include "CFolderDos.h"

OSErr		CFolderDos::IFolderDos(
	CDiskDos		*cDiskDos, 
	Dos_SectorSpec	sector
) {
	OSErr				err = noErr;
	Dos_EntryIndex		entryIndex	= 0;	//	relative to cur dir sector
	Dos_EntryIndex		dirIndex	= 0;	//	relative to entire directory
	
	err = _inherited::IFolder(
		cDiskDos, NULL, *(DiskLocSpecUnion *)&sector, 
		entryIndex, dirIndex);

	if (!err) {
		err = Dos_ForEachDirSector(Dos_CacheDirectory, NULL);
		if (!err) err = AddEntries(TRUE, FALSE);
	}	
		
	return err;
}

void			CFolderDos::Dispose(void)
{
	(void)Dos_ForEachDirSector(Dos_DisposeDirectory, NULL);
	_inherited::Dispose();
}

OSErr			CFolderDos::Dos_CacheDirectory(
	CFolderDos			*thiz, 
	Dos_SectorSpec		sectorSpec, 
	Dos_DirSector		*dirSectorP, 
	Boolean				*doneBP, 
	void				*data)
{	
	return thiz->i_cDisk.dos->GetSector(sectorSpec, (Dos_Sector **)&dirSectorP);
}

OSErr			CFolderDos::Dos_DisposeDirectory(
	CFolderDos			*thiz, 
	Dos_SectorSpec		sectorSpec, 
	Dos_DirSector		*dirSectorP, 
	Boolean				*doneBP, 
	void				*data)
{	
	return thiz->i_cDisk.dos->DisposeSector(sectorSpec);
}

OSErr			CFolderDos::Dos_ForEachDirSector(
	Dos_ForEachDirSectorCB	Dos_ForEachUserCB, 
	void					*data)
{
	OSErr				err = noErr, err2;
	Dos_VtocSector		*vtocP;

	vtocP = i_cDisk.dos->GetMyEntry();
	
	if (vtocP) {
//		Dos_EntryIndex		entryIndex	= 0;	//	relative to cur dir sector
		Boolean				doneB = FALSE;
		Dos_SectorSpec		sectorSpec = vtocP->dirSector, nextSectorSpec;
		Dos_DirSector		*dirSectorP;

		do {
			err = i_cDisk.dos->GetSector(sectorSpec, (Dos_Sector **)&dirSectorP);
			
			if (!err) {
				err = (*Dos_ForEachUserCB)(
					this, sectorSpec, dirSectorP, &doneB, data);
				
				nextSectorSpec = dirSectorP->nextDirSector;
				
				err2 = i_cDisk.dos->DisposeSector(sectorSpec);
				if (!err) err = err2;

				if (!err && !doneB) {
					sectorSpec = nextSectorSpec;
				}
			}
			
		} while (!err && !doneB && !Dos_IsNullSector(sectorSpec));
	}
	
	return err;
}

typedef struct {
	Dos_ForEachEntryCB	*Dos_ForEachUserCB;
	void				*data;
} Dos_EntryCBRec;

OSErr			CFolderDos::Dos_ForEachDirSectorEntry(
	CFolderDos			*thiz, 
	Dos_SectorSpec		sectorSpec, 
	Dos_DirSector		*dirSectorP, 
	Boolean				*doneBP, 
	void				*data)
{
	OSErr				err					= noErr;
	Dos_EntryIndex		entryIndexL			= 0;	//	relative to cur dir sector
	Dos_EntryCBRec		*entrySectorRecP	= (Dos_EntryCBRec *)data;
	Dos_DirEntry		*dirEntryP;

	for (
		entryIndexL = 0; 
		!err && !*doneBP && entryIndexL < Dos_kMaxEntriesPerSector; 
		entryIndexL++
	) {
		dirEntryP = &dirSectorP->entry[entryIndexL];
		
		err = (*entrySectorRecP->Dos_ForEachUserCB)(
			thiz, dirEntryP, sectorSpec, 
			entryIndexL, doneBP, entrySectorRecP->data);
	}
	
	return err;
}

OSErr			CFolderDos::Dos_ForEachEntry(
	Dos_ForEachEntryCB	Dos_ForEachUserCB, 
	void				*data)
{
	OSErr				err = noErr;
	Dos_EntryCBRec		entryRec;
	
	entryRec.Dos_ForEachUserCB	= Dos_ForEachUserCB;
	entryRec.data				= data;

	err = Dos_ForEachDirSector(
		Dos_ForEachDirSectorEntry, 
		&entryRec);

	return err;
}


Dos_DirEntry	*CFolderDos::GetEntry(Dos_SectorSpec sectorSpec, Dos_EntryIndex entryIndex)
{
	OSErr			err			= noErr;
	Dos_DirEntry	*dirEntryP	= NULL;
	Dos_DirSector	*dirSectorP;
	
	err = i_cDisk.dos->GetSector(sectorSpec, (Dos_Sector **)&dirSectorP);

	if (!err) {
		dirEntryP = &dirSectorP->entry[entryIndex];
	}

	return dirEntryP;	
}

Dos_SectorSpec		CFolderDos::GetNextDirSector(Dos_SectorSpec sectorSpec)
{
	Dos_SectorSpec	returnSpec	= Dos_gInvalidSector;
	Dos_DirSector	*dirSectorP;
	OSErr			err = noErr;

	err = i_cDisk.dos->GetSector(sectorSpec, (Dos_Sector **)&dirSectorP);

	if (!err) {
		returnSpec = dirSectorP->nextDirSector;
		(void)i_cDisk.dos->DisposeSector(sectorSpec);
	}

	return returnSpec;	
}


static	OSErr	Dos_GetEmptyEntryCB(
	CFolderDos			*thiz, 
	Dos_DirEntry		*entry, 
	Dos_SectorSpec		sectorSpec, 
	Dos_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data
) {
	Dos_EntryLocSpec	*entryLoc = (Dos_EntryLocSpec *)data;

	entryLoc->dirIndex++;
	
	if (Dos_IsEmptyEntry(entry)) {
		entryLoc->sectorSpec	= sectorSpec;
		entryLoc->entryIndex	= entryIndex;
		*done					= TRUE;
	}

	return noErr;
}

Boolean			CFolderDos::GetEmptyEntry(Dos_EntryLocSpec *entryLoc)
{
	OSErr		err = noErr;

	entryLoc->sectorSpec	= Dos_gEmptySector;
	entryLoc->entryIndex	= 0;
	entryLoc->dirIndex		= 0;
	
	err	= Dos_ForEachEntry(Dos_GetEmptyEntryCB, entryLoc);
	
	return err == noErr && !Dos_IsNullSector(entryLoc->sectorSpec);
}


static	OSErr	GetUniqueNameCB(
	CFolderDos			*thiz, 
	Dos_DirEntry		*entry, 
	Dos_SectorSpec		sectorSpec, 
	Dos_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data
) {
	if (!Dos_IsEmptyEntry(entry)) {
		Gen_GetUniqueNameRec	*nameRec = (Gen_GetUniqueNameRec *)data;
		Dos_FileNameStr			entryName;
		
		Dos_GetFileName(entry, entryName);

		if (strcmp(entryName, nameRec->name) == 0) {
			nameRec->unique	= FALSE;
			*done			= TRUE;
		}
	}

	return noErr;
}

Boolean			CFolderDos::GetUniqueName(char *fileName)
{
	OSErr					err		= noErr;
	Boolean					done	= FALSE;
	Dos_FileNameStr			newName;
	Gen_GetUniqueNameRec	nameRec;
	short					loop;
	
	nameRec.name	= newName;
	nameRec.unique	= TRUE;
	
	strcpy(newName, fileName);
	err	= Dos_ForEachEntry(GetUniqueNameCB, &nameRec);
	
	if (err) {
		nameRec.unique	= FALSE;
	} else if (!nameRec.unique) {
		for (loop = 1; !done && loop <= 99; loop++) {
			nameRec.unique	= TRUE;
			sprintf(newName, "%s %d", fileName, loop);
			err	= Dos_ForEachEntry(GetUniqueNameCB, &nameRec);

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

OSErr			CFolderDos::NewFile(Boolean isFolderB, CEntry **entryH)
{
	OSErr				err			= noErr;
	O_CTopic			*topic		= NULL;
	CEntry				*newEntry	= NULL;
	Dos_EntryLocSpec	entryLoc;
	Dos_SectorSpec		*sectorSpecP, owningSectorSpec;
	Dos_FileNameStr		fileName;
	Dos_DirEntry		*entryP;

	*entryH = NULL;
	
	if (isFolderB) {
		err = IC_Err_NOT_REALLY_A_FOLDER;
		goto new_file_done;
	}

	strcpy(fileName, "New File");
	if (!GetUniqueName(fileName)) {
		ReportError(err = IC_Err_FILE_ALREADY_EXISTS);
		goto new_file_done;
	}
	
	if (!GetEmptyEntry(&entryLoc)) {
		ReportError(err = IC_Err_NO_DIR_ENTRY);
		goto new_file_done;
	}
	
	entryP = GetEntry(entryLoc.sectorSpec, entryLoc.entryIndex);

	if (entryP == NULL) {
		ReportError(err = IC_Err_READ_ILLEGAL_TRACK_SECTOR);
		goto new_file_done;
	}
	
	//	deleted entries have 0xFF as their first track!
	entryP->firstTSSector	= Dos_gEmptySector;
	entryP->fileType		= Dos_FileType_TXT;
	Dos_SetFileName(entryP, fileName);
	entryP->size = SetRboShort(0);
		
	//	this call just allocates the first TSList and hangs it off the dir entry
	//	we don't actually need it right now
	if (!err) err = i_cDisk.dos->Dos_GetNextFreeTSSectorAlloc(
		entryP, &owningSectorSpec, &sectorSpecP, NULL);
	if (!err) err = i_cDisk.dos->DisposeSector(owningSectorSpec);
	if (err) goto new_file_error;

	if (GetParentFolder()) {
		topic = i_topicRef.cTopic;
	} else {
		topic = i_cDisk.dos->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 = entryLoc.dirIndex - 1;
	newEntry = NULL;
	
	err = Dos_MakeNewEntry(entryP, entryLoc.sectorSpec, 
		entryLoc.entryIndex, entryLoc.dirIndex, &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;

	(void)i_cDisk.dos->DisposeSector(entryLoc.sectorSpec);	//	block of entryP
	(void)i_cDisk.dos->FreeSector(entryP->firstTSSector);
	entryP->firstTSSector = Dos_gEmptySector;
	return err;

	new_file_done:
	*entryH = newEntry;
	err = i_cDisk.dos->SetAndDisposeSector(entryLoc.sectorSpec);	//	block of entryP
	
	return err;
}

OSErr			CFolderDos::AddEntries(Boolean normalB, Boolean deletedB)
{
	OSErr				err = noErr;
	Gen_MakeEntryData	data;
	
	data.dirIndexS	= 0;	//	relative to entire directory
	data.normalB	= normalB;
	data.deletedB	= deletedB;
	err = Dos_ForEachEntry(Dos_MakeNewEntryCB, &data);
	
	if (!err) err = _inherited::AddEntries(normalB, deletedB);
	
	return err;
}

//	static
OSErr	CFolderDos::Dos_MakeNewEntryCB(
	CFolderDos			*thiz, 
	Dos_DirEntry		*entry, 
	Dos_SectorSpec		sectorSpec, 
	Dos_EntryIndex		entryIndex, 
	Boolean				*done, 
	void				*data)
{
	OSErr				err			= noErr;
	Gen_MakeEntryData	*dataP		= (Gen_MakeEntryData *)data;
	
	(dataP->dirIndexS)++;	

	//	not deleted
	if (!Dos_IsNullSector(entry->firstTSSector)) {
		Boolean		deletedB	= Dos_IsDeleted(entry);
	
		if (
			(!deletedB && dataP->normalB)
			|| (deletedB && dataP->deletedB)
		) {
			err	= thiz->Dos_MakeNewEntry(
				entry, sectorSpec, entryIndex, dataP->dirIndexS, NULL);
		}
	}
	
	return err;
}

OSErr			CFolderDos::Dos_MakeNewEntry(
	Dos_DirEntry		*entry, 
	Dos_SectorSpec		sectorSpec, 
	Dos_EntryIndex		entryIndex, 	//	within cur sector
	Dos_EntryIndex		directoryIndex, //	within entire catalog
	CEntry				**newEntry0)
{
	OSErr		err = noErr;
	CFileDos	*cFile;
	
	if (NewObject(cFile, CFileDos, err)) {

		err = i_cDisk.dos->i_topicRef.cTopic->O_SetRecent();

		if (!err) err = cFile->IFileDos(
			i_cDisk.dos, this, sectorSpec, entryIndex, directoryIndex);

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

void			CFolderDos::ConformStrLen(char *nameStr)
{
	short	lengthS = strlen(nameStr);
	
	if (lengthS > Dos_kNameLength) {
		nameStr[lengthS] = Dos_kNameLength;
	}
}

OSErr		CFolderDos::Dos_DeleteEntry(Dos_EntryLocSpec entryLoc)
{
	OSErr				err = noErr, err2;
	Dos_DirEntry		*entryP;
	Dos_SectorSpec		tsSectorSpec, nextSectorSpec;
	Dos_TSListSector	*tsListP;
	Boolean				doneB = FALSE;

	i_cDisk.dos->FlushMemDisk(FALSE);
	
	entryP = GetEntry(entryLoc.sectorSpec, entryLoc.entryIndex);
	if (!entryP) {
		ReportError(err = IC_Err_ENTRY_NOT_FOUND);
	} else {
		tsSectorSpec	= entryP->firstTSSector;

		if (Dos_IsEmptyEntry(entryP)) {
			ReportError(err = IC_Err_FILE_ALREADY_DELETED);
		}
	}

	if (!err) {
		//	mark entry as deleted
		entryP->name[Dos_kNameLength - 1] = entryP->firstTSSector.track;
		entryP->firstTSSector.track = 0xFF;
	}
		
	//	get the TS list
	if (!err) {
		
		//	get the TS list
		err = i_cDisk.dos->GetSector(tsSectorSpec, (Dos_Sector **)&tsListP);
		
		//	free bitmap
		if (!err) do {
			ushort	loop;
			
			for (loop = 0; !err && !doneB && loop < Dos_kMaxSectorSpecs; loop++) {
				if (!Dos_IsNullSector(tsListP->fileSectors[loop])) {
					err = i_cDisk.dos->FreeSector(tsListP->fileSectors[loop]);
				}
			}
			
			if (!err) err = i_cDisk.dos->FreeSector(tsSectorSpec);
			
			nextSectorSpec = tsListP->nextTsSector;
			err2 = i_cDisk.dos->DisposeSector(tsSectorSpec);
			if (!err) err = err2;
			
			if (!err) {
				tsSectorSpec = nextSectorSpec;
				doneB = Dos_IsNullSector(tsSectorSpec);
				
				if (!doneB) {
					err = i_cDisk.dos->GetSector(tsSectorSpec, (Dos_Sector **)&tsListP);
				}
			}
		} while (!err && !doneB);
	}		
	
	if (entryP) {
		i_cDisk.dos->SetAndDisposeSector(entryLoc.sectorSpec);
	}
	
	UnCacheFolderSizes();

	i_cDisk.dos->FlushMemDisk(TRUE);
	
	return err;
}

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

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

