/*********************************************************/
/* 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

/*
 * Local variables:
 *   lStatFontInfo = the font information for the statistics window font (Geneva 9pt)
 *   lStatWindow = pointer to the statistics window
 *
 */

static RgnHandle gStatRegion = nil;
static Ptr gStatRecord = nil;
static long gStatLength = 0; 

static Rect gStatRect[strFreeMemory - strFileName + 2];
static ImageHandle gLastImage = (ImageHandle)-1;
static WindowPtr gStatWindow = nil;
static TEHandle gCreditsTE = nil;
static FontInfo gStatFontInfo;
static long gCreditsHeight;

/*
 * OpenStats()
 *
 *     Purpose: Creates and initializes the statistics window
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
OSErr OpenStats(void) 
{
	Handle textHandle, stylHandle;
	Rect theRect, mainRect;
	OSErr theErr = noErr;
	LHHandle theLines;
	Point where;
	short i;
	
	if (gStatWindow) {
		if (!WindowVisible(gStatWindow)) FWShowWindow(gStatWindow);
		return noErr;
	}
	if (!(gStatWindow = AllocateWindow())) return gIntError = errNoMemory, memFullErr;
	PushPort();
	MySetPort(nil);
	if (gStatWindow = FWGetNewFloatingWindow(rStatWindow, (Ptr)gStatWindow, (WindowPtr)-1)) {
		MySetPort((CGrafPtr)gStatWindow);
		TextFont(geneva);
		TextSize(9);
		TextMode(srcCopy);
		TextFace(bold);
		GetFontInfo(&gStatFontInfo);
		gStatFontInfo.widMax = StringWidth(gString[strDisplayedColors]) + 8;
		SizeStats(true);
		theRect = gStatWindow->portRect;
		InsetRect(&theRect, 2, 2);
		if (gCreditsTE = TEStylNew(&theRect, &gStatWindow->portRect)) {
			if (textHandle = GetResource('TEXT', rCreditsText)) {
				if (stylHandle = GetResource('styl', rCreditsText)) {
					HLock(textHandle);
					HLock(stylHandle);
					TEStylInsert(*textHandle, GetHandleSize(textHandle),
								(StScrpHandle)stylHandle, gCreditsTE);
					ReleaseResource(stylHandle);
				} else TEDispose(gCreditsTE), gCreditsTE = nil;
				ReleaseResource(textHandle);
			} else TEDispose(gCreditsTE), gCreditsTE = nil;
			if (gCreditsTE) {
				TESetJust(teCenter, gCreditsTE);
				if ((*gCreditsTE)->lineHeight == -1 || (*gCreditsTE)->fontAscent == -1) {
					theLines = (*GetStylHandle(gCreditsTE))->lhTab;
					for (i = gCreditsHeight = 0; i < (*gCreditsTE)->nLines; i++)
						gCreditsHeight += (*theLines)[i].lhHeight;
				} else gCreditsHeight = (*gCreditsTE)->nLines * (*gCreditsTE)->lineHeight;
			}
		}
		SizeStats(gThePrefs.statsZoomed);
		theRect = gStatWindow->portRect;
		OffsetRect(&theRect, -theRect.left, -theRect.top);
		if (gThePrefs.statsBounds.top != gThePrefs.statsBounds.bottom) {
			gThePrefs.statsBounds.right = gThePrefs.statsBounds.left + Width(&theRect);
			gThePrefs.statsBounds.bottom = gThePrefs.statsBounds.top + Height(&theRect);
		}
		GetActiveRect(gMainMonitor, &mainRect);
		where.h = mainRect.left + kWindowBorderWidth;
		where.v = mainRect.bottom - kWindowBorderHeight - theRect.bottom;
		PlaceWindow(gStatWindow, &gThePrefs.statsBounds, where);
		gLastImage = (ImageHandle)-1;
		gThePrefs.statsOpen = true;
	} else gIntError = errNoMemory, theErr = memFullErr, DeallocateWindow(gStatWindow);
	PopPort();
	return theErr;
}

/*
 * CloseStats()
 *
 *     Purpose: Closes the statistics window
 *      Inputs: none
 *     Returns: nothing
 *
 */

