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

//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================

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

//=====================================================================================
// Includes specific to this module
//=====================================================================================

#include "JPEGViewEventHandlers.h"

//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================

static OSErr DrawToDummyPort(ImageHandle theImage, Rect *srcRect, Rect *dstRect);
static PicHandle MyOpenCPicture(Rect *dstRect, ImageHandle theImage);
static Boolean WasDesktop(FSSpec *spec, Boolean recursive, ICMProgressProcRecordPtr theProg,
			OSErr *theErr);

//=====================================================================================
// pascal OSErr HandleQuantize(AppleEvent *theEvent, AppleEvent *reply, long refCon)
//=====================================================================================
// Handles the quantize image AppleEvent, from the JPEGView suite.
//=====================================================================================

extern pascal OSErr HandleQuantize(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(refCon)
#endif
	ObjectTokenHandle theToken;
	PaletteHandle thePalette;
	CTabHandle theColors;
	ImageHandle theImage;
	OSErr theErr = noErr;
	AEDesc theObject;
	long numColors;
	
	AEBegin();
	theErr = AEExtractObject(theEvent, keyDirectObject, &theObject);
	if (theErr == noErr) {
		theErr = AEExtractLong(theEvent, keyColorCount, &numColors);
		if (theErr == noErr) {
			theToken = (ObjectTokenHandle)theObject.dataHandle;
			if (theObject.descriptorType == cImage) {
				if ((*theToken)->count == 1) {
					theImage = FindImage((WindowPtr)(*theToken)->object[0]);
					StartSpinning();
					theErr = DoQuantize(theImage, numColors, &thePalette, nil);
					StopSpinning(&qd.arrow);
					if (theErr == noErr) {
						if (theColors = (CTabHandle)NewHandle(4)) {
							Palette2CTab(thePalette, theColors);
							HLock((Handle)theColors);
							if (reply->dataHandle)
								theErr = AEPutParamPtr(reply, keyAEResult, typeColorTable, (void *)*theColors, GetHandleSize((Handle)theColors));
							DisposeHandle((Handle)theColors);
						} else theErr = memFullErr;
						DisposePalette(thePalette);
					}
				} else theErr = errAENotASingleObject;
			} else theErr = errAECantHandleClass;
		}
		AEDisposeDesc(&theObject);
	}
	AEEnd();
	return theErr;
}

//=====================================================================================
// pascal OSErr HandleSlideShow(AppleEvent *theEvent, AppleEvent *reply, long refCon)
//=====================================================================================
// Handles the JPEGView slide show AppleEvent, from the JPEGView suite.
//=====================================================================================

