/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information.       */
/* Contact email: mac@aarongiles.com                     */
/*********************************************************/

#if THINK_C
#include "THINK.Header"
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif

#include <string.h>

/*
 * AllocatePort()
 *
 *     Purpose: Allocates a port (actually a dialog record) from our global pool
 *      Inputs: none
 *     Returns: a pointer to the memory location
 *
 */
 
CGrafPtr AllocatePort(void)
{
	int i;
	
	for (i = 0; i < kMaxPorts; i++) 
		if (!gPortPool[i].window.port.portBits.baseAddr) {
			gPortPool[i].window.port.portBits.baseAddr = (Ptr)0xffffffff;
			return (CGrafPtr)&gPortPool[i];
		}
	return nil;
}

/*
 * DecallocatePort(thePort)
 *
 *     Purpose: Deallocates a port from our global pool
 *      Inputs: thePort = the port to deallocate
 *     Returns: nothing
 *
 */
 
void DeallocatePort(CGrafPtr thePort)
{
	Ptr theStart = StripAddress(gPortPool);
	Ptr theEnd = theStart + kMaxPorts * sizeof(DialogRecord);
	Ptr theAdr = StripAddress(thePort);
	short i;
	
	if ((theAdr >= theStart) && (theAdr < theEnd))
		for (i = 0; i < sizeof(DialogRecord); i++) *theAdr++ = 0;
}

/*
 * CheckedNewHandle(theSize, canForce)
 *
 *     Purpose: Allocates a handle, either in the application heap or in the system heap
 *      Inputs: theSize = the size of the handle to allocate
 *				canForce = true if we're allowed to delete GWorlds to force memory
 *     Returns: a new handle, or nil if not enough memory
 *
 */
 
Handle CheckedNewHandle(Size theSize, Boolean canForce)
{
	OSErr theErr = noErr;
	Handle theHandle;
	
	if (!IsMemAvailable(theSize) || !(theHandle = NewHandle(theSize))) {
		theHandle = TempNewHandle(theSize, &theErr);
		if (theErr != noErr && canForce) {
			if (!MakeMemAvailable(theSize) || !(theHandle = NewHandle(theSize)))
				return nil;
		}
	}
	return (theErr == noErr) ? theHandle : nil;
}

/*
 * AnyNewHandle(theSize)
 *
 *     Purpose: Allocates a handle, either in the application heap or in the system heap
 *      Inputs: theSize = the size of the handle to allocate
 *     Returns: a new handle, or nil if not enough memory
 *
 */
 
Handle AnyNewHandle(Size theSize)
{
	OSErr theErr = noErr;
	Handle theHandle;
	
	if (!(theHandle = NewHandle(theSize))) theHandle = TempNewHandle(theSize, &theErr);
	return (theErr == noErr) ? theHandle : nil;
}

/*
 * ClearMem(thePtr, theSize)
 *
 *     Purpose: Zeroes out a block of memory
 *      Inputs: thePtr = pointer to the memory
 *				theSize = the size of the block to clear
 *     Returns: nothing
 *
 */
 
void ClearMem(Ptr thePtr, register Size theSize)
{
	register union {
		uchar *cPtr;
		ushort *sPtr;
		ulong *lPtr;
	} p;
	
	p.cPtr = (uchar *)thePtr;
	if (theSize && ((long)p.cPtr & 1)) *p.cPtr++ = 0, theSize--;
	if (theSize && ((long)p.sPtr & 2)) *p.sPtr++ = 0, theSize -= 2;
	while (theSize >= 4) *p.lPtr++ = 0, theSize -= 4;
	if (theSize >= 2) *p.sPtr++ = 0, theSize -= 2;
	if (theSize) *p.cPtr++ = 0;
//	memset(thePtr, 0, theSize);
}

/*
 * GWorldSize(theGWorld)
 *
 *     Purpose: Returns the real size of the given GWorld
 *      Inputs: theGWorld = a pointer to the GWorld
 *     Returns: the size, in bytes
 *
 */
 
