#include "CDiskMapWindow.h"
#include "ProFileTypes.h"
#include "ADFS_LogFile.h"
#include "FileSystemTypes.h"
#include "CFolder.h"
#include "CDisk.h"
#include "Disassembly.h"
#include "BasicTokenizer.h"
#include "AppleWorks.h"
#include "CFile.h"
#include "ProStructs.h"
#include "IC_Errors.h"
#include "CCopyFile_Entry.h"

/**********************************************/
OSErr			CCopyFile_Entry::ICopyFile_Entry(
	CCopyFile		*parent0, 
	Ptr				myData)
{
	OSErr		err = noErr;
	
	err = _inherited::ICopyFile(parent0, CCT_Copy_ENTRY, myData);
		
	i_fileSysBuf		= NULL;
	i_fileSysBufMaxSize	= 0;
	i_fileSysBufCurSize	= 0;

	i_eofB					= FALSE;
	i_detokenizeB			= FALSE;
	i_preDetokenBuf			= NULL;
	i_preDetokenBufMaxSize	= 0;
	i_preDetokenBufCurSize	= 0;
	i_new_lineB				= TRUE;

	return err;
}

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

/**********************************************/
CEntry			*CCopyFile_Entry::GetEntry(void)
{
	return (CEntry *)(i_myFileData == NULL ? i_myData : i_myFileData);
}

OSErr		CCopyFile_Entry::GetFileInfo(CCT_MemFileRec *fileRecP)
{
	OSErr	err		= noErr;
	CEntry	*entryP = GetEntry();
	
	if (!err) {
		entryP->GetName(fileRecP->nameAC);
		fileRecP->fileType	= entryP->GetFileType_ProEquiv();
		fileRecP->auxType	= entryP->GetAuxType();
		entryP->GetCreatedTime(&fileRecP->creDate);
		entryP->GetModifiedTime(&fileRecP->modDate);
		entryP->GetAccessBits(&fileRecP->access);

		fileRecP->forkedB	= entryP->IsForked();

		err = _inherited::GetFileInfo(fileRecP);
	}
		
	return err;
}

OSErr		CCopyFile_Entry::SetFileInfo(CCT_MemFileRec *fileRecP)
{
	OSErr		err			= noErr;
	CEntry		*entryP		= GetEntry();
	
	entryP->SetCreatedTime(&fileRecP->creDate);
	entryP->SetModifiedTime(&fileRecP->modDate);
	entryP->SetAccessBits(&fileRecP->access);

	_inherited::SetFileInfo(fileRecP);

	if (fileRecP->fileType != Pro_FileType_DIR) {
	
		((CFile *)entryP)->SetBinFileType();
		entryP->InvalStat(ADFS_Stat_ICON);
		entryP->InvalStat(ADFS_Stat_KIND);
		entryP->InvalStat(ADFS_Stat_SIZE);
		entryP->InvalStat(ADFS_Stat_USED);
		entryP->i_cDisk.gen->InvalStat(ADFS_Stat_USED);
	}
	
	if (entryP->i_cDisk.gen->i_diskMapP) {
		entryP->i_cDisk.gen->i_diskMapP->Refresh(entryP, TRUE, TRUE);
	}
	
	return err;
}

/**********************************************/
OSErr		CCopyFile_Entry::IsFolder(Boolean *isFolderB)
{
	FSObjectType	type = GetEntry()->i_type;
	
	*isFolderB = FSObject_IS_FOLDER(type);
	
	return noErr;
}

OSErr		CCopyFile_Entry::CountFilesInFolder(ulong *numFilesL)
{
	OSErr		err			= noErr;
	CEntry		*entryP		= GetEntry();
	CFolder		*folderP;
	
	if (entryP->i_type == FSObject_DISK) {
		folderP = ((CDisk *)entryP)->i_rootDir.gen;
	} else {
		err = ASSERT(entryP->i_type == FSObject_FOLDER);
		folderP = (CFolder *)entryP;
	}
	
	if (!err) *numFilesL = (ulong)folderP->CountEntries();
	
	return err;
}

OSErr		CCopyFile_Entry::GetIndFileInFolder(ulong fileIndex, Ptr *fileDataP)
{
	OSErr		err			= noErr;
	CEntry		*entryP		= GetEntry();
	CFolder		*folderP;
	
	if (entryP->i_type == FSObject_DISK) {
		folderP = ((CDisk *)entryP)->i_rootDir.gen;
	} else {
		err = ASSERT(entryP->i_type == FSObject_FOLDER);
		folderP = (CFolder *)entryP;
	}
	
	if (!err) *fileDataP = (Ptr)folderP->GetIndEntry(fileIndex);
	
	return err;
}

