#include "MemUtils.h"
#include "IC_Errors.h"
#include "CDialogDeleteFile.h"
#include "ADFS_LogFile.h"
#include "CDialogCopy.h"

#include "CCopyFile.h"

/**********************************************/
OSErr			CCopyFile::ICopyFile(
	CCopyFile		*parent0, 
	CCT_CopyType	copyType, 
	Ptr				myData)
{
	OSErr		err = noErr;
	
	i_copyTreeP		= NULL;
	i_copyType		= copyType;
	i_myData		= myData;
	i_myFileData	= NULL;
	i_parentP0		= parent0;
	i_sisterP0		= NULL;
	i_childP0		= NULL;
	i_ioType		= ADFS_IO_NONE;
	i_res_forkB		= FALSE;
	
	return err;
}

void			CCopyFile::Dispose(void)
{
	if (i_childP0) {
		i_childP0->Dispose();
	}
	
	if (i_sisterP0) {
		i_sisterP0->Dispose();
	}

	DisposeData();

	delete this;
}

/**********************************************/
OSErr	CCopyFile::GetFileInfo(CCT_MemFileRec *fileRecP)
{
	OSErr			err = noErr;
	DateTimeRec		nowTime;
	
	GetTime(&nowTime);

	if (DateTimeIsNull(&fileRecP->creDate)) {
		fileRecP->creDate = nowTime;
	}
		
	if (DateTimeIsNull(&fileRecP->modDate)) {
		fileRecP->modDate = nowTime;
	}
		
	if (fileRecP->forkedB) {
		GetForkInfo(&fileRecP->forkInfo);
	}

	return err;
}

/**********************************************/

OSErr	CCopyFile::IsFolder(Boolean *isFolderB)
{
	OSErr		err = noErr;
	
	//	override me
	*isFolderB = FALSE;
	
	return err;
}

OSErr	CCopyFile::CountFilesInFolder(ulong *numFilesL)
{
	OSErr		err = noErr;
	
	//	override me
	*numFilesL = 0;

	return err;
}

OSErr	CCopyFile::GetIndFileInFolder(ulong fileIndex, Ptr *fileDataP)
{
	OSErr		err = noErr;
	
	//	override me
	*fileDataP = NULL;
	
	return err;
}

/**********************************************/
OSErr		CCopyFile::CreateFile(Ptr parentFolderP, CCT_MemFileRec *fileRecP)
{
	OSErr		err = noErr;

	//	override me

	return err;
}

OSErr		CCopyFile::Delete(void)
{
	OSErr		err = noErr;
	
	//	override me
	return err;
}

OSErr		CCopyFile::SetFileInfo(CCT_MemFileRec *fileRecP)
{
	OSErr		err = noErr;

	if (fileRecP->forkedB && SupportsForks()) {
		SetForkInfo(&fileRecP->forkInfo);
	}

	return err;
}

void		CCopyFile::DisposeData(void)
{
	//	override me
	i_myFileData = NULL;
}

/**********************************************/
OSErr	CCopyFile::Open(ADFS_IOType ioType, Boolean resForkB)
{
	OSErr		err = noErr;
	
	i_ioType	= ioType;
	i_res_forkB = resForkB;

	//	override me
	return err;
}

OSErr	CCopyFile::Close(void)
{
	OSErr		err = noErr;
	
	//	override me
	return err;
}

OSErr	CCopyFile::Read(ulong *bytesIO, char *bufP)
{
	OSErr		err = noErr;
	
	//	override me
	return err;
}

OSErr	CCopyFile::Write(ulong *bytesIO, char *bufP)
{
	OSErr		err = noErr;
	
	//	override me
	return err;
}
	
OSErr			CCopyFile::VerifyFreeSpace(CCT_CopyRec *copyRecP)
{
	OSErr		err = noErr;
	
	err = ASSERT(!i_sisterP0 && !i_childP0);

	//	override me
	//	twiddle barber poles
	if (!err && copyRecP->copyDialogP0) {
		copyRecP->copyDialogP0->SetProgress(0);
	}

	return err;
}

OSErr			CCopyFile::VerifyFileSize(CCopyFile *sourceP, ulong fileSizeL)
{
	//	override me
	
	return noErr;
}

OSErr			CCopyFile::GetSizeSelf(CCT_CopyRec *copyRecP)
{
	OSErr		err = noErr;
	
	//	override me
	//	twiddle barber poles
	if (!err && copyRecP->copyDialogP0) {
		copyRecP->copyDialogP0->i_itemRemainUL = copyRecP->totalItemsS;
		copyRecP->copyDialogP0->InvalDialogItem(kCopy_ITEM_REMAIN_VERB);
		copyRecP->copyDialogP0->SetProgress(0);
	}

	return err;
}

