//	CpmStructs.c

#include "stdio.h"
#include "Utils.h"
#include "CpmStructs.h"
#include "IC_ImageTypes.h"
#include "ADFS_LogFile.h"

static	Boolean		Cpm_IsCpmTest(DiskImageRec *imageRec)
{
	Cpm_DirEntry	*dirEntry;
	Cpm_DirEntry	*entryP;
	Boolean			isCpm = TRUE, all_should_eraseB = FALSE;
	short			loop;
	
	dirEntry = (Cpm_DirEntry *)Cpm_GetBlock(imageRec, Cpm_kDirStartBlock);
	
	for (
		loop = 0;
		isCpm && loop < Cpm_kMaxUseableDirEntries;
		loop++
	) {
		entryP = &dirEntry[loop];
		
		if (all_should_eraseB) {
			if (entryP->extentNum != Cpm_kErasedSentinel) {
				isCpm = FALSE;
			}
		} else {
			
			if (entryP->extentNum == Cpm_kErasedSentinel) {
				all_should_eraseB = TRUE;
			} else if (
				entryP->extentNum > loop
				|| entryP->extentNum > Cpm_kMaxExtentNum
				|| entryP->numRecords > Cpm_kRecordsPerExtent
			) {
				isCpm = FALSE;
			}
		}
	}

	if (isCpm) {
		imageRec->osType = FSType_CPM;
	}
	
	return isCpm;														
}


	//	check first entry of every sector (8 sectors == 2 blocks)
/*
	for (
		loop = 0;
		isCpm && (loop * 8) < Cpm_kMaxUseableDirEntries;
		loop++
	) {
		isCpm = (
			GetRboShort(dirEntry->system)	== 0
			&& dirEntry->numRecords > 0 && dirEntry->numRecords <= Cpm_kRecordsPerExtent
		) || (
			dirEntry->userNumber						== Cpm_kErasedSentinel
			&& dirEntry->extentNum						== Cpm_kErasedSentinel
			&& dirEntry->system.low						== Cpm_kErasedSentinel
			&& dirEntry->system.high					== Cpm_kErasedSentinel
		);
		
		dirEntry = &dirEntry[8];
	}
*/
	

static	Boolean		Cpm_TestNibOrder(DiskImageRec *imageRec, FSType assumeOrder, FSType orig)
{
	Boolean		success = FALSE;
	
	AssumeConvertImageType(imageRec, assumeOrder, FSType_CPM);
	
	success = Cpm_IsCpmTest(imageRec);
	
	if (success) {
		ADFS_Log("Assumed it was a CP/M disk in ");
		ADFS_Log_FSType(assumeOrder);
		ADFS_Log(" sector order\n");

		ImageRec_OrigOrder(imageRec)	= assumeOrder;
	} else {
		UnAssumeConvertImageType(imageRec, assumeOrder, orig);
	}
	
	return success;
}