OSErr		CCopyFile_Entry::CreateFile(Ptr parentFolderP, CCT_MemFileRec *fileRecP)
{
	OSErr			err			= noErr;
	CEntry			*entryP		= GetEntry();
	FSObjectType	type		= entryP->i_type;
	CFolder			*folderP	= (CFolder *)parentFolderP;
	
	err = ASSERT(type == FSObject_FOLDER || type == FSObject_DISK_ROOT_FOLDER);

	if (!err) err = ASSERT(
		folderP->i_type == FSObject_FOLDER 
		|| folderP->i_type == FSObject_DISK_ROOT_FOLDER);
	
	if (!err) {
		if (!folderP->GetUniqueName(fileRecP->nameAC)) {
			ReportError(err = IC_Err_FILE_ALREADY_EXISTS);
		}
	}

	if (!err) err = folderP->NewFile(
		fileRecP->fileType == Pro_FileType_DIR, &entryP);

	if (err == IC_Err_NOT_REALLY_A_FOLDER) {
		i_myFileData = (Ptr)parentFolderP;
	} else {
		
		if (!err) {
			entryP->SetName(fileRecP->nameAC);
			entryP->Select(TRUE, FALSE);
			
			if (fileRecP->fileType != Pro_FileType_DIR) {
				entryP->SetFileType_ProEquiv(fileRecP->fileType);
				entryP->SetAuxType(fileRecP->auxType);
				((CFile *)entryP)->SetBinFileType();
			}
			
			i_myFileData = (Ptr)entryP;
		}
	}
	
	return err;
}

void		CCopyFile_Entry::DisposeData(void)
{
	//	don't akchalee dispose anything
	_inherited::DisposeData();
}

OSErr			CCopyFile_Entry::Open(ADFS_IOType ioType, Boolean resForkB)
{
	OSErr		err			= noErr;
	CEntry		*entryP		= GetEntry();
	
	i_eofB					= FALSE;

	if (!err) entryP->i_cDisk.gen->FlushMemDisk(FALSE);
	if (!err) err = entryP->ADFS_Open(ioType, resForkB, &i_fileSysBuf, &i_fileSysBufMaxSize);
	if (!err) err = _inherited::Open(ioType, resForkB);
	i_fileSysBufCurSize = 0;
	
	if (
		ioType == ADFS_IO_READ
		&& IsCopyTranslated()
		&& entryP->IsTranslatable()
		&& entryP->i_fileType != ADFS_File_TEXT
	) {
		i_detokenizeB			= TRUE;
		i_preDetokenBuf			= i_fileSysBuf;
		i_preDetokenBufMaxSize	= i_fileSysBufMaxSize;
		i_preDetokenBufCurSize	= 0;
		i_new_lineB				= TRUE;

		i_fileSysBuf			= entryP->i_basBufP;
		
		if (entryP->i_fileType == ADFS_File_AWP) {
			i_fileSysBufMaxSize		= i_preDetokenBufMaxSize * kAwpBufSizeMultiplier;
		} else if (entryP->i_fileType == ADFS_File_BINARY) {
			i_fileSysBufMaxSize		= i_preDetokenBufMaxSize * kBinBufSizeMultiplier;
		} else {
			i_fileSysBufMaxSize		= i_preDetokenBufMaxSize * kBasBufSizeMultiplier;
		}
		
		ResetAWP();
		gTokenizerP->Reset();
		
		if (entryP->i_fileType == ADFS_File_BINARY) {
			CFile		*fileP = (CFile *)entryP;
						
			ResetDisasm(fileP->GetLoadAddress(), fileP->GetLogicalSize());
		}
	}

	return err;
}


OSErr			CCopyFile_Entry::Close(void)
{
	OSErr		err			= noErr;
	CEntry		*entryP		= GetEntry();
	
	if (!err) err = _inherited::Close();
	if (!err) err = entryP->ADFS_Close();
	if (!err) entryP->i_cDisk.gen->FlushMemDisk(TRUE);

	i_fileSysBuf			= NULL;
	i_fileSysBufMaxSize		= 0;
	i_detokenizeB			= FALSE;
	i_preDetokenBuf			= NULL;
	i_preDetokenBufMaxSize	= 0;
	i_preDetokenBufCurSize	= 0;
	i_new_lineB				= FALSE;
	
	if (!err) err = ASSERT(i_fileSysBufCurSize == 0);

	return err;
}

