//	CFileCpm

#include "ProStructs.h"
#include "CFolderCpm.h"
#include "CDiskCpm.h"
#include "CFileCpm.h"

OSErr		CFileCpm::IFileCpm(
	CDiskCpm			*cDisk, 
	CFolderCpm			*cParentFolderCpm, 
	Cpm_BlockNum		block, 
	Cpm_EntryIndex		diskLocDirEntryIndex,	//	relative to cur dir sector
	Cpm_EntryIndex		directoryIndex			//	relative to entire directory
) {
	OSErr				err = noErr;
	DiskLocSpecUnion	locSpec;
	
	locSpec.cpm = block;
	
	err = _inherited::IFile(
		cDisk, 
		cParentFolderCpm, 
		locSpec, 
		diskLocDirEntryIndex, 
		directoryIndex);
	
	return err;
}

ushort			CFileCpm::GetFileType_ProEquiv(void)
{
	ushort		proType;
	OSType		fileType = GetCpmFileOSType();
	
	switch (fileType) {
		
		case '.ASC':
		case '.PRN':
		case '.DOC':
		case '.ASM':
		case '.BAS':
		case '.TXT': {
			proType = Pro_FileType_TXT;
			break;
		}
		
		case '.BAD': {
			proType = Pro_FileType_BAD;
			break;
		}

		default: {
			proType = Pro_FileType_BIN;
			break;
		}
	}
	
	return proType;
}

Cpm_DirEntry	*CFileCpm::GetMyEntry(void)
{
	return ((CFolderCpm *)GetParentFolder())->GetEntry(
		i_diskLocDirEntryIndex);
}

Cpm_AccessBits		Cpm_GetAccessBits(Cpm_DirEntry *entryP)
{
	Cpm_AccessBits		bits	= Cpm_kAccessChar_ILLEGAL_0;
	Cpm_AccessChar		charIndex;
	
	FOR_EACH_ACCESS_CHAR(charIndex) {
		if (Cpm_IsAccessCharSet(entryP->name[charIndex])) {
			bits |= 1 << charIndex;
		}
	}
	
	return bits;
}

Cpm_AccessBits		CFileCpm::GetCpmAccessBits(void)
{
	Cpm_DirEntry		*entryP	= GetMyEntry();
	Cpm_AccessBits		bits	= Cpm_kAccessChar_ILLEGAL_0;

	if (entryP) {
		bits = Cpm_GetAccessBits(entryP);
	}
	
	return bits;
}

void			CFileCpm::GetAccessBits(Gen_AccessBits *bits)
{
	Cpm_DirEntry	*entryP	= GetMyEntry();
	
	if (entryP) {
		_inherited::GetAccessBits(bits);

		if (Cpm_IsAccessCharSet(entryP->name[Cpm_kAccessChar_READ_ONLY])) {
			bits->destroyEnable	= FALSE;
			bits->renameEnable	= FALSE;
			bits->writeEnable	= FALSE;
		}
		
		if (Cpm_IsAccessCharSet(entryP->name[Cpm_kAccessChar_ARCHIVE])) {
			bits->backup = TRUE;
		}
	}
}

void		Cpm_SetNameOnAllEntries(
	Cpm_DirEntry			*entryP, 
	Cpm_FileNameAndTypeStr	typeNameZ)
{
	Cpm_DirEntry	*nextEntryP, origEntry	= *entryP;
	Boolean			doneB					= FALSE;
	
	do {
		memcpy(entryP->name, typeNameZ, Cpm_kNameAndTypeLength);
		
		nextEntryP	= &(entryP[1]);
		doneB = !Cpm_MatchName(&origEntry, nextEntryP);
		
		if (!doneB) {
			entryP = nextEntryP;
		}
	} while (!doneB);
}

void			Cpm_SetAccessBits(Cpm_DirEntry *entryP, Cpm_AccessBits cpmBits)
{
	Cpm_FileNameAndTypeStr	typeNameAC;

	GetTypeName(entryP, NULL, NULL, &cpmBits, typeNameAC);
	Cpm_SetNameOnAllEntries(entryP, typeNameAC);
}

