//	MemUtils.c

#include "CDeskTop.h"
#include "string.h"
#include "stdio.h"
#include "ADFS_LogFile.h"
#include "ADFS_Prefs.h"

#include "MemUtils.h"

#ifdef DEBUG
	#define		ENABLE_TRACKING		1
#else
	#define		ENABLE_TRACKING		0
#endif

#define			kMaxStrLen		42

typedef struct {
	char	whatAC[kMaxStrLen];
	void	*dataP;
} TrackNode;

#define			kMaxTrackers	8192

typedef struct {
	long		numTrackedL;
	long		maxTrackedL;
	ulong		totalSizeL;
	ulong		peakSizeL;
	char		typeAC[10];
	TrackNode	tracksA[kMaxTrackers];
} DataTrackerRec;

#if	ENABLE_TRACKING
	DataTrackerRec		gTrackHandles;
	DataTrackerRec		gTrackPtrs;
#endif

static	void	strcpy_what(char *dest, char *src)
{
	ulong	strLen = strlen(src);
	
	if (strLen > (kMaxStrLen - 1)) {
		strLen = kMaxStrLen - 1;
	}
	
	memcpy(dest, src, strLen);
	dest[strLen] = 0;
}

void			StartTracking(void)
{
	#if	ENABLE_TRACKING
		memclr(&gTrackHandles, sizeof(gTrackHandles));
		memclr(&gTrackPtrs, sizeof(gTrackPtrs));
		
		strcpy_what(gTrackHandles.typeAC,	"handle");
		strcpy_what(gTrackPtrs.typeAC,		"pointer");
	#endif
}


#if	ENABLE_TRACKING
static	short		ScanFor(DataTrackerRec *trackRecP, void *scanForThis)
{
	extern		FILE		*gLogfile;
	short		curScanS;
	
	for (curScanS = 0; curScanS < kMaxTrackers; curScanS++) {
		if (trackRecP->tracksA[curScanS].dataP == scanForThis) {
			break;
		}
	}
	
	if (curScanS == kMaxTrackers) {
		curScanS = -1; 
	}
	
	return curScanS;
}

static	void		ReportLost(DataTrackerRec *trackRecP)
{
	short		curScanS;
	char		strAC[256], strAC2[256];
	ulong		sizeL;
	
	sprintf(strAC, 
		"Tracked a maximum of %ld %ss.  Peak useage: %s\n", 
		trackRecP->maxTrackedL, 
		trackRecP->typeAC, 
		FormatSize((ulong)trackRecP->peakSizeL, strAC2));
	ADFS_Log(strAC);

	sprintf(strAC, 
		"Lost %ld %ss totalling %s in size.\n", 
		trackRecP->numTrackedL, 
		trackRecP->typeAC, 
		FormatSize((ulong)trackRecP->totalSizeL, strAC2));
	
	if (trackRecP->numTrackedL) {
		ReportErrorStr(-1, strAC);
	} else {
		ADFS_Log(strAC);
	}

	for (curScanS = 0; curScanS < kMaxTrackers; curScanS++) {
		if (trackRecP->tracksA[curScanS].dataP != NULL) {
			
			if (trackRecP == &gTrackHandles) {
				sizeL = GetHandleSize((Handle)trackRecP->tracksA[curScanS].dataP);
			} else {
				sizeL = GetPtrSize((Ptr)trackRecP->tracksA[curScanS].dataP);
			}
			
			sprintf(strAC, 
				"  Lost a Ò%sÓ %s, that was %s in size.\n", 
				trackRecP->tracksA[curScanS].whatAC, 
				trackRecP->typeAC, 
				FormatSize(sizeL, strAC2));
			
			ADFS_Log(strAC);
		}
	}

	ADFS_Log("\n");
}
#endif

void			ReportTracking(void)
{
	#if	ENABLE_TRACKING
		ADFS_Log("---- Tracking Info ----\n");
		ReportLost(&gTrackHandles);
		ReportLost(&gTrackPtrs);
	#endif
}

static	OSErr	Purge(Boolean criticalB)
{
	return gDesktop->Purge(criticalB);
}