void CloseStats(void)
{
	PushPort();
	MySetPort(nil);
	if (gCreditsTE) TEDispose(gCreditsTE), gCreditsTE = nil;
	if (gStatWindow) {
		gThePrefs.statsOpen = false;
		SaveWindowPosition(gStatWindow, &gThePrefs.statsBounds);
		FWCloseWindow(gStatWindow);
		DeallocateWindow(gStatWindow);
	}
	gStatWindow = nil;
	PopPort();
}

/*
 * SizeStats(zoomed)
 *
 *     Purpose: Sets the size of the statistics window appropriately
 *      Inputs: zoomed = true if the window is zoomed to its large size
 *     Returns: nothing
 *
 */
 
void SizeStats(Boolean zoomed)
{
	short line = gStatFontInfo.leading + gStatFontInfo.ascent + gStatFontInfo.descent;
	short width, height;
	
	width = 5 * gStatFontInfo.widMax / 2 + 20;
	height = gStatFontInfo.leading + gStatFontInfo.descent;
	if (zoomed) height += 15 * line;
	else height += line;
	SizeWindow(gStatWindow, width, height, false);
	PushPort();
	MySetPort((CGrafPtr)gStatWindow);
	EraseRect(&gStatWindow->portRect);
	DrawStatWindow();
	PopPort();
}