/*
static	Boolean		Cpm_IsCorrectOrder(DiskImageRec *imageRec)
{
	short		sectorS;
	char		charC;
	Boolean		goodB = TRUE;
	
	for (sectorS = 0; goodB && sectorS < 16; sectorS++) {
		if (sectorS < 10) {
			charC = '0' + sectorS;
		} else {
			charC = 'A' + sectorS - 10;
		}
		
		goodB = imageRec->image.gen->track[4].sector[sectorS].byte[0] == charC;
	}
	
	return goodB;
}

Gen_Disk	image_copy;

typedef		Byte				IC_SectorOrderMap[Gen_kSectorsPerTrack];
typedef		IC_SectorOrderMap	IC_SectorOrderTable[FSType_NUMTYPES];

extern	IC_SectorOrderTable		gOrderTable;

static	void		Cpm_FindSectorOrder(DiskImageRec *imageRec)
{
	short		sectorS, rev_sectorS;
	char		charAC[8];
	char		*charP = &charAC[2];
	Boolean		goodB = TRUE;
	
	ConvertImage(imageRec, FSType_GEN);
	ADFS_Log("In CPM order:\n");

	for (sectorS = 0; goodB && sectorS < 16; sectorS++) {
		*charP = imageRec->image.gen->track[4].sector[sectorS].byte[0];
		
		if (*charP >= '0' && *charP <= '9') {
			rev_sectorS = *charP - '0';
		} else if (*charP >= 'A' && *charP <= 'F') {
			rev_sectorS = *charP - 'A' + 10;
		} else {
			ADFS_Log("bad char\n");
			rev_sectorS = 0;
		}
		
		gOrderTable[FSType_CPM][rev_sectorS] = sectorS;
		
		sprintf(charAC, "0x%X", rev_sectorS);
		ADFS_Log(charAC);
		ADFS_Log(", ");
		if (sectorS == 7) {
			ADFS_Log("\n");
		}
	}
	
	ADFS_Log("\nThe table used:\n");
	
	for (sectorS = 0; goodB && sectorS < 16; sectorS++) {

		rev_sectorS = gOrderTable[FSType_CPM][sectorS];

		sprintf(charAC, "0x%X", rev_sectorS);
		ADFS_Log(charAC);
		ADFS_Log(", ");
		if (sectorS == 7) {
			ADFS_Log("\n");
		}
	}

	ConvertImage(imageRec, FSType_CPM);

	ADFS_Log("\nThe new look:\n");

	for (sectorS = 0; goodB && sectorS < 16; sectorS++) {
		*charP = imageRec->image.gen->track[4].sector[sectorS].byte[0];
		
		sprintf(charAC, "0x%c", *charP);
		ADFS_Log(charAC);
		ADFS_Log(", ");
		if (sectorS == 7) {
			ADFS_Log("\n");
		}
	}

	ADFS_Log("\n");
}

	FSType		fsType0, fsType1, fsType2, fsType3;
	long		result;
	short		foundS = 0;
	
	ADFS_Log("Testing for CP/M Order:\n");
	memcpy(&image_copy, imageRec->image.gen, sizeof(Gen_Disk));
		
	for (fsType0 = FSType_GEN; fsType0 < FSType_NUMTYPES; fsType0 = (FSType)(fsType0 + 1)) {
		imageRec->curOrder = fsType0;

		for (fsType1 = FSType_GEN; fsType1 < FSType_NUMTYPES; fsType1 = (FSType)(fsType1 + 1)) {
			for (fsType2 = FSType_GEN; fsType2 < FSType_NUMTYPES; fsType2 = (FSType)(fsType2 + 1)) {
				for (fsType3 = FSType_GEN; fsType3 < FSType_NUMTYPES; fsType3 = (FSType)(fsType3 + 1)) {
					ConvertImage(imageRec, fsType1);
					AssumeConvertImageType(imageRec, fsType2, fsType3);
					if (Cpm_IsCorrectOrder(imageRec)) {
						
						if (
							fsType0 != FSType_PAS
							&& fsType1 != FSType_PAS
							&& fsType2 != FSType_PAS
							&& fsType3 != FSType_PAS
						) {
							ADFS_Log("Assume it's ");
							ADFS_Log_FSType(fsType0);
							ADFS_Log(", convert it to ");
							ADFS_Log_FSType(fsType1);
							ADFS_Log(", assume it's ");
							ADFS_Log_FSType(fsType2);
							ADFS_Log(", convert it to ");
							ADFS_Log_FSType(fsType3);
							ADFS_Log("\n");
						}
					}
					UnAssumeConvertImageType(imageRec, fsType2, fsType1);
					ConvertImage(imageRec, fsType0);

					result = memcmp(&image_copy, imageRec->image.gen, sizeof(Gen_Disk));
					if (result != 0) {
						AlertID("corrupted!", -1);
						return;
					}
				}
			}
		}
	}
	
	AlertID("done", -1);
	return;
	found:
	imageRec->curOrder = FSType_CPM;
}
*/
Boolean		Cpm_IsCpm(DiskImageRec *imageRec)
{
	Boolean			isCpm = FALSE;

	if (IS_ImageRec_IN_MEMORY(imageRec)) {
		isCpm = Cpm_IsCpmTest(imageRec);

		if (isCpm) {
			ADFS_Log("It was already in CP/M order\n");
			ImageRec_OrigOrder(imageRec)	= FSType_CPM;
		} else {
			FSType		origType = imageRec->curOrder;
			
			if (!isCpm) {
				ConvertImage(imageRec, FSType_CPM);

				isCpm = Cpm_IsCpmTest(imageRec);
				
				if (isCpm) {
					ADFS_Log("Knew it was in ");
					ADFS_Log_FSType(ImageRec_OrigOrder(imageRec));
					ADFS_Log(" order, now CP/M order\n");
					
					ASSERT(ImageRec_OrigOrder(imageRec) != FSType_GEN);
					if (ImageRec_OrigOrder(imageRec) == FSType_UNK) {
						ImageRec_OrigOrder(imageRec)	= FSType_CPM;
					}
				} else {
					ConvertImage(imageRec, origType);
				}
			}
			
			if (!isCpm) isCpm = Cpm_TestNibOrder(imageRec, FSType_GEN, origType);
			if (!isCpm) isCpm = Cpm_TestNibOrder(imageRec, FSType_DOS, origType);
			if (!isCpm) isCpm = Cpm_TestNibOrder(imageRec, FSType_PRO, origType);
			if (!isCpm) isCpm = Cpm_TestNibOrder(imageRec, FSType_C2P, origType);
		}
	}
	
	if (!isCpm) {
		ADFS_Log("It was not a CP/M disk\n");
	} else {
		imageRec->curOrder = FSType_CPM;
	}
		
	return isCpm;														
}