OSErr			TrackSetHandleSize(Handle handleH, ulong bytesL)
{
	OSErr		err = noErr;

	#if	ENABLE_TRACKING
		short		foundS	= ScanFor(&gTrackHandles, handleH);
		ulong		sizeL	= GetHandleSize(handleH);
	
		if (foundS == -1) {
			char	strAC[256];
			char	strAC2[256];
			
			sprintf(strAC, "SetHandleSizing an untracked handle.  Size = %s", 
				FormatSize(sizeL, strAC2));
				
			ReportErrorStr(err = -1, strAC);
		}
	#endif

	if (!err) {
		SetHandleSize(handleH, bytesL);
		err = MemError();
		if (err == memFullErr) {
			err = Purge(FALSE);
			if (!err) {
				SetHandleSize(handleH, bytesL);
				err = MemError();
			}
		}

		if (err == memFullErr) {
			err = Purge(TRUE);
			if (!err) {
				SetHandleSize(handleH, bytesL);
				err = MemError();
			}
		}
		
		#if	ENABLE_TRACKING
			if (!err) {
				char		strAC[256], sizeAC[256], totalAC[256];

				gTrackHandles.totalSizeL += bytesL - sizeL;
				
				if (gTrackHandles.peakSizeL < gTrackHandles.totalSizeL ) {
					gTrackHandles.peakSizeL = gTrackHandles.totalSizeL;
				}
				
				if (gPrefsH && (**gPrefsH).log_memoryB) {
					ADFS_Log("SetHandleSize: ");
					ADFS_Log(gTrackHandles.tracksA[foundS].whatAC);
					
					sprintf(
						strAC, ", size: %s, Num tracked: %ld, total size: %s\n", 
						FormatSize(bytesL, sizeAC), gTrackHandles.numTrackedL, 
						FormatSize(gTrackHandles.totalSizeL, totalAC));

					ADFS_Log(strAC);
				}
			}
		#endif
	}
	
	return err;
}


OSErr			TrackPtrAndHand(Ptr pointerP, Handle handleH, ulong bytesL)
{
	OSErr		err		= noErr;
	
	#if	ENABLE_TRACKING
		short		foundS	= ScanFor(&gTrackHandles, handleH);
		ulong		sizeL	= GetHandleSize(handleH);
	
		if (foundS == -1) {
			char	strAC[256];
			char	strAC2[256];
			
			sprintf(strAC, "PtrAndHanding an untracked handle.  Size = %s", 
				FormatSize(sizeL, strAC2));
				
			ReportErrorStr(err = -1, strAC);
		}
	#endif

	if (!err) {
		err = PtrAndHand(pointerP, handleH, bytesL);
		if (err == memFullErr) {
			err = Purge(FALSE);
			if (!err) err = PtrAndHand(pointerP, handleH, bytesL);
		}

		if (err == memFullErr) {
			err = Purge(TRUE);
			if (!err) err = PtrAndHand(pointerP, handleH, bytesL);
		}
		
		#if	ENABLE_TRACKING
			if (!err) {
				char		strAC[256], sizeAC[256], totalAC[256];

				gTrackHandles.totalSizeL += bytesL;
				
				if (gTrackHandles.peakSizeL < gTrackHandles.totalSizeL ) {
					gTrackHandles.peakSizeL = gTrackHandles.totalSizeL;
				}
				
				if (gPrefsH && (**gPrefsH).log_memoryB) {
					ADFS_Log("PtrAndHand: ");
					ADFS_Log(gTrackHandles.tracksA[foundS].whatAC);
					
					sprintf(
						strAC, ", size: %s, Num tracked: %ld, total size: %s\n", 
						FormatSize(bytesL, sizeAC), gTrackHandles.numTrackedL, 
						FormatSize(gTrackHandles.totalSizeL, totalAC));

					ADFS_Log(strAC);
				}
			}
		#endif
	}
	
	return err;
}