OSErr			CCopyFile_Entry::DeTokenize(ushort	*bytesReadS)
{
	OSErr		err = noErr;
	ushort		bytesDetokenized;
	CEntry		*entryP		= GetEntry();
	
	if (entryP->i_fileType == ADFS_File_BASIC) {
		DeTokenizeBasic(
			i_preDetokenBuf, &i_preDetokenBufCurSize, 
			*bytesReadS, i_preDetokenBufMaxSize, &i_new_lineB, 
			i_fileSysBuf, &bytesDetokenized);

		*bytesReadS = (long)bytesDetokenized;
	} else if (entryP->i_fileType == ADFS_File_INTBASIC) {
		DeTokenizeInt(
			i_preDetokenBuf,	*bytesReadS,
			i_fileSysBuf,		&bytesDetokenized);
			
		*bytesReadS = (long)bytesDetokenized;
	} else if (entryP->i_fileType == ADFS_File_AWP) {
		DeTokenizeAwp(
			i_preDetokenBuf,	*bytesReadS,
			i_fileSysBuf,		&bytesDetokenized);
		
		*bytesReadS = (long)bytesDetokenized;
	} else if (entryP->i_fileType == ADFS_File_BINARY) {
		err = DeTokenizeAssembly(
			i_preDetokenBuf,	*bytesReadS,
			i_fileSysBuf,		&bytesDetokenized);
		
		*bytesReadS = (long)bytesDetokenized;
	} else {
		ASSERT(0);
	}
	
	return err;
}


/*
	1) buffer may be empty
	2) buffer may have stuff already in it, left over from last read
	3) may read less than full buffer, and loop read 'till full
	4) may read more than full buffer
	
	do {
		if (fileSysBuf not empty) {
			move stuff from fileSysBuf to userBuf
			mark new size of both bufs
		}
		
		if (userBuf not full) {
			assert empty fileSysBuf
			Read into fileSysBuf
			mark new size of fileSysBuf
		}
		
	} while (userBuf not full or eof)
*/
OSErr			CCopyFile_Entry::Read(ulong *bytesIO, char *userBufP)
{
	OSErr		err = noErr;
	ushort		bytesReadS, userBufCurSize = 0, userBytesToCopy = 0;
	CEntry		*entryP;
	Boolean		doneB = FALSE;
	
	if (!err) err = ASSERT(i_myFileData == NULL);
	
	if (!err) {
		entryP = GetEntry();
		
		do {
			if (i_fileSysBufCurSize != 0) {
				userBytesToCopy = i_fileSysBufCurSize;

				if (userBytesToCopy > *bytesIO - userBufCurSize) {
					userBytesToCopy = *bytesIO - userBufCurSize;
				}

				memcpy(&userBufP[userBufCurSize], i_fileSysBuf, userBytesToCopy);
				userBufCurSize		+= userBytesToCopy;

				memmove(
					i_fileSysBuf, &i_fileSysBuf[userBytesToCopy], 
					i_fileSysBufMaxSize - userBytesToCopy);

				i_fileSysBufCurSize -= userBytesToCopy;
			}
			
			if (i_eofB) {
				doneB = TRUE;
			}

			if (!doneB && !err && userBufCurSize < *bytesIO) {
				ASSERT(i_fileSysBufCurSize == 0);

				if (!err) err = entryP->ADFS_Read(&bytesReadS);
				if (err == eofErr) {
					err		= noErr;
					i_eofB	= TRUE;
				}
				
				if (!err && i_detokenizeB) {
					err = DeTokenize(&bytesReadS);

					if (err == eofErr) {
						err		= noErr;
						i_eofB	= TRUE;
					}
				}
				
				i_fileSysBufCurSize = bytesReadS;
			}
			
			if (!doneB) {
				doneB = userBufCurSize == *bytesIO;
			}
			
			if (err) {
				doneB = TRUE;
			}
		} while (!doneB);
	}

	*bytesIO = userBufCurSize;

	if (!err) err = _inherited::Read(bytesIO, userBufP);
	
	if (i_eofB && i_fileSysBufCurSize == 0) {
		err = eofErr;
	}

	return err;
}

