#include "ADFS_Prefs.h"

#include "CDialogCopy.h"
#include "ADFS_LogFile.h"

#include "CCopyFile_Entry.h"
#include "CCopyFile_MetaTree.h"
#include "CCopyFile_Tree.h"
#include "CCopyFile_FsSpec.h"

#include "CCopyTree.h"

//	pass in the destination
//	must be a folder or a tree
OSErr	GetNewCopyTree(
	CCT_CopyType	copyType, 
	Ptr				myData, 
	CCopyTree		**copyTreeH)
{
	OSErr		err = noErr;
	
	*copyTreeH = new CCopyTree();
	
	if (!(*copyTreeH)) err = memFullErr;
	if (!err) err = (*copyTreeH)->ICopyTree(copyType, myData);

	if (err) {
		(*copyTreeH)->Dispose();
		(*copyTreeH) = NULL;
	}
	
	return err;
}

static Boolean	s_isCopyTranslatedB	= FALSE;
Boolean			IsCopyTranslated(void)
{
	return s_isCopyTranslatedB;
}

void			SetCopyTranslated(Boolean isCopyTranslatedB)
{
	s_isCopyTranslatedB = isCopyTranslatedB;
}

/******************************************************/
OSErr			CCopyTree::ICopyTree(CCT_CopyType destCopyType, Ptr destFolderP)
{
	OSErr		err = noErr;
	
	err = GetNewCopyFile(
		NULL, destCopyType, destFolderP, &i_destP);

	i_sourceP = NULL;
	
	return err;
}
	
void			CCopyTree::Dispose(void)
{
	if (i_destP) i_destP->Dispose();
	if (i_sourceP) i_sourceP->Dispose();
	
	delete this;
}

OSErr	CCopyTree::GetNewCopyFile(
	CCopyFile		*parent0, 
	CCT_CopyType	copyType, 
	Ptr				myData, 
	CCopyFile		**copyFileH)
{
	OSErr		err = noErr;
	
	switch (copyType) {
		
		default: {
			err = ASSERT(0);
			break;
		}

		case CCT_Copy_ENTRY: {
			CCopyFile_Entry		*entryP;
			
			*copyFileH = entryP = new CCopyFile_Entry;
			if (!(*copyFileH)) err = memFullErr;
			if (!err) err = entryP->ICopyFile_Entry(
				parent0, myData);
			break;
		}

		case CCT_Copy_META_TREE: {
			CCopyFile_MetaTree		*treeP;
			
			*copyFileH = treeP = new CCopyFile_MetaTree;
			if (!(*copyFileH)) err = memFullErr;
			if (!err) err = treeP->ICopyFile_MetaTree(
				parent0, myData);
			break;
		}

		case CCT_Copy_TREE: {
			CCopyFile_Tree		*treeP;
			
			*copyFileH = treeP = new CCopyFile_Tree;
			if (!(*copyFileH)) err = memFullErr;
			if (!err) err = treeP->ICopyFile_Tree(
				parent0, myData);
			break;
		}

		case CCT_Copy_FSSPEC: {
			CCopyFile_FsSpec	*specP;
			
			*copyFileH = specP = new CCopyFile_FsSpec;
			if (!(*copyFileH)) err = memFullErr;
			if (!err) err = specP->ICopyFile_FsSpec(
				parent0, myData);
			break;
		}
	}

	if (err) {
		(*copyFileH)->Dispose();
		(*copyFileH) = NULL;
	} else {
		(*copyFileH)->i_copyTreeP = this;
	}
	
	return err;
}

OSErr			CCopyTree::ScanSourceForCopy(Ptr myDataP, CCopyFile **fileExistsH)
{
	OSErr		err = noErr;

	*fileExistsH = NULL;

	if (i_sourceP) {
		err = i_sourceP->ScanForCopyR(myDataP, fileExistsH);
	}

	return err;
}

OSErr			CCopyTree::Remove(CCopyFile *copyFileP)
{
	OSErr		err = noErr;
	
	//	for now, we can only remove items from the root
	ASSERT(copyFileP && copyFileP->i_parentP0 == NULL);
	
	if (!err) {
		CCopyFile	**prevFileH	= NULL;
		
		for (
			prevFileH = &i_sourceP;
			(*prevFileH) != NULL && (*prevFileH)->i_sisterP0 != copyFileP;
			prevFileH = &(*prevFileH)->i_sisterP0
		) {}
		
		err = ASSERT((*prevFileH) != NULL);

		if (!err) {
			*prevFileH = (*prevFileH)->i_sisterP0;
			copyFileP->i_sisterP0 = NULL;
		}
	}
	
	return err;
}