/*
 * ZoomStats(zoomed)
 *
 *     Purpose: Toggles the zoomed state of the stats window
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
void ZoomStats(void)
{
	gThePrefs.statsZoomed = !gThePrefs.statsZoomed;
	SizeStats(gThePrefs.statsZoomed);
}

/*
 * GetStatWindow()
 *
 *     Purpose: Returns a pointer to the statistics window
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
WindowPtr GetStatWindow(void)
{
	return gStatWindow;
}

/*
 * SetStatistics()
 *
 *     Purpose: Updates the statistics window
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
void SetStatistics(void) 
{
	if (gStatWindow) {
		PushPort();
		MySetPort((CGrafPtr)gStatWindow);
		InvalRect(&gStatWindow->portRect);
		PopPort();
	} 
	if (GetCommentsWindow()) DrawCommentsWindow();
}

/*
 * DrawStatWindow()
 *
 *     Purpose: Draws the statistics window
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
void DrawStatWindow(void) 
{
	static Boolean gCreditsLast = false;
	short vPos = gStatFontInfo.ascent + gStatFontInfo.leading;
	ImageHandle theImage = nil;
	WindowPtr theWindow;
	
	if (!gStatRegion) gStatRegion = NewRgn();
	else SetEmptyRgn(gStatRegion);
	CalcVis((WindowPeek)gStatWindow);
	for (theWindow = GetFirstWindow(); theWindow && !(theImage = FindImage(theWindow)); theWindow = NextWindow(theWindow));
	if (theImage != gLastImage) gLastImage = theImage;
	
	if (theImage && IsAboutBox(theImage)) {
		if (gCreditsTE) {
			TEUpdate(&gStatWindow->portRect, gCreditsTE);
			gCreditsLast = true;
		}
	} else {
		if (gCreditsLast) {
			EraseRect(&gStatWindow->portRect);
			gCreditsLast = false;
		}
		if (gThePrefs.statsZoomed) {
			DrawStatFileName(theImage, &vPos);
			DrawStatImageSize(theImage, &vPos);
			DrawStatImageColors(theImage, &vPos);
			DrawStatImageStatus(theImage, &vPos);
			DrawStatCompression(theImage, &vPos);
			DrawStatFileFormat(theImage, &vPos);
			DrawStatImageLength(theImage, &vPos);
		
			DrawStatDisplayedSize(theImage, &vPos);
			DrawStatDisplayedColors(theImage, &vPos);
			DrawStatDisplayQuality(theImage, &vPos);
			DrawStatDisplayTime(theImage, &vPos);
			DrawStatOffscreen(theImage, &vPos);
			DrawStatFree(theImage, &vPos);
			if (theImage && (gThePrefs.reminderCount == 10000)) gThePrefs.reminderCount = 0;
		} else DrawStatSummary(theImage, &vPos);
	}
	if (GetCommentsWindow() && !gStatRecord) DrawCommentsWindow();
}	

/*
 * DrawStatSummary(theImage, vPos)
 *
 *     Purpose: Draws a single-line summary for given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatSummary(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) {
		AddString(theVal, gString[strSummary1]);
		StuffString(theVal, 1, (*theImage)->file.name);
		StuffNumber(theVal, 2, (GetHandleSize((*theImage)->data) + 1023L) >> 10);
		StuffString(theVal, 3, (*theImage)->format->formatName);
		StuffNumber(theVal, 4, Width(&(*theImage)->crect));
		StuffNumber(theVal, 5, Height(&(*theImage)->crect));
		StuffDepth(theVal, 6, (*theImage)->depth);
		DrawStatItem(*vPos, -1, theVal);
	} else {
		long temp;
		
		AddString(theVal, gString[strFreeMemory1]);
		temp = (FreeMem() + 1023 - gMemSlop) >> 10;
		if (temp < 0) temp = 0;
		StuffNumber(theVal, 1, temp);
		DrawStatItem(*vPos, strFreeMemory, theVal);
	}
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatFileName(theImage, vPos)
 *
 *     Purpose: Draws the file name of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatFileName(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) AddString(theVal, (*theImage)->file.name);
	else if (gThePrefs.reminderCount > 20) gThePrefs.reminderCount = 10000;
	DrawStatItem(*vPos, strFileName, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatImageSize(theImage, vPos)
 *
 *     Purpose: Draws the size of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatImageSize(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) {
		Rect theRect = (*theImage)->grect;
		if (Cropped(theImage)) AddString(theVal, gString[strImageSize2]);
		else AddString(theVal, gString[strImageSize1]);
		StuffNumber(theVal, 1, Width(&(*theImage)->crect));
		StuffNumber(theVal, 2, Height(&(*theImage)->crect));
		StuffNumber(theVal, 3, Width(&theRect));
		StuffNumber(theVal, 4, Height(&theRect));
	}
	DrawStatItem(*vPos, strImageSize, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatImageStatus(theImage, vPos)
 *
 *     Purpose: Draws the status of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatImageStatus(ImageHandle theImage, short *vPos)
{
	Str255 theVal;
	Boolean comma;
	
	theVal[0] = 0;
	if (theImage) {
		comma = false;
		if (Banded(theImage)) {
			AddString(theVal, gString[strBanded]);
			comma = true;
		}
		if (Cropped(theImage)) {
			if (comma) {
				AddChar(theVal, gListSep);
				AddChar(theVal, ' ');
			}
			AddString(theVal, gString[strCropped]);
			comma = true;
		}
		if (Corrupt(theImage)) {
			if (comma) {
				AddChar(theVal, gListSep);
				AddChar(theVal, ' ');
			}
			AddString(theVal, gString[strCorrupt]);
			comma = true;
		}
		if (Aborted(theImage)) {
			if (comma) {
				AddChar(theVal, gListSep);
				AddChar(theVal, ' ');
			}
			AddString(theVal, gString[strAborted]);
			comma = true;
		}
		if (!comma) AddString(theVal, gString[strNormal]);
	}
	DrawStatItem(*vPos, strImageStatus, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatImageColors(theImage, vPos)
 *
 *     Purpose: Draws the depth of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatImageColors(ImageHandle theImage, short *vPos)
{
	Str255 theVal;
	short i;

	theVal[0] = 0;
	if (theImage) {
		i = (*theImage)->depth;
		if ((((*theImage)->depth == 8) && ((*theImage)->compression == kJPEGCompression)) || (i > 32))
			AddString(theVal, gString[strImageColors2]);
		else AddString(theVal, gString[strImageColors1]);
		StuffDepth(theVal, 1, i);
	}	
	DrawStatItem(*vPos, strImageColors, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatCompression(theImage, vPos)
 *
 *     Purpose: Draws the compression type of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatCompression(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) 
		BlockMove((*theImage)->compressionDesc, theVal, *(*theImage)->compressionDesc + 1);
	DrawStatItem(*vPos, strCompression, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatFileFormat(theImage, vPos)
 *
 *     Purpose: Draws the file format of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatFileFormat(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) AddString(theVal, (*theImage)->format->formatName);
	DrawStatItem(*vPos, strFileFormat, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatImageLength(theImage, vPos)
 *
 *     Purpose: Draws the image size of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatImageLength(ImageHandle theImage, short *vPos)
{
	long csize, psize;
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) {
		csize = GetHandleSize((*theImage)->data) << 3;
		if ((*theImage)->compression) {
			psize = ((*theImage)->depth == 32) ? 24 : (*theImage)->depth;
			psize *= (long)Height(&(*theImage)->grect) * (long)Width(&(*theImage)->grect);
			if ((csize * 5L) > psize) {
				AddString(theVal, gString[strImageLength2]);
				if (csize > psize) StuffNumber(theVal, 2, 0);
				else StuffNumber(theVal, 2, 100L * (psize - csize) / psize);
			} else {
				AddString(theVal, gString[strImageLength3]);
				StuffNumber(theVal, 2, psize / csize);
			}
		} else AddString(theVal, gString[strImageLength1]);
		csize = ((csize >> 3) + 1023L) >> 10;
		StuffNumber(theVal, 1, csize);
	}
	DrawStatItem(*vPos, strImageLength, theVal);
	*vPos += (gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent) << 1;
}

/*
 * DrawStatDisplayedSize(theImage, vPos)
 *
 *     Purpose: Draws the displayed size of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatDisplayedSize(ImageHandle theImage, short *vPos)
{
	long scaleX, scaleY;
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) {
		if (!EqualSizeRect(&(*theImage)->wrect, &(*theImage)->crect)) {
			scaleX = 200L * (long)Width(&(*theImage)->wrect) / Width(&(*theImage)->crect);
			scaleX = (scaleX + 1) >> 1;
			scaleY = 200L * (long)Height(&(*theImage)->wrect) / Height(&(*theImage)->crect);
			scaleY = (scaleY + 1) >> 1;
			if (abs(scaleX - scaleY) <= 1) {
				AddString(theVal, gString[strDisplayedSize2]);
				StuffNumber(theVal, 3, (scaleX + scaleY) / 2);
			} else {
				AddString(theVal, gString[strDisplayedSize3]);
				StuffNumber(theVal, 3, scaleX);
				StuffNumber(theVal, 4, scaleY);
			}
		} else AddString(theVal, gString[strDisplayedSize1]);
		StuffNumber(theVal, 1, Width(&(*theImage)->wrect));
		StuffNumber(theVal, 2, Height(&(*theImage)->wrect));
	}
	DrawStatItem(*vPos, strDisplayedSize, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatDisplayedColors(theImage, vPos)
 *
 *     Purpose: Draws the displayed colors of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatDisplayedColors(ImageHandle theImage, short *vPos)
{
	Str255 theVal;
	short depth;

	theVal[0] = 0;
	if (theImage) {
		depth = (*(*theImage)->dmon)->depth;
		if (depth < 4 || depth > 8) {
			if (Dithered(theImage)) AddString(theVal, gString[strDisplayedColors1]);
			else AddString(theVal, gString[strDisplayedColors2]);
			StuffDepth(theVal, 1, depth);
		} else {
			switch ((*theImage)->npalette) {
				case plSys:
					if (Dithered(theImage)) AddString(theVal, gString[strSystemColors1]);
					else AddString(theVal, gString[strSystemColors2]);
					break;
				case plGrey:
					if (Dithered(theImage)) AddString(theVal, gString[strGrayscales1]);
					else AddString(theVal, gString[strGrayscales2]);
					break;
				case plImage:
					if (Dithered(theImage)) AddString(theVal, gString[strImageColorsX1]);
					else AddString(theVal, gString[strImageColorsX2]);
					break;
				case plQuant:
					if (Dithered(theImage)) AddString(theVal, gString[strReducedColors1]);
					else AddString(theVal, gString[strReducedColors2]);
					break;
				default:
					if (Dithered(theImage)) AddString(theVal, gString[strDisplayedColors1]);
					else AddString(theVal, gString[strDisplayedColors2]);
					break;
			}
			StuffDepth(theVal, 1, depth);
		}
	}
	DrawStatItem(*vPos, strDisplayedColors, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}
	
/*
 * DrawStatDisplayQuality(theImage, vPos)
 *
 *     Purpose: Draws the display quality of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatDisplayQuality(ImageHandle theImage, short *vPos)
{
	Str255 theVal;
	short depth;

	theVal[0] = 0;
	if (theImage) {
		depth = (*(*theImage)->dmon)->depth;
		if ((depth > 8) || ((depth == 8) &&
			((*theImage)->npalette != plSys) && ((*theImage)->npalette != plGrey))) {
			if ((*theImage)->depth >= 24) {
				switch ((*theImage)->quality) {
					case iqVHigh: AddString(theVal, gString[strVeryHigh]); break;
					case iqHigh: if (depth < 24) {
									AddString(theVal, gString[strHigh]); break;
								 }
					case iqMedium: AddString(theVal, gString[strNormal]); break;
					default: AddString(theVal, gString[strNA]);
				}
			} else AddString(theVal, gString[strNA]);
		} else AddString(theVal, gString[strNA]);
	}
	DrawStatItem(*vPos, strDisplayQuality, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatDisplayTime(theImage, vPos)
 *
 *     Purpose: Draws the display time of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatDisplayTime(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) {
		if ((*theImage)->dtime) {
			AddString(theVal, gString[strDisplayTime1]);
			StuffNumber(theVal, 1, ((*theImage)->dtime + 50L) / 1000L);
			StuffNumber(theVal, 2, (((*theImage)->dtime + 50L) / 100L) % 10L);
		} else AddString(theVal, gString[strNA]);
	}
	DrawStatItem(*vPos, strDisplayTime, theVal);
	*vPos += gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent;
}

/*
 * DrawStatOffscreen(theImage, vPos)
 *
 *     Purpose: Draws the gworld size of the given image
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatOffscreen(ImageHandle theImage, short *vPos)
{
	Str255 theVal;

	theVal[0] = 0;
	if (theImage) {
		if ((*theImage)->gworld) {
			if (GWOrigSize(theImage)) AddString(theVal, gString[strOffscreenBitmap2]);
			else AddString(theVal, gString[strOffscreenBitmap1]);
			StuffNumber(theVal, 1, (GWorldSize((*theImage)->gworld) + 1023L) >> 10);
		} else AddString(theVal, gString[strNoBitmap]);
	}
	DrawStatItem(*vPos, strOffscreenBitmap, theVal);
	*vPos += (gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent) << 1;
}

/*
 * DrawStatFree(theImage, vPos)
 *
 *     Purpose: Draws the amount of free memory left
 *      Inputs: theImage = pointer to the image record
 *				vPos = pointer to the vertical position of the item
 *     Returns: nothing
 *
 */
 