void			CFileCpm::SetAccessBits(Gen_AccessBits *bitsP)
{
	Cpm_DirEntry	*entryP = GetMyEntry();
	
	if (entryP) {
		Cpm_AccessBits			cpmBits		= GetCpmAccessBits();
		Boolean					read_onlyB	= !(bitsP->destroyEnable && bitsP->renameEnable && bitsP->writeEnable);
		
		Cpm_SetAccessBit(cpmBits, Cpm_kAccessBit_READ_ONLY, read_onlyB);
		Cpm_SetAccessBit(cpmBits, Cpm_kAccessBit_ARCHIVE, bitsP->backup);

		Cpm_SetAccessBits(entryP, cpmBits);
		(void)i_cDisk.cpm->SetBlock();
	}

	_inherited::SetAccessBits(bitsP);
}

char	*Cpm_GetName(Cpm_DirEntry *entryP, char *bufAC)
{
	short			nameIndex;
	
	for (nameIndex = 0; nameIndex < Cpm_kNameLength; nameIndex++) {
		bufAC[nameIndex] = entryP->name[nameIndex] & 0x7F;
		if (bufAC[nameIndex] == ' ') {
			break;
		}
	}

	bufAC[nameIndex] = 0;
	return bufAC;
}

char		*CFileCpm::GetName(char *buf)
{
	Cpm_DirEntry			*entryP = GetMyEntry();
	Cpm_FileNameStr			entryName;
	Cpm_FileTypeStr			entryType;
	
	Cpm_GetName(entryP, entryName);
	Cpm_GetDescription(entryP, entryType);
	sprintf(buf, "%s.%s", entryName, entryType);

	return buf;
}

void		GetTypeName(
	Cpm_DirEntry			*entryP, 
	Cpm_FileNameStr			nameZ0, 
	Cpm_FileTypeStr			typeZ0, 
	Cpm_AccessBits			*bits0, 
	Cpm_FileNameAndTypeStr	typeNameZ)
{
	Cpm_FileNameStr		nameAC;
	char				*nameZ;
	Cpm_FileTypeStr		typeAC;
	char				*typeZ;
	Cpm_AccessBits		bits, *bitsP;

	if (nameZ0) {
		nameZ = nameZ0;
	} else {
		nameZ = nameAC;
		Cpm_GetName(entryP, nameAC);
	}

	if (typeZ0) {
		typeZ = typeZ0;
	} else {
		typeZ = typeAC;
		Cpm_GetDescription(entryP, typeAC);
	}

	if (bits0) {
		bitsP = bits0;
	} else {
		bitsP = &bits;
		bits = Cpm_GetAccessBits(entryP);
	}
	
	Cpm_GetTypeName(nameZ, typeZ, bitsP, typeNameZ);
}

void		Cpm_GetTypeName(
	Cpm_FileNameStr			nameZ, 
	Cpm_FileTypeStr			typeZ, 
	Cpm_AccessBits			*bitsP, 
	Cpm_FileNameAndTypeStr	typeNameZ)
{
	Boolean				no_more_charsB;
	Cpm_AccessChar		charIndex;
	char				curCharC;

	no_more_charsB = FALSE;

	FOR_EACH_ACCESS_CHAR(charIndex) {
		curCharC = ' ';
		
		if (charIndex <= Cpm_kAccessChar_WHEEL_PROTECT) {
		
			if (!no_more_charsB && nameZ[charIndex] == 0) {
				no_more_charsB = TRUE;
			}
			
			if (!no_more_charsB) {
				curCharC = nameZ[charIndex];
			}
		} else {
			if (charIndex == Cpm_kAccessChar_READ_ONLY) {
				no_more_charsB = FALSE;
			}
			
			if (!no_more_charsB && typeZ[charIndex - Cpm_kAccessChar_READ_ONLY] == 0) {
				no_more_charsB = TRUE;
			}
			
			if (!no_more_charsB) {
				 curCharC = typeZ[charIndex - Cpm_kAccessChar_READ_ONLY];
			}
		}
		
		Cpm_SetAccessChar(
			curCharC, Cpm_GetAccessBit(*bitsP, 1 << charIndex));

		typeNameZ[charIndex] = curCharC;
	}
}