OSErr			CCopyFile::GetSizeR(CCT_CopyRec *copyRecP, CCopyFile *destP)
{
	OSErr			err = noErr;
	CCT_CopyRec		selfRec = *copyRecP;
	
	if (!err) err = GetSizeSelf(copyRecP);
	if (!err) err = destP->VerifyFileSize(this, copyRecP->totalSizeL - selfRec.totalSizeL);
	
	if (!err && i_childP0) {
		err = i_childP0->GetSizeR(copyRecP, destP);
	}
	
	if (!err && i_sisterP0) {
		err = i_sisterP0->GetSizeR(copyRecP, destP);
	}
	
	return err;
}

Boolean			CCopyFile::IsForked(void)
{
	//	override me
	return FALSE;
}

OSErr			CCopyFile::GetForkInfo(CCT_ForkInfo *forkInfoP)
{
	OSErr	err = ASSERT(IsForked());
	
	forkInfoP->hasFInfo		= FALSE;
	forkInfoP->hasXFInfo	= FALSE;
	
	return err;
}

OSErr			CCopyFile::SetForkInfo(CCT_ForkInfo *forkInfoP)
{
	OSErr	err = ASSERT(IsForked());
	
	return err;
}

Boolean			CCopyFile::SupportsForks(void)
{
	//	override me
	return FALSE;
}

OSErr			CCopyFile::GetParentRef(Ptr *parentFolderH)
{
	//	override me
	return ASSERT(0);
}

OSErr			CCopyFile::GetVolumeRef(Ptr *volumeH)
{
	//	override me
	return ASSERT(0);
}

OSErr			CCopyFile::MoveFileToSelf(CCopyFile *srcFileP)
{
	//	override me
	return ASSERT(0);
}

void			CCopyFile::LogCopyFileType(void)
{
	//	override me
	ASSERT(0);
}

OSErr			CCopyFile::VerifyMoveFileWithinVolume(void)
{
	//	override me
	return noErr;
}

#define			CCT_kFileBufferSize		1024
OSErr			CCopyFile::CopySelfTo(
	Ptr			destParentFolderP, 
	CCT_CopyRec *copyRecP)
{
	OSErr			err				= noErr;
	ulong			curProgStartL	= 0;
	Boolean			isFolderB;
	CCT_CopyRec		curFileCopyRec;
	Ptr				sourceParentFolderP;

	if (copyRecP->copyDialogP0) {
		structclr(curFileCopyRec);
				
		err = GetSizeSelf(&curFileCopyRec);
		curProgStartL = copyRecP->copyDialogP0->i_curBytesUL;
	}

	if (!err) err = IsFolder(&isFolderB);

	//	as long as we're not moving it within the same folder
	
	if (!err) err = GetParentRef(&sourceParentFolderP);
	
	if (!err) {
		if (sourceParentFolderP == destParentFolderP) {
			if (copyRecP->copyDialogP0 && !isFolderB) {
				copyRecP->copyDialogP0->SetProgress(
					curProgStartL + curFileCopyRec.totalSizeL);
			}
		} else {
			char			bufferAC[CCT_kFileBufferSize];
			ulong			bytesIO;
			Boolean			doneB	= FALSE;
			OSErr			err2	= noErr;
			CCT_MemFileRec	memFileRec;
			Ptr				srcVolRef, dstVolRef;
			
			//	check if we're moving a file within the same volume
			if (!err) err = GetVolumeRef(&srcVolRef);
			if (!err) err = copyRecP->destP->GetVolumeRef(&dstVolRef);
			
			if (!err && srcVolRef == dstVolRef) {
				err = VerifyMoveFileWithinVolume();
			}

			structclr(memFileRec);
			if (!err) err = GetFileInfo(&memFileRec);
			
			if (!err) {
				ADFS_Log("Copying file '");
				ADFS_Log(memFileRec.nameAC);
				ADFS_Log("'\n");
			}

			if (!err) err = copyRecP->destP->CreateFile(
				destParentFolderP, &memFileRec);
			
			if (!err) {
				if (srcVolRef == dstVolRef) {
					err = copyRecP->destP->MoveFileToSelf(this);
					if (!err) err = copyRecP->destP->SetFileInfo(&memFileRec);
					
					if (!isFolderB) {
						copyRecP->copyDialogP0->SetProgress(
							curProgStartL + curFileCopyRec.totalSizeL);
					} else {
						copyRecP->destP->DisposeData();
					}
				} else if (!isFolderB) {
					ulong			reportedSizeL	= 0;
					
					if (copyRecP->copyDialogP0) {
						copyRecP->copyDialogP0->i_itemRemainUL--;
						copyRecP->copyDialogP0->InvalDialogItem(kCopy_ITEM_REMAIN_VERB);
					}
				
					if (!err) {
						Boolean		isResForkB	= FALSE;
						//	else we're moving it across volumes
						
						do {
							bytesIO	= CCT_kFileBufferSize;
							
							if (!err) err = Open(ADFS_IO_READ, isResForkB);
							if (!err) {
								//	now open that file
								if (!err) err = copyRecP->destP->Open(ADFS_IO_WRITE, isResForkB);
								
								if (!err) {
									if (!err) do {
										if (!err) err = Read(&bytesIO, bufferAC);

										if (err == eofErr) {
											err = noErr;
											doneB = TRUE;
										}
										
										if (!err) err = copyRecP->destP->Write(&bytesIO, bufferAC);
										
										if (!err && copyRecP->copyDialogP0) {
											
											reportedSizeL += bytesIO;
											
											if (reportedSizeL > curFileCopyRec.totalSizeL) {
												reportedSizeL = curFileCopyRec.totalSizeL;
											}
											
											copyRecP->copyDialogP0->SetProgress(
												curProgStartL + reportedSizeL);
										}
										
										if (err) doneB = TRUE;
									} while (!doneB);
									
									err2 = copyRecP->destP->Close();
									if (!err) err = err2;
								}

								err2 = Close();
								if (!err) err = err2;
							}
							
							if (!err && !isResForkB) {
								isResForkB = IsForked();
								
								if (isResForkB) {
									doneB = !copyRecP->destP->SupportsForks();
								}
							}
							
							if (err) doneB = TRUE;
						} while (!doneB);
					}

					if (err == IC_Err_DISK_FULL) {
						ReportError(err);
						(void)copyRecP->destP->Delete();
					} else {
						if (!err) err = copyRecP->destP->SetFileInfo(&memFileRec);
					}
				}
			
				if (!isFolderB) {
					copyRecP->destP->DisposeData();
				}
			}
		}
	}
	
	return err;
}