void		TrackHandle(Handle it, char *what, ulong sizeL)
{
	#if	ENABLE_TRACKING
		if (it) {
			short		nextFreeS = ScanFor(&gTrackHandles, NULL);
									
			if (nextFreeS == -1) {
				ReportErrorStr(-1, "ran out of handle tracking space\n");
			} else {
				gTrackHandles.numTrackedL++;

				if (gTrackHandles.maxTrackedL < gTrackHandles.numTrackedL) {
					gTrackHandles.maxTrackedL = gTrackHandles.numTrackedL;
				}
				
				gTrackHandles.totalSizeL += sizeL;
				
				if (gTrackHandles.peakSizeL < gTrackHandles.totalSizeL ) {
					gTrackHandles.peakSizeL = gTrackHandles.totalSizeL;
				}

				gTrackHandles.tracksA[nextFreeS].dataP = it;
				strcpy_what(gTrackHandles.tracksA[nextFreeS].whatAC, what);
				
				if (gPrefsH && (**gPrefsH).log_memoryB) {
					char		strAC[256], sizeAC[256], totalAC[256];
					
					ADFS_Log("NewHandle: ");
					ADFS_Log(gTrackHandles.tracksA[nextFreeS].whatAC);
					
					sprintf(
						strAC, ", size: %s, Num tracked: %ld, total size: %s\n", 
						FormatSize(sizeL, sizeAC), gTrackHandles.numTrackedL, 
						FormatSize(gTrackHandles.totalSizeL, totalAC));

					ADFS_Log(strAC);
				}
			}
		}
	#endif
}

void	TrackDetachResource(char *what, Handle resH)
{
	DetachResource(resH);
	TrackHandle(resH, what, GetHandleSize(resH));
}


Handle			TrackNewHandle(char *what, ulong sizeL)
{
	OSErr		err = noErr;
	Handle		it = NewHandle(sizeL);
	
	if (it == NULL) {
		err = Purge(FALSE);
		if (!err) it = NewHandle(sizeL);
	}

	if (it == NULL) {
		err = Purge(TRUE);
		if (!err) it = NewHandle(sizeL);
	}

	TrackHandle(it, what, sizeL);
	
	return it;
}

Handle			TrackNewHandleClear(char *what, ulong sizeL)
{
	Handle		it = TrackNewHandle(what, sizeL);
	
	if (it) {
		memclr(*it, sizeL);
	}
		
	return it;
}

void			TrackDisposeHandle(Handle it)
{
	#if	ENABLE_TRACKING
		if (!it) {
			ReportErrorStr(-1, "Disposing a NULL Handle, eh?");
		} else {
			short		foundS	= ScanFor(&gTrackHandles, it);
			ulong		sizeL	= GetHandleSize(it);
			
			if (foundS == -1) {
				char	strAC[256];
				char	strAC2[256];
				
				sprintf(strAC, "Disposing an untracked handle.  Size = %s", 
					FormatSize(sizeL, strAC2));
					
				ReportErrorStr(-1, strAC);
			} else {
				gTrackHandles.tracksA[foundS].dataP = NULL;
				
				gTrackHandles.numTrackedL--;
				
				if (sizeL > gTrackHandles.totalSizeL) {
					ReportErrorStr(-1, "Disposing more handle data than we got?");
				}

				gTrackHandles.totalSizeL -= sizeL;
				
				if (gTrackHandles.numTrackedL < 0) {
					ReportErrorStr(-1, "Disposing more handles than we got?");
				}

				if (gPrefsH && (**gPrefsH).log_memoryB) {
					char		strAC[256], sizeAC[256], totalAC[256];
					
					ADFS_Log("DisposeHandle: ");
					ADFS_Log(gTrackHandles.tracksA[foundS].whatAC);
					
					sprintf(
						strAC, ", size: %s, Num tracked: %ld, total size: %s\n", 
						FormatSize(sizeL, sizeAC), 
						gTrackHandles.numTrackedL, 
						FormatSize(gTrackHandles.totalSizeL, totalAC));

					ADFS_Log(strAC);
				}

				gTrackHandles.tracksA[foundS].whatAC[0] = 0;
			}
		}
	#endif

	if (it) {
		DisposeHandle(it);
	}
}