void		CFileCpm::SetName(char *buf)
{
	short					strLenS	= strlen(buf);
	
	if (strLenS) {
		Cpm_DirEntry			*entry	= GetMyEntry();
		char					*dotP;
		Cpm_FileNameStr			newNameAC;
		Cpm_FileNameStr			newTypeAC;
		Cpm_FileNameAndTypeStr	typeNameAC;
		
		dotP = strrchr(buf, '.');
		
		if (dotP == NULL) {
			GetDescription(newTypeAC);
		} else {
			strcpy(newTypeAC, &dotP[1]);
			strLenS	= strlen(newTypeAC);
			
			if (strLenS > Cpm_kTypeLength) {
				strLenS = Cpm_kTypeLength;
			} else if (strLenS == 0) {
				GetDescription(newTypeAC);
				strLenS	= strlen(newTypeAC);
			}
		
			newTypeAC[strLenS] = 0;
			*dotP = 0;

			strLenS	= strlen(buf);
		}
		
		if (strLenS > Cpm_kNameLength) {
			strLenS = Cpm_kNameLength;
		} else if (strLenS == 0) {
			Cpm_GetName(entry, buf);
			strLenS	= strlen(buf);
		}
		
		memcpy(newNameAC, buf, strLenS);
		newNameAC[strLenS] = 0;
		
		//	sanitize CP/M name here
		Cpm_SanitizeName(newNameAC);
		Cpm_SanitizeName(newTypeAC);
		
		sprintf(typeNameAC, "%s.%s", newNameAC, newTypeAC);
		_inherited::SetName(typeNameAC);

		GetTypeName(entry, newNameAC, newTypeAC, NULL, typeNameAC);
		Cpm_SetNameOnAllEntries(entry, typeNameAC);
		InvalStat(ADFS_Stat_KIND);

		(void)i_cDisk.cpm->SetBlock();
	}
}

ushort		CFileCpm::GetLoadAddress(void)
{
	return 0;
}

void		CFileCpm::SetLoadAddress(ushort address)
{
}

void			CFileCpm::SetFileType_ProEquiv(Byte proType)
{
}

OSErr 		CFileCpm::ReadFile(Byte *buffer, long length)
{
	OSErr	err = noErr;
	
	return err;
}

OSErr 		CFileCpm::WriteFile(Byte *buffer, long length)
{
	OSErr	err = noErr;
	
	return err;
}

char	*Cpm_GetDescription(Cpm_DirEntry *entryP, char *bufAC)
{
	short			typeIndex;
	
	for (typeIndex = 0; typeIndex < Cpm_kTypeLength; typeIndex++) {
		bufAC[typeIndex] = entryP->fileType[typeIndex] & 0x7F;
		if (bufAC[typeIndex] == ' ') {
			break;
		}
	}

	bufAC[typeIndex] = 0;
	return bufAC;
}

char		*CFileCpm::GetDescription(char *buf)
{
	Cpm_GetDescription(GetMyEntry(), buf);
	return buf;
}

OSType		Cpm_GetFileOSType(Cpm_DirEntry *entryP)
{
	OSType				osType = 0;
	Cpm_FileTypeStr		fileTypeAC;
	char				*charP;
	short				indexS;
	
	Cpm_GetDescription(entryP, fileTypeAC);
	osType = *((long *)&fileTypeAC);
	osType = ((ulong)'.') << 24 | osType >> 8;
	
	for (indexS = 0; indexS < 4; indexS++) {
		charP = &((char *)&osType)[indexS];
		
		if (*charP >= 'a' && *charP <= 'z') {
			*charP = *charP - 'a' + 'A';
		}
	}
	
	return osType;
}

OSType		CFileCpm::GetCpmFileOSType(void)
{
	return Cpm_GetFileOSType(GetMyEntry());
}

/*
ushort		CFileCpm::GetAuxType(void)
{
	return GetCpmFileOSType();
}
*/

Boolean		Cpm_MatchName(Cpm_DirEntry *entry1P, Cpm_DirEntry *entry2P)
{
	Boolean		matchB = FALSE;
	
	if (entry1P->userNumber == entry2P->userNumber) {
		OSType		type1, type2;
	
		type1 = Cpm_GetFileOSType(entry1P);
		type2 = Cpm_GetFileOSType(entry2P);
		
		if (type1 == type2) {
			Cpm_FileNameStr		name1, name2;
			
			Cpm_GetName(entry1P, name1);
			Cpm_GetName(entry2P, name2);
			matchB = strcmp(name1, name2) == 0;
		}
	}
	
	return matchB;
}