/*
	1) userBufSize >= 0
	2) fileSysBufsize == 0
	3) userBufSize may be > i_fileSysBufMaxSize, so loop write
	4) userBufSize may be < i_fileSysBufMaxSize, that's okay

	do {
		if (userBuf not empty) {
			move stuff from userBuf to fileSysBuf
			mark new size of both bufs
		}
		
		if (fileSysBuf is full || userBuf is empty) {
			write into fileSysBuf
			mark fileSysBuf empty
		}
		
	} while (!err && userBuf not empty)
*/
OSErr			CCopyFile_Entry::Write(ulong *bytesIO, char *userBufP)
{
	OSErr		err = noErr;
	ushort		bytesWrittenS = 0, userBufCurSize = 0, userBytesToCopy = 0;
	CEntry		*entryP;
	
	if (!err) err = ASSERT(i_fileSysBufCurSize == 0);

	if (!err) {
		entryP = GetEntry(); 
		userBufCurSize = *bytesIO;
	}
	
	if (!err) do {
		if (userBufCurSize != 0) {
			userBytesToCopy = userBufCurSize;

			if (userBytesToCopy > i_fileSysBufMaxSize - i_fileSysBufCurSize) {
				userBytesToCopy = i_fileSysBufMaxSize - i_fileSysBufCurSize;
			}

			memcpy(&i_fileSysBuf[i_fileSysBufCurSize], userBufP, userBytesToCopy);
			i_fileSysBufCurSize += userBytesToCopy;

			userBufCurSize		-= userBytesToCopy;
			memmove(userBufP, &userBufP[userBytesToCopy], userBufCurSize);
		}
				
		if	(
			i_fileSysBufCurSize == i_fileSysBufMaxSize 
			|| userBufCurSize == 0
		) {
			bytesWrittenS = i_fileSysBufCurSize;

			if (!err) err = entryP->ADFS_Write(&bytesWrittenS);

			i_fileSysBufCurSize = 0;
		}
	} while (!err && userBufCurSize != 0);

	if (!err) err = _inherited::Write(bytesIO, userBufP);
	
	return err;
}

//	called only in dest system
OSErr			CCopyFile_Entry::VerifyFreeSpace(CCT_CopyRec *copyRecP)
{
	OSErr		err		= noErr;
	CEntry		*entryP = GetEntry();
	ulong		sizeL;
	
	ASSERT(
		entryP->i_type == FSObject_FOLDER
		|| entryP->i_type == FSObject_DISK_ROOT_FOLDER);
	
	sizeL	= entryP->i_cDisk.gen->CalcBytesUsedByFile(copyRecP->totalSizeL);
	err		= entryP->i_cDisk.gen->VerifyFreeSpace(sizeL);
	
	if (!err) err = _inherited::VerifyFreeSpace(copyRecP);
	
	return err;
}

OSErr			CCopyFile_Entry::VerifyMoveFileWithinVolume(void)
{
	OSErr		err			= noErr;
//	CEntry		*dstEntryP	= GetEntry();
	CDisk		*destVolP	= GetEntry()->i_cDisk.gen;
	
	if (destVolP->i_imageRec->osType != FSType_PRO) {
		char		errorAC[256];
		char		dstOSTypeAC[64];
		
		sprintf(errorAC, 
			"Error: You can't move files within %s disk images.", 
			destVolP->GetDescription(dstOSTypeAC)); 

//		ReportErrorStr(-1, errorAC);
		err = IC_Err_UNSUPPORTED_FILE_SYSTEM;
	}
	
	return err;
}

OSErr			CCopyFile_Entry::VerifyFileSize(CCopyFile *sourceP, ulong fileSizeL)
{
	OSErr			err			= noErr;
	CDisk			*destVolP	= GetEntry()->i_cDisk.gen;
	ulong			maxFileSizeL;
	CCT_MemFileRec	srcFileRec;
	Pro_FileTypeRec	typeRec;
	
	if (!err) err = sourceP->GetFileInfo(&srcFileRec);
	if (!err) maxFileSizeL = destVolP->GetVolumeMaxFileSize(srcFileRec.fileType);
		
	if (!err && fileSizeL > maxFileSizeL) {
		char		errorAC[256];
		char		srcFileTypeAC[64];
		char		fileSizeAC[64];
		char		dstOSTypeAC[64];
		char		dstVolAC[64];
		char		maxFileSizeAC[64];
		
		typeRec.fileType	= srcFileRec.fileType;
		typeRec.auxType		= srcFileRec.auxType;
		
		if (Pro_GetFileType(&typeRec)) {
			strcpy(srcFileTypeAC, typeRec.descStr);
		} else {
			sprintf(srcFileTypeAC, "$%02X", (short)srcFileRec.fileType);
		}

		sprintf(errorAC, 
			"Error: The %s file '%s' (%s) exceeds the individual file "	\
			"size limit of the %s volume '%s' (%s).", 
			srcFileTypeAC,
			srcFileRec.nameAC, 
			FormatSize(fileSizeL, fileSizeAC), 
			destVolP->GetDescription(dstOSTypeAC), 
			destVolP->GetName(dstVolAC), 
			FormatSize(maxFileSizeL, maxFileSizeAC));
			
		ReportErrorStr(-1, errorAC);
		err = IC_Err_FILE_TOO_LARGE;
	}
	
	if (!err) err = _inherited::VerifyFileSize(sourceP, fileSizeL);
	
	return err;
}

