//	CFolder.c

#include "ADFS_O_Callbacks.h"
#include "Utils.h"
#include "FSUtils.h"
#include "ProFileTypes.h"
#include "CDialogDeleteFile.h"
#include "Pro_Utils.h"
#include "ProStructs.h"
#include "ADFS_Icons.h"
#include "CDisk.h"
#include "CFile.h"
#include "CFolder.h"

OSErr		CFolder::IFolder(
	CDisk				*cDisk, 
	CFolder				*cParentFolder, 
	DiskLocSpecUnion	diskLoc, 
	EntryIndex			diskLocDirEntryIndex,	//	relative to cur dir sector
	EntryIndex			directoryIndex			//	relative to entire directory
) {
	OSErr			err = noErr;
	O_CTopic		*topic;
	
	err = _inherited::IEntry(
		cDisk, cParentFolder, 
		cDisk == NULL ? FSObject_ROOT_FOLDER : 
			(cParentFolder ? FSObject_FOLDER : FSObject_DISK_ROOT_FOLDER), 
		diskLoc, diskLocDirEntryIndex, directoryIndex
	);
	
	if (!err) {
		if (cParentFolder) {
			topic = i_topicRef.cTopic;
		} else {
			topic = cDisk ? cDisk->i_topicRef.cTopic : NULL;
		}
		
		if (topic) {
			err = topic->O_SetRecent();
		}
	}
	
	return err;
}

Err		CFolder::EntryDispatchCB(
	O_CBType				cbType, 
	O_CBDataP			cbData)
{
	OSErr		err = noErr;
	
	switch (cbType) {
		
		case ADFS_O_CB_PANE_ARRAY_GET_FILES_FOLDERS:
		case ADFS_O_CB_PANE_ARRAY_GET_SEL_FILES_FOLDERS: {
			err = _inherited::EntryDispatchCB(
				ADFS_O_CB_PANE_ARRAY_GET, cbData);
			break;
		}
		
		default: {
			err = _inherited::EntryDispatchCB(cbType, cbData);
			break;
		}
	}

	return err;
}

ADFS_IconType		CFolder::GetIconType(void)
{
	return ADFS_Icon_FOLDER;
}

OSErr	CFolder::NewFile(Boolean isFolder, CEntry **entryH)
{
	return noErr;
}

//	gets prodos equivalent file type
ushort			CFolder::GetFileType_ProEquiv(void)
{
	return Pro_FileType_DIR;
}

/*
OSErr			CFolder::ADFS_SpoolToFile(FSSpec *fileSpec)
{
	OSErr				err = noErr;

	ReportErrorStr(-1, "No spooling baby!");
	return err;
}
*/

OSErr			CFolder::CanAcceptFlavor(
	DragReference	theDrag, 
	ItemReference	itemRef, 
	FlavorType		flavorType, 
	Boolean			*goodFlavorB)
{
	OSErr		err = noErr;
	
	if (flavorType == kDragFlavorTypeADFS) {
		CEntry		*theEntry;
		Size		dataSize = sizeof(CEntry *);

		err = GetFlavorData(
			theDrag, itemRef, kDragFlavorTypeADFS,
			&theEntry, &dataSize, 0);
		
		if (
			!err && (
				theEntry->i_type == FSObject_FILE
				|| theEntry->i_type == FSObject_FOLDER
			) && theEntry->i_cDisk.pro == i_cDisk.pro
		) {
			*goodFlavorB = TRUE;
		}
	}
	
	return err;
}

OSErr			CFolder::DragReceiveFlavor(
	DragReference	theDrag, 
	ItemReference	itemRef, 
	FlavorType		flavorType, 
	void			*data)
{
	OSErr		err = noErr;
	
	switch (flavorType) {

		case kDragFlavorTypeADFS: {
			if (IsLocked()) {
				ReportError(err = IC_Err_DISK_LOCKED);
			} else {
				CEntry		*entryP;
				Size		dataSize = sizeof(Ptr);

				err = GetFlavorData(
					theDrag, itemRef, kDragFlavorTypeADFS,
					&entryP, &dataSize, 0);
				
				if (!err) {
					err = ADFS_SetPendingCopy(
						CCT_Copy_ENTRY, 
						CCT_Copy_ENTRY, 
						entryP);
				}
			}
			break;
		}
		
		//	cope with Promises too
		case flavorTypeHFS: {
			if (IsLocked()) {
				ReportError(err = IC_Err_DISK_LOCKED);
			} else {
				HFSFlavor		hfsFlavor;
				
				err = HFSFlavorDataFromDrag(
					theDrag, itemRef, &hfsFlavor);
				
				if (!err) {			
					err = ADFS_SetPendingCopy(
						CCT_Copy_FSSPEC, 
						CCT_Copy_ENTRY, 
						&hfsFlavor.fileSpec);
				}
			}
			break;
		}
	}
	
	return err;
}