static	ulong		Cpm_GetPhysicalSize(Cpm_DirEntry **entryPP)
{
	Cpm_DirEntry	*nextEntryP;
	ulong			fileSize	= 0;
	Boolean			doneB		= FALSE;
	short			numBlocks;
	
	do {
		if ((*entryPP)->numRecords == 0) {
			numBlocks	= 0;
		} else {
			numBlocks	= (((*entryPP)->numRecords - 1) / Cpm_kRecordsPerBlock) + 1;
		}

		fileSize	+= numBlocks * Cpm_kBytesPerBlock;
		nextEntryP	= &((*entryPP)[1]);
		
		doneB = !Cpm_MatchName((*entryPP), nextEntryP);
		
		if (!doneB) {
			ASSERT((*entryPP)->numRecords == Cpm_kRecordsPerExtent);
			(*entryPP) = nextEntryP;
		}
	} while (!doneB);
	
	if (!(fileSize >= 0 && fileSize <= Cpm_kMaxFileSize)) {
		ReportErrorStr(fileSize, "Illegal file size");
		fileSize = 0;
	}
	
	return fileSize;
}

ulong		CFileCpm::GetPhysicalSize(void)
{
	if (i_physical_sizeL == 0) {
		Cpm_DirEntry	*entryP		= GetMyEntry();

		i_physical_sizeL = Cpm_GetPhysicalSize(&entryP);
	}

	return i_physical_sizeL;
}

ulong		CFileCpm::GetLogicalSize(void)
{
	if (i_logical_sizeL == 0) {
		Cpm_DirEntry	*entryP		= GetMyEntry();
		
		i_logical_sizeL	= Cpm_GetPhysicalSize(&entryP);
		
		if (i_logical_sizeL != 0) {
			short			curByte, extentNum, recordNum;
			Cpm_Block		*lastBlockP;
			Cpm_Record		*lastRecordP;

			extentNum = (entryP->numRecords - 1) / Cpm_kRecordsPerBlock;
			recordNum = (entryP->numRecords - 1) % Cpm_kRecordsPerBlock;
			lastBlockP = i_cDisk.cpm->GetBlock(entryP->extent[extentNum]);
			
			if (lastBlockP == NULL) {
				i_logical_sizeL = 0;
			} else {
				i_logical_sizeL -= Cpm_kBytesPerBlock;
				i_logical_sizeL += Cpm_kBytesPerRecord * (recordNum);
				
				lastRecordP = &lastBlockP->record[recordNum];
				
				if (i_fileType == ADFS_File_TEXT) {
					for (
						curByte = 0;
						curByte < Cpm_kBytesPerRecord;
						curByte++
					) {
						if (lastRecordP->byte[curByte] != Cpm_kTextFileEnd) {
							i_logical_sizeL++;
						} else {
							curByte = Cpm_kBytesPerRecord;
						}
					}
				} else {
					i_logical_sizeL += Cpm_kBytesPerRecord;

					for (
						curByte = 0;
						curByte < Cpm_kBytesPerRecord;
						curByte++
					) {
						if (lastRecordP->byte[Cpm_kBytesPerRecord - 1 - curByte] == Cpm_kErasedSentinel) {
							i_logical_sizeL--;
						} else {
							curByte = Cpm_kBytesPerRecord;
						}
					}
				}
			}
		}
	}
	
	return i_logical_sizeL;
}

DateTimeRec	*CFileCpm::GetModifiedTime(DateTimeRec *dt)
{
	return _inherited::GetModifiedTime(dt);
}

ADFS_IconType		CFileCpm::GetIconType(void)
{
	return _inherited::GetIconType();
}

OSErr				CFileCpm::Delete(Boolean warnB, CDialogCopy *copyP0)
{
	OSErr			err			= noErr;
	CFolderCpm		*folderP	= (CFolderCpm *)GetParentFolder();

	if (!err) err = _inherited::Delete(warnB, copyP0);
	err = folderP->Cpm_DeleteEntry(i_diskLocDirEntryIndex);

	return err;
}