long GWorldSize(GWorldPtr theGWorld) 
{
	long theSize = sizeof(CGrafPort) + sizeof(GDevice) + 4096;
	PixMapHandle thePixMap;
	
	if (!theGWorld) return 0;
	thePixMap = GetGWorldPixMap(theGWorld);
	if (thePixMap) {
		if ((*thePixMap)->pmTable) {
			theSize += (sizeof(ColorTable) + sizeof(ColorSpec) * 
					   ((*(*thePixMap)->pmTable)->ctSize + 1));
			theSize += (1L << (*gMainMonitor)->oldres) * (1L << (*gMainMonitor)->oldres) *
					   (1L << (*gMainMonitor)->oldres);
		}
		theSize += (long)((*thePixMap)->rowBytes & 0x3fff) * 
				   (long)Height(&(*thePixMap)->bounds);
	}
	return theSize;
}

/*
 * EstGWorldSize(bounds, depth)
 *
 *     Purpose: Returns an estimate of the GWorld size for a given rectangle and depth
 *      Inputs: bounds = the bounding rectange of the proposed GWorld
 *				depth = the proposed depth
 *     Returns: the size, in bytes
 *
 */
 
long EstGWorldSize(Rect *bounds, short depth) 
{
	long theSize = sizeof(CGrafPort) + sizeof(GDevice) + 16384L;
	
	if (depth <= 8) {
		theSize += sizeof(ColorTable) + sizeof(ColorSpec) * (1 << depth);
		theSize += (1 << (*gMainMonitor)->oldres) * (1 << (*gMainMonitor)->oldres) *
				   (1 << (*gMainMonitor)->oldres);
	}
	theSize += (((long)Width(bounds) * (long)depth) / 8L + 4L) * (long)Height(bounds);
	return theSize;
}

/*
 * IsMemAvailable(request)
 *
 *     Purpose: Determines in a block of memory of the given size is available
 *      Inputs: request = the requested memory size
 *     Returns: true if there's room; false otherwise
 *
 */
 
Boolean IsMemAvailable(long request) 
{
	long total, contig;
	
	if (MaxBlock() > (request + gMemSlop)) return true;
	PurgeSpace(&total, &contig);
	return (request < contig) && ((request + gMemSlop) < total);
}

/*
 * MakeMemAvailable(request)
 *
 *     Purpose: Determines in a block of memory of the given size is available;
 *					if not, we try killing off some GWorlds to do it
 *      Inputs: request = the requested memory size
 *     Returns: true if there's room; false otherwise
 *
 */
 
Boolean MakeMemAvailable(long request) 
{
	Boolean answer;
	
	while (!(answer = IsMemAvailable(request)) && GetMem());
	return answer;
}

/*
 * GetMem()
 *
 *     Purpose: Kills off the GWorld of the most-hidden window
 *      Inputs: none
 *     Returns: true if we found something to kill; false if we're out of options
 *
 */
 
long GetMem(void) 
{
	WindowPtr theWindow, bestWindow = nil;
	ImageHandle theImage, bestImage;
	long theSize;
	
	if (gThePrefs.useBitmaps == ubAlways) return 0;
	for (theWindow = FWFrontWindow(); theWindow; theWindow = NextWindow(theWindow))
		if ((theImage = FindImage(theWindow)) && (*theImage)->gworld) { 
			bestWindow = theWindow;
			bestImage = theImage;
		}
	if (bestWindow == nil) return 0;
	theSize = GWorldSize((*bestImage)->gworld);
	KillGWorld(bestImage);
	return theSize;
}

/*
 * GrowZone(cbNeeded)
 *
 *     Purpose: Called in emergencies; kills off unnecessary GWorlds
 *      Inputs: cbNeeded = the number of bytes we need
 *     Returns: the number of bytes we freed
 *
 */
 
pascal long GrowZone(Size cbNeeded)
{
#if applec
#pragma unused(cbNeeded)
#endif
	return 0;
/*	long theA5 = SetCurrentA5(), val;
	short tmpVal = gThePrefs.useBitmaps;
	
	if (tmpVal == ubAlways) gThePrefs.useBitmaps = ubOptional;
	val = GetMem();
	gThePrefs.useBitmaps = tmpVal;
	theA5 = SetA5(theA5);
	return val;*/
}