void DrawStatFree(ImageHandle theImage, short *vPos)
{
#if applec
#pragma unused(theImage)
#endif
	Str255 theVal;
	long temp;

	theVal[0] = 0;
	temp = (FreeMem() + 1023 - gMemSlop) >> 10;
	if (temp < 0) temp = 0;
	AddString(theVal, gString[strFreeMemory1]);
	StuffNumber(theVal, 1, temp);
	DrawStatItem(*vPos, strFreeMemory, theVal);
	*vPos += (gStatFontInfo.ascent + gStatFontInfo.leading + gStatFontInfo.descent) << 1;
}

/*
 * DrawStatItem()
 *
 *     Purpose: Draws a given string in the statistics window, clearing off any old text
 *      Inputs: vPos = the vertical position of the item
 *				item = the item number
 *				theNum = the new value of the item
 *     Returns: nothing
 *
 */
 
void DrawStatItem(short vPos, short item, StringPtr theNum) 
{
	short rectItem = gThePrefs.statsZoomed ? item - strFileName : 0;
	uchar *theName;
	Rect theRect;
	
	gStatRect[rectItem].top = vPos - gStatFontInfo.ascent - gStatFontInfo.leading;
	gStatRect[rectItem].bottom = vPos + gStatFontInfo.descent;
	if (gThePrefs.statsZoomed) {
		TextFace(bold);
		gStatRect[rectItem].left = gStatFontInfo.widMax - StringWidth(gString[item]);
		MoveTo(gStatRect[rectItem].left, vPos);
		if (gStatRecord) BlockMove(&gString[item][1], &gStatRecord[gStatLength], *gString[item]), gStatLength += *gString[item];
		else DrawString(gString[item]);
	} else {
		gStatRect[rectItem].left = 3;
		TextFace(bold);
		MoveTo(gStatRect[rectItem].left, vPos);
		if (item != -1) {
			if (gStatRecord) BlockMove(&gString[item][1], &gStatRecord[gStatLength], *gString[item]), gStatLength += *gString[item];
			else DrawString(gString[item]);
		} else {
			short oldLen = *theNum;
			theName = theNum++;
			*theName = 1;
			while (*theNum++ != ':') (*theName)++;
			*theNum = oldLen - *theName - 1;
			if (gStatRecord) BlockMove(&theName[1], &gStatRecord[gStatLength], *theName), gStatLength += *theName;
			else DrawString(theName);
		}
		if (gStatRecord) gStatRecord[gStatLength++] = ' ';
		else DrawChar(' ');
	}
	if (gThePrefs.statsZoomed) theRect.left = gStatFontInfo.widMax + 3;
	else if (item == -1) theRect.left = 3 + StringWidth(theName) + CharWidth(' ');
	else theRect.left = 3 + StringWidth(gString[item]) + CharWidth(' ');
	theRect.right = gStatWindow->portRect.right;
	theRect.top = vPos - gStatFontInfo.ascent;
	theRect.bottom = vPos + gStatFontInfo.descent;
	if (theNum[0]) {
		TextFace(normal);
		MoveTo(theRect.left, vPos);
		if (gStatRecord) BlockMove(&theNum[1], &gStatRecord[gStatLength], *theNum), gStatLength += *theNum;
		else DrawString(theNum);
		theRect.left += StringWidth(theNum);
	} else if (gThePrefs.reminderCount == 10000) {
		TextFace(normal);
		if (!gStatRecord) EraseRect(&theRect);
		theRect.left += (Width(&theRect) - StringWidth(gString[strNagMessages + item])) >> 1;
		MoveTo(theRect.left, vPos);
		if (gStatRecord) BlockMove(&gString[strNagMessages + item][1], &gStatRecord[gStatLength], *gString[strNagMessages + item]),
					gStatLength += *gString[strNagMessages + item];
		else DrawString(gString[strNagMessages + item]);
		theRect.left = theRect.right;
	} else theRect.left = gStatFontInfo.widMax;
	gStatRect[rectItem].right = theRect.left;
	if (!gStatRecord) EraseRect(&theRect);
	if (gStatRecord) gStatRecord[gStatLength++] = '\r';
	if (gStatRegion) {
		RgnHandle tempRgn = NewRgn();
		if (tempRgn) {
			RectRgn(tempRgn, &gStatRect[rectItem]);
			UnionRgn(gStatRegion, tempRgn, gStatRegion);
			DisposeRgn(tempRgn);
		}
	}
}