ulong			CFileCpm::ADFS_GetBufSize(void)
{
	ushort	size = sizeof(Cpm_Block);
	return size;
}

OSErr		CFileCpm::Cpm_GetIndBlockNum(
	Cpm_BlockNum blockIndex, 
	Cpm_BlockNum *blockNum)
{
	OSErr			err		= noErr;
	Cpm_DirEntry	*entryP	= GetMyEntry();
	
	if (!entryP) {
		err = IC_Err_ENTRY_NOT_FOUND;
	} else {
		Cpm_BlockNum	extentNum			= blockIndex / Cpm_kBlocksPerExtent;
		Cpm_BlockNum	extentBlockIndex	= blockIndex % Cpm_kBlocksPerExtent;
		
		entryP = &(entryP[extentNum]);
		*blockNum = entryP->extent[extentBlockIndex];
	}
	
	return err;
}

OSErr			CFileCpm::ADFS_Read(ushort *bytesRead)
{
	OSErr			err = noErr;
	Cpm_BlockNum	curBlockIndex	= (Cpm_BlockNum)(i_filePos >> 10);		//	fp  is base 0, div by 1024
	Cpm_BlockNum	lastBlockIndex	= curBlockIndex;
	
	*bytesRead = 0;

	//	error if we're not reading block boundaries
	if (i_filePos != (ulong)curBlockIndex * (ulong)Cpm_kBytesPerBlock) {
		err = IC_Err_BLOCK_BOUNDARIES;
		ReportError(err);
	}
	
	if (!err && i_eof > 0) {
		Cpm_BlockNum	blockNum;
		Cpm_Block		*blockP;

		lastBlockIndex	= (Cpm_BlockNum)((i_eof - 1) >> 10);		//	eof is base 1, div by 1024

		if (!err && curBlockIndex <= lastBlockIndex) {
			if (!err) err = Cpm_GetIndBlockNum(curBlockIndex, &blockNum);
			if (!err) {
				blockP = i_cDisk.cpm->GetBlock(blockNum);
				if (blockP == NULL) {
					err = IC_Err_READ_ILLEGAL_FILE_BLOCK;
				}
			}
			
			if (!err) {
				*((Cpm_Block *)i_fileBufP) = *blockP;
				
				//	if this is the last block read, then we read <= 1024 bytes!
				if (curBlockIndex == lastBlockIndex) {
					*bytesRead = ((i_eof - 1) & 0x000003FF) + 1;
				} else {
					*bytesRead = Cpm_kBytesPerBlock;
				}
			}

			if (!err) err = _inherited::ADFS_Read(bytesRead);
		}
	}
		
	if (!err) {
		if (curBlockIndex >= lastBlockIndex) {
			err = eofErr;
		}
	}
	
	return err;
}

OSErr			CFileCpm::Cpm_WriteBlock(ushort numBytesS, Cpm_Block *blockBufP)
{
	OSErr				err = noErr;
	Cpm_DirEntry 		*entryP = GetMyEntry();
	Cpm_Block			*blockP;
	Cpm_BlockNum		*blockNumP;
	
	if (!entryP) {
		err = IC_Err_READ_ILLEGAL_TRACK_SECTOR;
	}
	
	if (!err) {
		if (numBytesS > sizeof(Cpm_Block)) {
			err = IC_Err_CANT_WRITE_MORE_THAN_1;
			ReportError(err);
		}
	}
	
	if (!err) {
		if (!err) err = i_cDisk.cpm->Cpm_GetNextFreeExtentAlloc(&entryP, &blockNumP);
		if (!err) err = i_cDisk.cpm->GetFreeBlock(blockNumP);
		if (!err) blockP = i_cDisk.cpm->GetBlock(*blockNumP);
		
		if (!blockP) {
			err = IC_Err_READ_ILLEGAL_TRACK_SECTOR;
		}

		if (!err) {
			*blockP = *blockBufP;

			if (numBytesS < sizeof(Cpm_Block)) {
				entryP->numRecords += (numBytesS / Cpm_kBytesPerRecord) + 1;
				memset(
					&blockP->byte[numBytesS], 
					i_fileType == ADFS_File_TEXT ? Cpm_kTextFileEnd : Cpm_kErasedSentinel, 
					sizeof(Cpm_Block) - numBytesS);
			} else {
				entryP->numRecords += Cpm_kRecordsPerBlock;
			}
		}
	}
	
	return err;
}