//	it's okay to add a folder AND one of it's contents,
//	since we'll weed out duplicates on the fly
OSErr			CCopyTree::AddEntry(
	CCopyFile		*parentP0,
	CCT_CopyType	copyType, 
	Ptr				myData, 
	CCopyFile		**newEntryH0)
{
	OSErr		err				= noErr;
	CCopyFile	*prevSisterP0	= NULL;
	CCopyFile	*fileExistsP	= NULL;
	CCopyFile	**copyFileH		= NULL;

	if (parentP0) {
		if (parentP0->i_childP0) {
			prevSisterP0 = parentP0->i_childP0;
		} else {
			copyFileH = &parentP0->i_childP0;
		}
	} else {
		if (i_sourceP) {
			prevSisterP0 = i_sourceP;
		} else {
			copyFileH = &i_sourceP;
		}
	}
	
	if (prevSisterP0) {
		while (prevSisterP0->i_sisterP0) {
			prevSisterP0 = prevSisterP0->i_sisterP0;
		}

		copyFileH = &prevSisterP0->i_sisterP0;
	}

	err = ScanSourceForCopy(myData, &fileExistsP);
	if (!err) {
		if (fileExistsP) {
			/*
				If I found a copy of this file already in my
				sources, then create a new source to place into the
				tree.  The copy found is 1 of 2 cases:
				
				1) the copy I found is part of the root:
					ie: i'm presently in the process of adding a
					folder heirarchy of which this is a subfolder:
					
					todo: Move it to new location (here)
					return it in copyFileH
					
				2) the copy I found is in a sub folder:
					ie: i'm about to add an item who's parent was 
					already added from a prior AddEntry call (not
					one in this curent recursion)
					
					todo: do nothing at all
					assert newEntryH0 == NULL
					set copyFileH to NULL
			*/
			if (fileExistsP->i_parentP0 == NULL) {
				err = Remove(fileExistsP);
				
				if (!err) {
					*copyFileH = fileExistsP;
				}
			} else {
				//	case 2
				err = ASSERT(newEntryH0 == NULL);
				
				#ifdef DEBUG
					if (err) {
						DebugStr("\pyuk");
					}
				#endif
			}
		} else {
			err = GetNewCopyFile(
				parentP0, copyType, myData, copyFileH);
			
			if (!err) err = (*copyFileH)->AddEntryR();
		}
	}

	if (!err && newEntryH0) {
		*newEntryH0 = *copyFileH;
	}
	
	return err;
}

static	short				CopyTree_CompareFile(CCopyFile *file1, CCopyFile *file2)
{
	short				result = 0;
	OSErr				err = noErr;
	CCT_MemFileRec		memFileRec1;
	CCT_MemFileRec		memFileRec2;
	
	if (!err) err = file1->GetFileInfo(&memFileRec1);
	if (!err) err = file2->GetFileInfo(&memFileRec2);
	
	if (!err) {
		result = strcmp(memFileRec1.nameAC, memFileRec2.nameAC);
	}
	
	return result;
}

static	CCopyFile			*CopyTree_GetIndChild(CCopyFile *curChildP0, short indexS)
{
	while (indexS--) {
		curChildP0 = curChildP0->i_sisterP0;
		ASSERT(curChildP0);
	}

	ASSERT(curChildP0);
	
	return curChildP0;
}

static	short				CopyTree_CountChildren(CCopyFile *curChildP0)
{
	short		total = 0;
	
	while (curChildP0) {
		total++;
		curChildP0 = curChildP0->i_sisterP0;
	}
	
	return total;
}

static	void		CopyTree_Swap(CCopyFile *where1, CCopyFile *where2)
{
	void	*tempP;
	
	tempP = where1->i_myData;
	where1->i_myData = where2->i_myData;
	where2->i_myData = (Ptr)tempP;

	tempP = where1->i_myFileData;
	where1->i_myFileData = where2->i_myFileData;
	where2->i_myFileData = (Ptr)tempP;
		
	tempP = where1->i_childP0;
	where1->i_childP0 = where2->i_childP0;
	where2->i_childP0 = (CCopyFile *)tempP;
}

static	void		CopyTree_Sort(CCopyFile *childrenP, CCT_CopyRec *copyRecP)
{
	short		total = CopyTree_CountChildren(childrenP);

	if (total > 0) {
		short		i, j;
		CCopyFile	*where1, *where2;
		
		for (i = 1; i < total; i++) {
			j = i;

			where1 = CopyTree_GetIndChild(childrenP, j);
			where2 = CopyTree_GetIndChild(childrenP, j - 1);

			while (j > 0 && CopyTree_CompareFile(where1, where2) < 0) {
				CopyTree_Swap(where1, where2);
				if (--j > 0) {
					where1 = where2;
					where2 = CopyTree_GetIndChild(childrenP, j - 1);
				}
			}
		}

		for (i = 0; i < total; i++) {
			where1 = CopyTree_GetIndChild(childrenP, i);
			CopyTree_Sort(where1->i_childP0, copyRecP);
		}
	}
}

OSErr			CCopyTree::Copy(Boolean showProgressB)
{
	OSErr			err = noErr;
	CCT_CopyRec		copyRec;
	
	structclr(copyRec);
	
	if (showProgressB) {
		copyRec.copyDialogP0 = ShowCopyDialog();
	}
	
	copyRec.destP = i_destP;
	
	//	show barber poles
	if (!err) err = i_sourceP->GetSizeR(&copyRec, i_destP);
	if (!err) err = i_destP->VerifyFreeSpace(&copyRec);
	
	//	show actual progress
	if (!err && copyRec.copyDialogP0) {
		copyRec.copyDialogP0->SetItemRemain(copyRec.totalItemsS);
		copyRec.copyDialogP0->SetMaxProgress(copyRec.totalSizeL);
	}

	if (!err) {
		ADFS_Log("Begin Copy from ");
		i_sourceP->LogCopyFileType();
		ADFS_Log(" to ");
		i_destP->LogCopyFileType();
		ADFS_Log("\n");
	}
	
	if (i_sourceP->i_copyType == CCT_Copy_FSSPEC) {
		CopyTree_Sort(i_sourceP, &copyRec);
	}
	
	if (!err) err = i_sourceP->CopyToR(i_destP->i_myData, &copyRec);
	
	if (showProgressB && copyRec.copyDialogP0) {
		copyRec.copyDialogP0->HideCopyDialog();
	}

	return err;
}