Boolean			CFolder::GetUniqueName(char *fileName)
{
	return TRUE;
}

void			CFolder::ConformStrLen(char * nameStr)
{
}

CEntry		*CFolder::GetIndEntry(EntryIndex entryIndex)
{
	CEntry			*entryP = NULL;
	O_CTopic		*cTopic = GetEntryTopic(this);
	
	if (cTopic->O_GetIndTopic(entryIndex, &cTopic) == noErr) {
		if (cTopic) entryP = GetTopicEntry(cTopic);
	}
	
	return entryP;
}

EntryIndex		CFolder::CountEntries(void)
{
	O_CTopic			*cTopic = GetEntryTopic(this);
	O_TopicIndex	numTopics;
	EntryIndex			numEntries = 0;
	
	if (cTopic->O_CountTopics(&numTopics) == noErr) {
		numEntries = numTopics;
	}
	
	return numEntries;
}

OSErr			CFolder::MoveFile(CEntry *srcEntryP, CEntry *dstEntryP)
{
	OSErr		err = noErr;
	
	if (i_cDisk.gen->i_imageRec->osType != FSType_PRO) {
		err = IC_Err_ENTRY_NOT_FOUND;
	} else {
		O_CTopic		*cTopic = GetEntryTopic(this);
	
		err = cTopic->O_MoveChildren(
			GetEntryTopic(srcEntryP), 
			GetEntryTopic(dstEntryP));
	}
	
	return err;
}

CEntryArray		*CFolder::GetDirectory(void)
{
	ADFS_O_CBData_GET_SEL	getSel;
	
	getSel.arrayP				= GetNewEntryArray();
	getSel.not_if_ancestor_selB	= FALSE;
	
	if (getSel.arrayP) {
		if (i_type == FSObject_DISK_ROOT_FOLDER) {
			i_cDisk.gen->ApplyToItems(
				O_Iterate_CHILDREN, 
				ADFS_O_CB_PANE_ARRAY_GET, 
				&getSel);
		} else {
			ApplyToItems(
				O_Iterate_CHILDREN, 
				ADFS_O_CB_PANE_ARRAY_GET, 
				&getSel);
		}
	}
	
	return getSel.arrayP;
}

OSErr		CFolder::OffsetEachFile(
	CFile		*firstFileP, 
	CFile		*lastFileP, 
	short		offsetL)
{
	OSErr		err = noErr;
	CFile		*curFileP = firstFileP;
	Boolean		doneB;
	
	err = ASSERT(firstFileP && lastFileP);
	
	if (!err) do {
		curFileP->i_diskLocDirEntryIndex	+= offsetL;
		curFileP->i_directoryIndex			+= offsetL;
		
		doneB = curFileP == lastFileP;
		
		if (!doneB) {
			O_TopicIndex		indexL;
			
			err = curFileP->i_topicRef.cTopic->O_GetTopicIndex(&indexL);
			
			if (!err) {
				curFileP = (CFile *)GetIndEntry(indexL + 1);
				err = ASSERT(curFileP != NULL);
			}
		}
	} while (!err && !doneB);
	
	return err;
}

OSErr			CFolder::DeleteFolderContents(
	Boolean		warnB, 
	CDialogCopy	*copyP0)
{
	OSErr		err				= noErr;
	Boolean		emptyFolderB	= TRUE;
	long		numEntriesL		= CountEntries();
	char		bufAC[256];
	CEntry		*entryP;
	long		curEntryL;
	
	if (warnB) {
		GetName(bufAC);
	} else {
		bufAC[0] = 0;
	}
	
	for (curEntryL = numEntriesL - 1; !err && curEntryL >= 0; curEntryL--) {
		entryP = GetIndEntry(curEntryL);
		
		if (entryP) {
			if (!entryP->IsDeleted()) {
				emptyFolderB	= FALSE;
				curEntryL		= 0;
			}
		}
	}

	if (
		!warnB
		|| emptyFolderB 
		|| DoDeleteFileDialog(bufAC) == kDlog_DeleteFile_ItemID_DELETE
	) {
		for (curEntryL = numEntriesL - 1; !err && curEntryL >= 0; curEntryL--) {
			entryP = GetIndEntry(curEntryL);
			
			if (entryP) {
				if (!entryP->IsDeleted()) {
					err = entryP->Delete(warnB, copyP0);
				}
			} else {
				ReportError(err = IC_Err_ENTRY_NOT_FOUND);
			}
		}
	} else {
		err = 1;
	}

	return err;
}