OSErr			CCopyFile_Entry::GetSizeSelf(CCT_CopyRec *copyRecP)
{
	OSErr		err		= noErr;
	CEntry		*entryP = GetEntry();
	
	if (entryP->i_type == FSObject_FILE) {
		copyRecP->totalItemsS++;
		copyRecP->totalSizeL += entryP->GetLogicalSize();
	}

	if (!err) err = _inherited::GetSizeSelf(copyRecP);

	return err;
}

OSErr			CCopyFile_Entry::ScanForCopySelf(Ptr myDataP, CCopyFile **fileExistsH)
{
	OSErr		err = noErr;
	CEntry		*myEntryP = GetEntry();
	CEntry		*testEntryP = (CEntry *)myDataP;
	
	if (myEntryP == testEntryP) {
		*fileExistsH = this;
	}
	
	return err;
}

OSErr			CCopyFile_Entry::GetParentRef(Ptr *parentFolderH)
{
	OSErr		err		= noErr;
	CEntry		*entryP	= GetEntry();

	if (entryP->i_type == FSObject_DISK) {
		*parentFolderH = NULL;
	} else {
		*parentFolderH = (Ptr)entryP->GetParent();
	}

	return err;
}

OSErr			CCopyFile_Entry::GetVolumeRef(Ptr *volumeH)
{
	OSErr		err		= noErr;
	CEntry		*entryP	= GetEntry();
	
	*volumeH = (Ptr)entryP->i_cDisk.gen;
	
	return err;
}

OSErr			CCopyFile_Entry::MoveFileToSelf(CCopyFile *srcFileP)
{
	OSErr		err			= noErr;
	CEntry		*dstEntryP	= GetEntry();
	CEntry		*srcEntryP	= (CEntry *)srcFileP->i_myData;
	
	err = ASSERT(srcFileP->i_copyType == CCT_Copy_ENTRY);
	if (!err) err = ASSERT(srcEntryP->i_cDisk.gen->i_imageRec->osType == FSType_PRO);
	if (!err) err = ASSERT(dstEntryP->i_type != FSObject_DISK);
	if (!err) err = dstEntryP->GetParentFolder()->MoveFile(srcEntryP, dstEntryP);

	return err;
}

OSErr		CCopyFile_Entry::Delete(void)
{
	OSErr			err			= noErr;
	CEntry			*dstEntryP	= GetEntry();
	
	err = dstEntryP->Delete();
	
	return err;
}

void			CCopyFile_Entry::LogCopyFileType(void)
{
	CDisk		*destVolP	= GetEntry()->i_cDisk.gen;
	char		vol_typeAC[64];
	char		dstOSTypeAC[64];
	char		dstVolAC[256];

	sprintf(vol_typeAC, 
		"the %s %s '%s'.", 
		destVolP->GetDescription(dstOSTypeAC), 
		(IS_ImageRec_PHYSICAL(destVolP->i_imageRec) ? "physical disk" : "disk image"),
		destVolP->GetWhereString(dstVolAC));

	ADFS_Log(vol_typeAC);
}

Boolean			CCopyFile_Entry::IsForked(void)
{
	return GetEntry()->IsForked();
}

Boolean			CCopyFile_Entry::SupportsForks(void)
{
	return GetEntry()->i_cDisk.gen->SupportsForks();
}

OSErr			CCopyFile_Entry::GetForkInfo(CCT_ForkInfo *forkInfoP)
{
	return GetEntry()->GetForkInfo(forkInfoP);
}

OSErr			CCopyFile_Entry::SetForkInfo(CCT_ForkInfo *forkInfoP)
{
	return GetEntry()->SetForkInfo(forkInfoP);
}