Ptr				TrackNewPtr(char *what, ulong sizeL)
{
	OSErr		err = noErr;
	Ptr			it = NewPtr(sizeL);
	
	if (it == NULL) {
		err = Purge(FALSE);
		if (!err) it = NewPtr(sizeL);
	}

	if (it == NULL) {
		err = Purge(TRUE);
		if (!err) it = NewPtr(sizeL);
	}

	#if	ENABLE_TRACKING
		if (it) {
			short		nextFreeS = ScanFor(&gTrackPtrs, NULL);
			
			if (nextFreeS == -1) {
				ReportErrorStr(-1, "ran out of pointer tracking space\n");
			} else {
				gTrackPtrs.numTrackedL++;

				if (gTrackPtrs.maxTrackedL < gTrackPtrs.numTrackedL) {
					gTrackPtrs.maxTrackedL = gTrackPtrs.numTrackedL;
				}

				gTrackPtrs.totalSizeL += sizeL;

				if (gTrackPtrs.peakSizeL < gTrackPtrs.totalSizeL ) {
					gTrackPtrs.peakSizeL = gTrackPtrs.totalSizeL;
				}
							

				gTrackPtrs.tracksA[nextFreeS].dataP = it;
				strcpy_what(gTrackPtrs.tracksA[nextFreeS].whatAC, what);

				if (gPrefsH && (**gPrefsH).log_memoryB) {
					char		strAC[256], sizeAC[256], totalAC[256];
					
					ADFS_Log("NewPtr: ");
					ADFS_Log(gTrackPtrs.tracksA[nextFreeS].whatAC);
					
					sprintf(
						strAC, ", size: %s, Num tracked: %ld, total size: %s\n", 
						FormatSize(sizeL, sizeAC), gTrackPtrs.numTrackedL, 
						FormatSize(gTrackPtrs.totalSizeL, totalAC));

					ADFS_Log(strAC);
				}
			}
		}
	#endif
	
	return it;
}

Ptr				TrackNewPtrClear(char *what, ulong sizeL)
{
	Ptr		it = TrackNewPtr(what, sizeL);
	
	if (it) {
		memclr(it, sizeL);
	}
	
	return it;
}

void			TrackDisposePtr(Ptr it)
{
	#if	ENABLE_TRACKING
		if (!it) {
			ReportErrorStr(-1, "Disposing a NULL Pointer, eh?");
		} else {
			short		foundS	= ScanFor(&gTrackPtrs, it);
			ulong		sizeL	= GetPtrSize(it);
			
			if (foundS == -1) {
				char	strAC[256];
				char	strAC2[256];
				
				sprintf(strAC, "Disposing an untracked Pointer.  Size = %s\n", 
					FormatSize(sizeL, strAC2));
					
				ReportErrorStr(-1, strAC);
			} else {
				gTrackPtrs.tracksA[foundS].dataP = NULL;

				gTrackPtrs.numTrackedL--;

				if (sizeL > gTrackPtrs.totalSizeL) {
					ReportErrorStr(sizeL, "Disposing more pointer data than we got?");
				}

				gTrackPtrs.totalSizeL -= sizeL;
				
				if (gTrackPtrs.numTrackedL < 0) {
					ReportErrorStr(gTrackPtrs.numTrackedL, "Disposing more Pointers than we got?");
				}

				if (gPrefsH && (**gPrefsH).log_memoryB) {
					char		strAC[256], sizeAC[256], totalAC[256];
					
					ADFS_Log("DisPtr: ");
					ADFS_Log(gTrackPtrs.tracksA[foundS].whatAC);
					
					sprintf(
						strAC, ", size: %s, Num tracked: %ld, total size: %s\n", 
						FormatSize(sizeL, sizeAC), 
						gTrackPtrs.numTrackedL, 
						FormatSize(gTrackPtrs.totalSizeL, totalAC));

					ADFS_Log(strAC);
				}

				gTrackPtrs.tracksA[foundS].whatAC[0] = 0;
			}
		}
	#endif
	
	if (it) {
		DisposePtr(it);
	}
}

void	memfill(void *mem, char value, unsigned long size)
{
	unsigned char*	p = (unsigned char*)mem;
	
	while (size--) {
		*p = value; 
 		++p;
	}
}

void	memclr(void *mem, unsigned long size)
{
	memfill(mem, 0, size);
}