OSErr			CFolder::UnDeleteFolderContents(
	Boolean			recursiveB, 
	CDialogCopy		*copyP0)
{
	OSErr		err				= noErr;
	Boolean		emptyFolderB	= TRUE;
	long		numEntriesL		= CountEntries();
	CEntry		*entryP;
	long		curEntryL;
		
	for (curEntryL = numEntriesL - 1; !err && curEntryL >= 0; curEntryL--) {
		entryP = GetIndEntry(curEntryL);
		
		if (entryP) {
			if (entryP->IsDeleted()) {
				emptyFolderB	= FALSE;
				curEntryL		= 0;
			}
		}
	}

	if (!emptyFolderB) {
		for (curEntryL = numEntriesL - 1; !err && curEntryL >= 0; curEntryL--) {
			entryP = GetIndEntry(curEntryL);
			
			if (entryP) {
				if (entryP->IsDeleted()) {
					err = entryP->UnDelete(recursiveB, copyP0);
				}
			} else {
				ReportError(err = IC_Err_ENTRY_NOT_FOUND);
			}
		}
	}

	return err;
}

CEntry		*CFolder::FindByName_C(char *nameZ)
{
	EntryIndex		curIndexL, maxIndexL = CountEntries();
	CEntry			*entryP, *foundEntryP = NULL;
	char			testNameAC[256];
	
	for (curIndexL = 0; curIndexL < maxIndexL; curIndexL++) {
		entryP = GetIndEntry(curIndexL);
		
		entryP->_inherited::GetName(testNameAC);
		
		if (CompareCString(nameZ, testNameAC)) {
			foundEntryP = entryP;
			curIndexL = maxIndexL;
		}
	}

	return foundEntryP;
}

void		CFolder::UnCacheFolderSizes(void)
{
	CFolder	*folderP = (CFolder *)GetParentFolder();

	i_physical_sizeL = 0;
	i_logical_sizeL = 0;

	if (i_topicRef.cTopic) {
		InvalStat(ADFS_Stat_SIZE);
		InvalStat(ADFS_Stat_USED);
	}
	
	if (folderP) {
		folderP->UnCacheFolderSizes();
	} else {
		i_cDisk.gen->UnCacheFolderSizes();
	}
	
	GetEnclosingWindow()->UnCacheStats();
}

OSErr		CFolder::AddEntries(Boolean normalB, Boolean deletedB)
{
	OSErr				err = noErr;
	
	if (deletedB) {
		UnCacheFolderSizes();
	}
	
	err = UpdateSort();

	return err;
}

OSErr		CFolder::ToggleDeletedFiles(void)
{
	OSErr		err = noErr;
	
	if (i_cDisk.gen->i_show_deletedB) {
		err = AddEntries(FALSE, TRUE);
	} else {
		long		curEntryL, numEntriesL = CountEntries();
		CEntry		*entryP;
		
		UnCacheFolderSizes();

		for (curEntryL = numEntriesL - 1; !err && curEntryL >= 0; curEntryL--) {
			entryP = GetIndEntry(curEntryL);
			
			if (entryP->IsDeleted()) {
				entryP->Dispose();
			} else {
				if (entryP->i_type == FSObject_FOLDER) {
					err = ((CFolder *)entryP)->ToggleDeletedFiles();
				}
			}
		}
	}
	
	return err;
}

OSErr		CFolder::Reorder(
	O_TopicIndex	oldIndex,
	O_TopicIndex	newIndex)
{
	OSErr		err = noErr;
	
	if (i_type == FSObject_DISK_ROOT_FOLDER) {
		err = i_cDisk.gen->Reorder(oldIndex, newIndex);
	} else {
		err = _inherited::Reorder(oldIndex, newIndex);
	}
	
	return err;
}

typedef struct {
	CFolder			*folderP;
	ADFS_StatType	sortBy;
} ADFS_DirSortRec;

static	OSErr 	CB_S_DirSort(
	void			*refconPV, 
	CEntryArray		*entriesP, 
	const void		*a,
	const void		*b, 
	int				*resultI)
{
	OSErr				err	= noErr;
	ADFS_DirSortRec		*sortRecP = (ADFS_DirSortRec *)refconPV;
	long				resultL = 0;
	
	*resultI = 0;
	
	err = sortRecP->folderP->CB_DirSort(
		sortRecP->sortBy, 
		*(CEntry **)a, 
		*(CEntry **)b, 
		&resultL);
	
	if (!err) {
		if (resultL < 0) {
			*resultI = -1;
		} else if (resultL > 0) {
			*resultI = 1;
		} else {
			*resultI = 0;
		}
	}
	
	return err;
}