extern void IdleCredits(void)
{
	ImageHandle theImage = nil;
	WindowPtr theWindow;

	if (!gStatWindow) return;
	for (theWindow = GetFirstWindow(); theWindow && !(theImage = FindImage(theWindow));
		 theWindow = NextWindow(theWindow));
	if (theImage && IsAboutBox(theImage) && gCreditsTE) {
		PushPort();
		MySetPort((CGrafPtr)gStatWindow);
		CalcVis((WindowPeek)gStatWindow);
		if (((*gCreditsTE)->viewRect.top - (*gCreditsTE)->destRect.top) >= gCreditsHeight)
				TEScroll(0, (*gCreditsTE)->viewRect.top - (*gCreditsTE)->destRect.top, gCreditsTE);
			else TEScroll(0, -1, gCreditsTE);
		PopPort();
	}
}

extern void HandleStatClick(EventRecord *theEvent)
{
	Point localPt = theEvent->where;
	
	PushPort();
	MySetPort((CGrafPtr)gStatWindow);
	GlobalToLocal(&localPt);
	PopPort();
	if (gStatRegion && PtInRgn(localPt, gStatRegion) && gDragMgrPresent && WaitMouseMoved(theEvent->where)) {
		RgnHandle theRgn = NewRgn(), tempRgn = NewRgn();
		DragReference theDrag;
		char statText[1024];
		OSErr theErr;
		
		if (theRgn && tempRgn) {
			theErr = NewDrag(&theDrag);
			if (theErr == noErr) {
				gStatRecord = statText;
				gStatLength = 0;
				PushPort();
				MySetPort((CGrafPtr)gStatWindow);
				DrawStatWindow();
				PopPort();
				gStatRecord = nil;
				AddDragItemFlavor(theDrag, (ItemReference)statText, 'TEXT', statText, gStatLength, 0);
				CopyRgn(gStatRegion, theRgn);
				GlobalRgn(theRgn, gStatWindow);
				CopyRgn(theRgn, tempRgn);
				InsetRgn(tempRgn, 1, 1);
				DiffRgn(theRgn, tempRgn, theRgn);
				SetDragItemBounds(theDrag, (ItemReference)statText, &(*theRgn)->rgnBBox);
				SetCursor(&qd.arrow);
				theErr = TrackDrag(theDrag, theEvent, theRgn);
				DisposeDrag(theDrag);
			}
		}
		if (theRgn) DisposeRgn(theRgn);
		if (tempRgn) DisposeRgn(tempRgn);
	} else if (FrontWindow() != gStatWindow) SendMoveToFront(gStatWindow);
}