OSErr			CFileCpm::ADFS_Write(ushort *bytesWritten)
{
	OSErr		err			= noErr;
	ushort		bytesLeftS	= *bytesWritten;
	char		*curBufP	= i_fileBufP;
	ushort		curBytesS	= sizeof(Cpm_Block);
	
	if (!err) err = ASSERT((i_eof % curBytesS) == 0);
	
	if (!err) err = _inherited::ADFS_Write(bytesWritten);
	
	while (!err && bytesLeftS) {

		if (bytesLeftS > sizeof(Cpm_Block)) {
			bytesLeftS -= sizeof(Cpm_Block);
		} else {
			curBytesS	= bytesLeftS;
			bytesLeftS	= 0;
		}

		if (!err) err = Cpm_WriteBlock(curBytesS, (Cpm_Block *)curBufP);
		
		curBufP += sizeof(Cpm_Block);
	}
	
	if (!err) i_eof += *bytesWritten;

	return err;
}

OSErr		CFileCpm::ADFS_Close(void)
{
	OSErr		err = noErr;
	OSErr		err2;
	
	err2 = _inherited::ADFS_Close();
	if (!err) err = err2;
	
	return err;
}

/****************************************************/
OSErr		CFileCpm::GetFileBlocks(
	Cpm_BlockNum	*fileBlockA0, 
	Cpm_BlockNum	*numFileBlocksSP)
{
	OSErr	err	= noErr;
	
	(*numFileBlocksSP) = ((i_eof - 1) >> 10) + 1;	//	eof is base 1, div by 1024;

	if (fileBlockA0) {
		Cpm_BlockNum	curIndex;
		
		for (curIndex = 0; !err && curIndex < *numFileBlocksSP; curIndex++) {
			err = Cpm_GetIndBlockNum(curIndex, &fileBlockA0[curIndex]);
			
			if (!err) fileBlockA0[curIndex] += Cpm_kHeaderBlocksPerDisk;
		}
	}
	
	return err;
}

OSErr		CFileCpm::GetEntryAlloc(
	Boolean			getAsBlocksB, 
	Gen_EntryAlloc	**sectorListH)
{
	OSErr		err = noErr;
	
	*sectorListH	= (Gen_EntryAlloc *)TrackNewPtrClear(
		"entry sectors, for file", sizeof(Gen_EntryAlloc));
	
	if (*sectorListH == NULL) err = memFullErr;
	
	if (!err) {
		Cpm_DirEntry	*entryP	= GetMyEntry();
		Cpm_BlockNum	numFileBlocksS;
		Cpm_BlockNum	*blockNumP = NULL;
				
		(**sectorListH).allocSize = getAsBlocksB ? Gen_AllocSize_BYTE : Gen_AllocSize_SECTORS;
		
		if (!entryP) {
			err = IC_Err_ENTRY_NOT_FOUND;
		} else {
			numFileBlocksS		= 0;
		}

		if (!err) err = GetFileBlocks(NULL, &numFileBlocksS);
		
		if (!err && numFileBlocksS) {
			blockNumP = (Cpm_BlockNum *)TrackNewPtrClear(
				"entry sectors, file blocks", 
				sizeof(Pro_BlockNum) * numFileBlocksS);
			
			if (blockNumP == NULL) err = memFullErr;
			
			if (!err) {
				(**sectorListH).type[Gen_Alloc_FILE].totalS	= numFileBlocksS;
				(**sectorListH).type[Gen_Alloc_FILE].u.byte_blocksA = blockNumP;
				blockNumP = NULL;
			}
		}

		if (!err) {
			numFileBlocksS		= 0;

			err = GetFileBlocks(
				(**sectorListH).type[Gen_Alloc_FILE].u.byte_blocksA,	&numFileBlocksS);
		}
	}
	
	if (!err && !getAsBlocksB) {
		err = i_cDisk.cpm->EntryBlocksToSectors(*sectorListH);
	}
	
	if (err) {
		DisposeEntryAlloc(*sectorListH);
		*sectorListH = NULL;
	}
	
	return err;
}

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