extern pascal OSErr HandleSlideShow(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(reply, refCon)
#endif
	long theLong, theCount, theIndex, actualSize;
	DescType theType, theKey;
	NestedProgress theProc = { { nil, 0 }, 0, 0L, 0L, false };
	ICMProgressProcRecordPtr progPtr = (ICMProgressProcRecordPtr)&theProc;
	OSErr theErr = noErr;
	Boolean theBoolean;
	FSSpec theFile;
	AEDesc theList;
	
	theProc.prog.progressProc = gGenericProgress;
	theProc.prog.progressRefCon = (long)&theProc;
	if (gSlideShow) return noErr;
	AEBegin();
	theErr = AEExtractLong(theEvent, keyDelay, &theLong);
	if (theErr == noErr) (*gSlideOptions)->delay = theLong;
	theErr = AEExtractBoolean(theEvent, keyFilenames, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->fileNames = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyFromDisk, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->fromMemory = !theBoolean;
	theErr = AEExtractBoolean(theEvent, keyIgnoreErrors, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->noErrors = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyImportFiles, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->allFiles = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyRandom, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->random = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyRepeat, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->repeat = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyScanRecursive, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->recursive = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyUserControl, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->userControl = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyOffscreen, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->offscreen = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyAutoComments, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->autoComments = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyHideWindoids, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->hideWindoids = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyHideControls, &theBoolean);
	if (theErr == noErr) (*gSlideOptions)->hideControls = theBoolean;
	theErr = AEExtractBoolean(theEvent, keyScreenSaver, &theBoolean);
	if (theErr == noErr) gScreenSaver = theBoolean;
	else gScreenSaver = false;
	if (gScreenSaver) gScreenSaverPoint = GlobalMouse();
	theErr = AEGetParamDesc(theEvent, keyDirectObject, typeAEList, &theList);
	if (theErr == noErr) {
		if (!(*gSlideOptions)->fromMemory) {
			StartSpinning();
			theErr = AECountItems(&theList, &theCount);
			if (theErr == noErr) {
				ParamText(gString[strScanningDirectory], gNullString, gNullString, gNullString);
				if (progPtr)
					CallICMProgressProc(progPtr->progressProc, codecProgressOpen, kIndefProgress, progPtr->progressRefCon);
				for (theIndex = 1; theIndex <= theCount; theIndex++) {
					if (theErr != noErr) break;
					theErr = AEGetNthPtr(&theList, theIndex, typeFSS, &theKey, &theType, (void *)&theFile, sizeof(FSSpec), &actualSize);
					if (theErr == noErr) {
						if (gScreenSaver) gSlideShow = true;
						if (!WasDesktop(&theFile, (*gSlideOptions)->recursive, progPtr, &theErr))
							theErr = AddSlideDir(&theFile, (*gSlideOptions)->recursive, progPtr, nil);
						if (gScreenSaver) gSlideShow = false;
					} else theErr = errAEBadListItem;
				}
				if (progPtr)
					CallICMProgressProc(progPtr->progressProc, codecProgressClose, kIndefProgress, progPtr->progressRefCon);
			} else theErr = errAEWrongDataType;
		}
		if (theErr == noErr) {
			InitSlideShow();
			gIntError = noErr;
		} else CleanUpSlideShow();
		AEDisposeDesc(&theList);
		StopSpinning(&qd.arrow);
	}
	AEEnd();
	return theErr;
}

//=====================================================================================
// pascal OSErr HandleCopy(AppleEvent *theEvent, AppleEvent *reply, long refCon)
//=====================================================================================
// Handles the Copy AppleEvent, from the Miscellaneous Events suite.
//=====================================================================================

extern pascal OSErr HandleCopy(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(theEvent, reply, refCon)
#endif
	OSErr theErr;
	
	AEBegin();
	theErr = DoCopy();
	AEEnd();
	return theErr;
}
	
extern OSErr DoCopy(void)
{
	ImageHandle theImage = FrontImage();
	Boolean dontDispose = false;
	OSErr theErr = noErr;
	PicHandle thePict;
	
	if (theImage) {
		theErr = GetSelectedPICT(theImage, &thePict);
		if (theErr == noErr) {
			if (!thePict) {
				thePict = (PicHandle)(*theImage)->data;
				dontDispose = true;
			}
			ZeroScrap();
			UnloadScrap();
			HLock((Handle)thePict);
			PutScrap(GetHandleSize((Handle)thePict), kPICTType, StripAddress((Ptr)*thePict));
			if (!dontDispose) DisposeHandle((Handle)thePict);
		} else if (theErr == codecAbortErr) theErr = noErr;
	} else theErr = errAEEventFailed;
	return theErr;
}

extern OSErr GetSelectedPICT(ImageHandle theImage, PicHandle *thePicture)
{
	Rect srcRect, dstRect;
	OSErr theErr = noErr;
	CGrafPort dummyPort;
	
	if (AntsHaveSelection((*theImage)->ants)) SelRectToCropRect(theImage, &srcRect);
	else srcRect = (*theImage)->crect;
	if (EqualRect(&srcRect, &(*theImage)->grect) && (*theImage)->format->inType == kPICTType) {
		*thePicture = nil;
		return noErr;
	}
	dstRect = srcRect;
	OffsetRect(&dstRect, -dstRect.left, -dstRect.top);
	OpenCPort(&dummyPort);
	RectRgn(dummyPort.visRgn, &dstRect);
	ClipRect(&dstRect);
	PushPort();
	MySetPort(&dummyPort);
	*thePicture = MyOpenCPicture(&dstRect, theImage);
	if (*thePicture) {
		theErr = DrawToDummyPort(theImage, &srcRect, &dstRect);
		ClosePicture();
		if (!Width(&(**thePicture)->picFrame) && !Height(&(**thePicture)->picFrame)) theErr = memFullErr;
		if (theErr != noErr) DisposeHandle((Handle)*thePicture), *thePicture = nil;
	} else theErr = memFullErr;
	PopPort();
	CloseCPort(&dummyPort);
	return theErr;
}

//=====================================================================================
// PicHandle MyOpenCPicture(Rect *dstRect, ImageHandle theImage)
//=====================================================================================
// Opens up a color picture, in preparation for copying.  We use a simple algorithm to
// figure out whether to use application or temporary memory, in order to maximize the
// likelihood that we'll be able to succeed.
//=====================================================================================

static PicHandle MyOpenCPicture(Rect *dstRect, ImageHandle theImage)
{
	Size estSize = (((long)Height(&(*theImage)->grect) * (long)Width(&(*theImage)->grect) * (long)(*theImage)->depth) >> 3) * 3L / 4L;
	Size grow, tempMax = TempMaxMem(&grow), appMax = MaxMem(&grow);
	THz oldZone = GetZone(), tempZone;
	Boolean inTempMem = true;
	OpenCPicParams theParams;
	OSErr theErr = noErr;
	PicHandle thePict;

	// set up the OpenCPicture parameters
	theParams.srcRect = *dstRect;
	theParams.hRes = theParams.vRes = 0x480000L;
	theParams.version = -2;
	theParams.reserved1 = theParams.reserved2 = 0;

	// allocate a handle in temp memory, and retrieve the temporary zone from that
	thePict = (PicHandle)TempNewHandle(4, &theErr);
	if (thePict) {
		tempZone = HandleZone((Handle)thePict);
		DisposeHandle((Handle)thePict);
	}

	// some simple logic to determine if we should use app or temp memory
	if (!HasVirtualMemory() && (estSize < appMax || estSize > tempMax)) inTempMem = false;
	
	// open the picture
	if (inTempMem) SetZone(tempZone);
	thePict = OpenCPicture(&theParams);
	SetZone(oldZone);
	return thePict;
}

//=====================================================================================
// OSErr DrawToDummyPort(ImageHandle theImage, Rect *srcRect, Rect *dstRect)
//=====================================================================================
// Actually does the drawing of the image to the dummy port.  Of course, the dummy port
// has been set up to bottleneck all the drawing into a picture, so nothing actually
// gets drawn.
//=====================================================================================

static OSErr DrawToDummyPort(ImageHandle theImage, Rect *srcRect, Rect *dstRect)
{
	NestedProgress progProc = { { nil, 0 }, 0, 0L, 0x10000L, false };
	short oldBackgroundTime = gThePrefs.backgroundTime;
	Rect boundsRect = (*theImage)->grect;
	JVDrawParamsHandle theParams;
	OSErr theErr = noErr;
	
	progProc.prog.progressProc = gGenericProgress;
	
	// hack: Finder doesn't seem to like it if we give any apps time during
	// the drag process, so we force 0 sleep time and checking with
	// EventAvail
	gThePrefs.backgroundTime = bgMinimal;

	// allocate and initialize the drawing parameters
	if (theParams = NewDrawParams(&boundsRect, iqMedium, false, &progProc, (*theImage)->privateData)) {
		SetUpDrawPort(theParams, kOnscreenPort, (CGrafPtr)qd.thePort, srcRect, dstRect, false);

		// preflight the drawing here and save the port
		KeepSpinning();
		theErr = PreflightDrawing(theParams);
		PushPort();
		if (theErr == noErr) {

			// point to the dummy drawing port and start the inner progress proc
			MySetPort((*theParams)->dummy);
			UpdateParamText(gString[strCopying], gNullString, gNullString, (*theImage)->file.name);
			CallICMProgressProc((*theParams)->progress.prog.progressProc, codecProgressOpen, 0L,
						(*theParams)->progress.prog.progressRefCon);
			
			// now do the drawing, either from the offscreen GWorld...
			if ((*theImage)->gworld && GWOrigSize(theImage))
				CopyGWorldToGWorld((*theImage)->gworld, (*theParams)->dummy, &(*theImage)->gworld->portRect,
							&(*theImage)->gworld->portRect, srcCopy, nil);

			// or from the image directly
			else theErr = (*theImage)->format->draw((*theImage)->data, theParams);

			// clean up our drawing, checking for missed aborts
			if (theErr == noErr) theErr = PostflightDrawing(theParams);
			else PostflightDrawing(theParams);

			// if we were aborted, tell the printer so, and close the inner prog.
			CallICMProgressProc((*theParams)->progress.prog.progressProc, codecProgressClose, 0L,
						(*theParams)->progress.prog.progressRefCon);
			KeepSpinning();
		}
		PopPort();
		DisposeHandle((Handle)theParams);
	}
	gThePrefs.backgroundTime = oldBackgroundTime;
	return theErr;
}

extern OSErr DoSlideShow(void)
{
	NestedProgress theProg = { { nil, 0 }, 0, 0L, 0L, false };
	ICMProgressProcRecordPtr progPtr = (ICMProgressProcRecordPtr)&theProg;
	OSErr theErr = noErr;
	FSSpec theSpec;

	if (!(*gSlideOptions)->fromMemory) {
		theProg.prog.progressProc = gGenericProgress;
		theProg.prog.progressRefCon = (long)&theProg;
		UpdateParamText(gString[strScanningDirectory], gNullString, gNullString, gNullString);
		CallICMProgressProc(theProg.prog.progressProc, codecProgressOpen, kIndefProgress, theProg.prog.progressRefCon);
		theErr = GetSlideSpec(&theSpec);
		if (theErr == noErr)
			if (!WasDesktop(&theSpec, (*gSlideOptions)->recursive, progPtr, &theErr))
				theErr = AddSlideDir(&theSpec, (*gSlideOptions)->recursive, progPtr, nil);
		CallICMProgressProc(theProg.prog.progressProc, codecProgressClose, kIndefProgress, theProg.prog.progressRefCon);
	}
	if (theErr == noErr) InitSlideShow();
	else {
		CleanUpSlideShow();
		if (theErr == noErr) MinorError(errNoMemory);
	}
	return theErr;
}

static Boolean WasDesktop(FSSpec *spec, Boolean recursive, ICMProgressProcRecordPtr theProg,
			OSErr *theErr)
{
	HParamBlockRec params;
	FSSpec theSpec;
	OSErr err;
	
	err = GetDeskFolderSpec(&theSpec, spec->vRefNum);
	if (err != noErr || !SameFile(&theSpec, spec)) return false;
	params.volumeParam.ioVolIndex = 1;
	params.volumeParam.ioCompletion = nil;
	params.volumeParam.ioNamePtr = theSpec.name;
	while (PBHGetVInfo(&params, false) == noErr && *theErr == noErr) {
		if (recursive) err = FSMakeFSSpec(params.ioParam.ioVRefNum, 2, "\p", &theSpec);
		else err = GetDeskFolderSpec(&theSpec, params.volumeParam.ioVRefNum);
		if (err == noErr) err = AddSlideDir(&theSpec, recursive, theProg, nil);
		if (err == memFullErr || err == codecAbortErr) *theErr = err;
		params.volumeParam.ioVolIndex++;
	}
}