OSErr			CCopyFile::CopyToR(Ptr destParentFolderP, CCT_CopyRec *copyRecP)
{
	OSErr		err = noErr;
	Boolean		heirarchic_fsB = TRUE;
	
	err = CopySelfTo(destParentFolderP, copyRecP);
	if (err == IC_Err_NOT_REALLY_A_FOLDER) {
		err				= noErr;
		heirarchic_fsB	= FALSE;
	}
	
	if (!err && i_childP0) {
		Ptr		destSubFolderP = copyRecP->destP->i_myFileData;
		
		//	if we just moved a folder on the same volume, 
		//	then i've already moved the children too and disposed
		//	the dest data
		if (destSubFolderP) {
			CCT_MemFileRec	memFileRec;

			copyRecP->destP->i_myFileData = NULL;
			
			if (!err) err = i_childP0->CopyToR(
				destSubFolderP, copyRecP);
			
			if (!err) err = ASSERT(copyRecP->destP->i_myFileData == NULL);
			
			copyRecP->destP->i_myFileData = destSubFolderP;
			
			if (heirarchic_fsB) {
				structclr(memFileRec);
	
				//	set the folder attributes now (lock, dates etc)
				if (!err) err = GetFileInfo(&memFileRec);
				if (!err) err = copyRecP->destP->SetFileInfo(&memFileRec);
			}
						
			copyRecP->destP->DisposeData();
		}
	} else {
		Boolean			isFolderB;
		
		if (!err) err = IsFolder(&isFolderB);
		
		if (!err && isFolderB) {
			copyRecP->destP->DisposeData();
		}
	}
	
	if (!err && i_sisterP0) {
		err = i_sisterP0->CopyToR(destParentFolderP, copyRecP);
	}
	
	return err;
}

OSErr			CCopyFile::AddEntryR(void)
{
	OSErr		err = noErr;
	Boolean		isFolderB;
		
	if (!err) err = IsFolder(&isFolderB);
	if (!err && isFolderB) {
		ulong		fileIndex, numFilesL;
		Ptr			fileDataP;
		CCopyFile	**copyFileH		= NULL;
		
		copyFileH = &i_childP0;

		if (!err) err = CountFilesInFolder(&numFilesL);
		
		for (fileIndex = 0; !err && fileIndex < numFilesL; fileIndex++) {
			if (!err) err = GetIndFileInFolder(fileIndex, &fileDataP);
			
			if (err == IC_Err_INVISIBLE_FILE) {
				err = noErr;
			} else {
				if (!err) err = i_copyTreeP->AddEntry(
					this, i_copyType, fileDataP, copyFileH);

				if (!err) {
					ASSERT(*copyFileH);
					copyFileH = &(*copyFileH)->i_sisterP0;
				}
			}
		}
	}
	
	return err;
}

OSErr			CCopyFile::ScanForCopySelf(Ptr myDataP, CCopyFile **fileExistsH)
{
	OSErr		err = noErr;
	
	//	override me
	
	return err;
}

OSErr			CCopyFile::ScanForCopyR(Ptr myDataP, CCopyFile **fileExistsH)
{
	OSErr		err = noErr;
	
	err = ScanForCopySelf(myDataP, fileExistsH);
	
	if (!err && !(*fileExistsH) && i_childP0) {
		err = i_childP0->ScanForCopyR(myDataP, fileExistsH);
	}
	
	if (!err && !(*fileExistsH) && i_sisterP0) {
		err = i_sisterP0->ScanForCopyR(myDataP, fileExistsH);
	}
	
	return err;
}