OSErr 		CFolder::CB_DirSort(
	ADFS_StatType	sortBy, 
	CEntry 			*a, 
	CEntry 			*b, 
	long			*resultL)
{
	OSErr			err			= noErr;
	Boolean			reverseB	= sortBy < 0;	
	DateTimeRec		a_time;
	DateTimeRec		b_time;
	char			name_a_AC[256];
	char			name_b_AC[256];
	
	if (reverseB) {
		sortBy = (ADFS_StatType)-sortBy;
	}
	
	switch (sortBy) {

		case ADFS_Stat_INDEX: {
			*resultL = (long)a->i_directoryIndex - (long)b->i_directoryIndex;
			break;
		}
		
		case ADFS_Stat_NAME: {
			a->GetName(name_a_AC);
			b->GetName(name_b_AC);
			
			*resultL = CompareCString(name_a_AC, name_b_AC);
			break;
		}
		
		case ADFS_Stat_SIZE: {	//	phys
			*resultL = a->GetPhysicalSize() - b->GetPhysicalSize();
			break;
		}
		
		case ADFS_Stat_USED: {	//	log
			*resultL = a->GetLogicalSize() - b->GetLogicalSize();
			break;
		}

		case ADFS_Stat_KIND: {
			a->GetDescription(name_a_AC);
			b->GetDescription(name_b_AC);

			*resultL = CompareCString(name_a_AC, name_b_AC);
			break;
		}

		case ADFS_Stat_CRE_DATE: {
			*resultL = CompareTime(a->GetCreatedTime(&a_time), b->GetCreatedTime(&b_time));
			break;
		}

		case ADFS_Stat_MOD_DATE: {
			*resultL = CompareTime(a->GetModifiedTime(&a_time), b->GetModifiedTime(&b_time));
			break;
		}

		case ADFS_Stat_LOCK: {
			*resultL = a->IsLocked() - b->IsLocked();
			break;
		}
	}
	
	if (*resultL == 0) {
		a->GetName(name_a_AC);
		b->GetName(name_b_AC);
		
		*resultL = CompareCString(name_a_AC, name_b_AC);

		if (*resultL == 0) {
			*resultL = (long)a->i_directoryIndex - (long)b->i_directoryIndex;
		}
	}
	
	if (reverseB && *resultL != 0) {
		*resultL = -(*resultL);
	}

	return err;
}

OSErr		CFolder::Sort(ADFS_StatType sortBy, Boolean recursiveB)
{
	OSErr			err			= noErr;
	CEntryArray		*entriesP	= GetDirectory();
	
	if (!entriesP) {
		ReportErrorStr(err = IC_Err_OS, "Couldn't allocate dir array");
	} else {
		ADFS_DirSortRec		sortRec;
		
		if (entriesP->Count() != CountEntries()) {
			char	bufAC[256];
			
			sprintf(
				bufAC, "sort err: dir array has %d items, otln has %d", 
				entriesP->Count(), CountEntries());
			
			ReportErrorStr(err = -1, bufAC);
		}
		
		if (!err) {
			sortRec.folderP	= this;
			sortRec.sortBy	= sortBy;

			err = entriesP->Sort(CB_S_DirSort, &sortRec);
		}

		if (!err) {
			long			newIndexL;
			CEntry			*curEntryP;
			
			CE_FOR_EACH(entriesP, newIndexL, curEntryP, err) {
				err = Reorder(curEntryP->GetEntryIndex(), newIndexL);
				
				if (!err) {
					if (
						recursiveB 
						&& (
							curEntryP->i_type == FSObject_FOLDER
							|| curEntryP->i_type == FSObject_DISK
						)
					) {					
						if (curEntryP->GetTwirled()) {
							curEntryP->UpdateSort(recursiveB);
						}
					}
				}
			} CE_END_EACH();
		}
		
		entriesP->Dispose();
	}
	
	return err;
}

OSErr		CFolder::UpdateSort(Boolean recursiveB)
{
	CFinderWindowHeader		*headerP = GetMyWindow()->i_header;
	ADFS_StatType			statType = headerP->i_arrangeBy;
	
	if (headerP->i_reversedB) {
		statType = (ADFS_StatType)-statType;
	}
	
	return Sort(statType, recursiveB);
}

Boolean		CFolder::IsLocked(void)
{
	if (GetParentFolder()) {
		return _inherited::IsLocked();
	} else {
		return i_cDisk.gen->IsLocked();
	}
}

OSErr		CFolder::CountRecurse(Boolean deletedB, ulong *totalL)
{
	OSErr			err = noErr;
	EntryIndex		curIndexL, maxIndexL = CountEntries();
	CEntry			*entryP;
	
	for (curIndexL = 0; !err && curIndexL < maxIndexL; curIndexL++) {
		entryP = GetIndEntry(curIndexL);
		
		if (deletedB == entryP->IsDeleted()) {
			(*totalL)++;
		}
		
		err = entryP->CountRecurse(deletedB, totalL);
	}
	
	return err;
}


OSErr		CFolder::NewFolder(CEntry **newFolderH)
{
	OSErr		err					= noErr;

	*newFolderH = NULL;
	
	return err;
}