/*
 * DoStatsHelp(globalPt)
 *
 *     Purpose: Pops up help for the statistics windoid; needed because it should be displayed even
 *					if it's not "active"
 *      Inputs: globalPt = point, in global coordinates, where the mouse is
 *     Returns: nothing
 *
 */
 
void DoStatsHelp(Point globalPt)
{
	static Boolean gLastWasGeneric = false;
	Boolean balloonUp = HMIsBalloon();
	HMMessageRecord theRecord;
	short index = 1, line;
	ImageHandle theImage;
	Point tip = { 0, 0 };
	WindowPtr theWindow;
	Rect aRect;
	
	if ((balloonUp && !gLastWasGeneric) || gInBackground) return;
	PushPort();
	MySetPort((CGrafPtr)gStatWindow);
	GlobalToLocal(&globalPt);
	for (theWindow = GetFirstWindow(); theWindow && !(theImage = FindImage(theWindow));
		 theWindow = NextWindow(theWindow));
	if (theImage && IsAboutBox(theImage)) index = 2;
	else if (gThePrefs.statsZoomed) {
		index = 3;
		for (line = 0; line <= (strFreeMemory - strFileName); line++)
			if (PtInRect(globalPt, &gStatRect[line])) {
				index = line + 4;
				break;
			}
	} else index = 1;
	if (gLastWasGeneric && index <= 3 && balloonUp) {
		PopPort();
		return;
	}
	if (gLastWasGeneric = (index <= 3)) aRect = gStatWindow->portRect;
	else aRect = gStatRect[line];
	if (balloonUp) HMRemoveBalloon();
	if (HMExtractHelpMsg(kHMRectListResType, rStatWindow, index, kHMEnabledItem, &theRecord) == noErr) {
		tip.h = aRect.right;
		tip.v = aRect.top + Height(&aRect) / 2;
		LocalToGlobal(&tip);
		GlobalRect(&aRect, gStatWindow);
		HMShowBalloon(&theRecord, tip, &aRect, nil, 0, 0, kHMRegularWindow);
	}
	PopPort();
}