static	char	*Cpm_GetFileName(Cpm_DirEntry *entry, char *fileNameBuf)
{
	short	nameIndex, typeIndex;
	
	for (nameIndex = 0; nameIndex < Cpm_kNameLength; nameIndex++) {
		fileNameBuf[nameIndex] = entry->name[nameIndex] & 0x7F;
		if (fileNameBuf[nameIndex] == ' ') {
			break;
		}
	}

	fileNameBuf[nameIndex++] = '.';

	for (typeIndex = 0; typeIndex < Cpm_kTypeLength; typeIndex++, nameIndex++) {
		fileNameBuf[nameIndex] = entry->fileType[typeIndex] & 0x7F;
	}
	
	fileNameBuf[nameIndex] = 0;
	
	return fileNameBuf;
}

static	long	Cpm_GetFileSize(Cpm_DirEntry *entry, long *dirIndex)
{
	long	fileSize = entry->numRecords * Cpm_kBytesPerRecord;
	
	while (entry->numRecords == Cpm_kRecordsPerExtent) {
		(*dirIndex)++;
		entry = &(entry[1]);
		fileSize += entry->numRecords * Cpm_kBytesPerRecord;
	}
	
	return fileSize;
}

static	void	Cpm_GetDiskSizes(DiskImageRec *imageRec, long *bytesUsed, long *bytesFree)
{
	Cpm_DirEntry		*directory, *entry;
	long				dirIndex;
	
	*bytesUsed = 0;
	
	directory = (Cpm_DirEntry *)Cpm_GetBlock(imageRec, Cpm_kDirStartBlock);
	
	for (
		dirIndex = 0;
		dirIndex < Cpm_kMaxUseableDirEntries;
		dirIndex++
	) {
		entry = (Cpm_DirEntry *)&(directory[dirIndex]);
		
		if (entry->userNumber != Cpm_kErasedSentinel) {
			(*bytesUsed) += Cpm_GetFileSize(entry, &dirIndex);
		}
	}
	
	*bytesFree = sizeof(Cpm_Disk) - *bytesUsed;
}

void		Cpm_Catalog(DiskImageRec *imageRec)
{
	Cpm_DirEntry		*directory, *entry;
	char				fileNameBuf[Cpm_kNameLength + Cpm_kTypeLength + 2];
	char				sizeBuf1[10], sizeBuf2[10];
	long				dirIndex;
	long				bytesUsed, bytesFree;
	
	directory = (Cpm_DirEntry *)Cpm_GetBlock(imageRec, Cpm_kDirStartBlock);
	
	printf(
		"\nCP/M Volume\n\n"
		"Name           Size\n\n"
	);

	for (
		dirIndex = 0;
		dirIndex < Cpm_kMaxUseableDirEntries;
		dirIndex++
	) {
		entry = (Cpm_DirEntry *)&(directory[dirIndex]);
		
		if (entry->userNumber != Cpm_kErasedSentinel) {
			printf(
				"%-12s %6s\n", 
				Cpm_GetFileName(entry, fileNameBuf), 
				FormatSize(Cpm_GetFileSize(entry, &dirIndex), sizeBuf1)
			);
		}
	}
	
	Cpm_GetDiskSizes(imageRec, &bytesUsed, &bytesFree);
	
	printf(
		"\nFree: %s  Used: %s\n", 
		FormatSize(bytesFree, sizeBuf1), 
		FormatSize(bytesUsed, sizeBuf2)
	);
}

/*
	MZX
	HZP
	CZM
	COM
	DAT
	BAS
	DOC
	ARK
	sys
*/