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

static ImageHandle ImageFromSpec(FSSpec *theFile);
static OSErr DrawToPrinter(ImageHandle theImage, NestedProgressPtr progProc);
static OSErr GetPrintParams(void);
static pascal void PrintIdleProc(void);

static short gOurResFile;
static PrIdleUPP gPrintIdle = nil;
static JVDrawParamsHandle gPrintParams;

/*
 * HandleOpenApplication(theEvent, reply, refCon)
 *
 *     Purpose: AppleEvent handler for the open application event; does one of four
 *					tasks upon startup, as specified in the preferences
 *      Inputs: theEvent = the AppleEvent record
 *				reply = the AppleEvent reply
 *				refCon = our personal work variable
 *     Returns: an OSErr with the overall result of the operation (i.e., noErr)
 *
 */

pascal OSErr HandleOpenApplication(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(theEvent, reply, refCon)
#endif
	WindowPtr front = FWFrontWindow();
	OSErr theErr;
	
	if (gInBackground) return noErr;
	if (front && (GetWRefCon(front) == kHelpWindowID)) return noErr;
	switch (gThePrefs.startWith) {
		case swAutoSlide:
			theErr = DoSlideShow();
			break;
		case swSlide:
			HandleMenuChoice(MakeMenuSelection(rFileMenu, fileSlideItem));
			break;
		case swOpen:
			HandleMenuChoice(MakeMenuSelection(rFileMenu, fileOpenItem));
			break;
		case swNone:
			break;
	}
	return noErr;
}

/*
 * GetOpenDefaults(openScreen, openFull, openPalette, drawQuality)
 *
 *     Purpose: Translates the current prefs into enumerated parameters for opening
 *      Inputs: openScreen = pointer to DescType to hold enumerated open screen
 *      		openFull = pointer to DescType to hold enumerated full screen mode
 *      		openPalette = pointer to DescType to hold enumerated palette
 *      		drawQuality = pointer to DescType to hold enumerated drawing quality
 *     Returns: nothing
 *
 */
 
void GetOpenDefaults(DescType *openScreen, DescType *openFull, DescType *openPalette, 
	DescType *drawQuality)
{
	switch (gThePrefs.openScreen) {
		case osDeepest: *openScreen = (gThePrefs.pickColor) ? kAEOSDeepestColor : kAEOSDeepestGray; break;
		case osLargest: *openScreen = (gThePrefs.pickColor) ? kAEOSLargestColor : kAEOSLargestGray; break;
		case osMain: *openScreen = kAEOSMain; break;
	}
	switch (gThePrefs.openFull) {
		case ofNoMatterWhat: *openFull = kAEOFAlways; break;
		case ofAlways: *openFull = kAEOFAlwaysIfLarge; break;
		case ofAsk: *openFull = kAEOFAskIfLarge; break;
		case ofNever: *openFull = kAEOFNever; break;
	}
	switch (gThePrefs.autoQuant) {
		case aqAlways: *openPalette = (gThePrefs.imagePalette) ? kAEOPQuantImageScrn :
																kAEOPQuantScrn; break;
		case aqOnly: *openPalette = (gThePrefs.imagePalette) ? kAEOPImageQuantScrn :
															  kAEOPQuantScrn; break;
		case aqNever: *openPalette = (gThePrefs.imagePalette) ? kAEOPImageScrn : 
															   kAEOPScrn; break;
	}
	switch (gThePrefs.imageQuality) {
		case iqVHigh: *drawQuality = kAEDQVeryHigh; break;
		case iqHigh: *drawQuality = kAEDQHigh; break;
		case iqMedium: *drawQuality = kAEDQNormal; break;
	}
}

/*
 * HandleOpenDocuments(theEvent, reply, refCon)
 *
 *     Purpose: AppleEvent handler for the open document event; accepts the input files
 *					one at a time and opens them in succession by calling OpenDocument
 *      Inputs: theEvent = the AppleEvent record
 *				reply = the AppleEvent reply
 *				refCon = our personal work variable
 *     Returns: an OSErr with the overall result of the operation (i.e., noErr)
 *
 */

pascal OSErr HandleOpenDocuments(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(reply, refCon)
#endif
	Boolean autoExpand, openVisible, openBitmaps, autoComments, fixTypes, fixCreator;
	DescType returnedType, openScreen, openFull, openPalette, drawQuality;
	long theCount, theIndex;
	OSErr theErr = noErr;
	AEDescList theList;
	AEKeyword theKey;
	Boolean noErrors;
	Size actualSize;
	FSSpec theFile;
	FInfo theInfo;
	
	AEBegin();
	theErr = AEGetParamDesc(theEvent, keyDirectObject, typeWildCard, &theList);
	if ((theErr == noErr) && (theList.descriptorType != typeObjectSpecifier)) {
		GetOpenDefaults(&openScreen, &openFull, &openPalette, &drawQuality);
		theErr = AEExtractEnumerated(theEvent, keyOpenScreen, &returnedType);
		if (theErr == noErr) openScreen = returnedType;
		theErr = AEExtractEnumerated(theEvent, keyOpenFull, &returnedType);
		if (theErr == noErr) openFull = returnedType;
		theErr = AEExtractBoolean(theEvent, keyAutoExpand, &autoExpand);
		if (theErr != noErr) autoExpand = gThePrefs.expandSmall;
		theErr = AEExtractBoolean(theEvent, keyVisible, &openVisible);
		if (theErr != noErr) openVisible = true;
		theErr = AEExtractEnumerated(theEvent, keyOpenPalette, &returnedType);
		if (theErr == noErr) openPalette = returnedType;
		theErr = AEExtractEnumerated(theEvent, keyOpenQuality, &returnedType);
		if (theErr == noErr) drawQuality = returnedType;
		theErr = AEExtractBoolean(theEvent, keyOpenBitmaps, &openBitmaps);
		if (theErr != noErr) openBitmaps = (gThePrefs.useBitmaps == ubAlways);
		theErr = AEExtractBoolean(theEvent, keyAutoComments, &autoComments);
		if (theErr != noErr) autoComments = gThePrefs.autoComments;
		theErr = AEExtractBoolean(theEvent, keyFixTypes, &fixTypes);
		if (theErr != noErr) fixTypes = gThePrefs.autoFix;
		theErr = AEExtractBoolean(theEvent, keyChangeCreator, &fixCreator);
		if (theErr != noErr) fixCreator = gThePrefs.changeCreator;
		theErr = AEExtractBoolean(theEvent, keyNoErrors, &noErrors);
		if (theErr != noErr) noErrors = false;
		DisableMenus();
		if (gThePrefs.onlyOne && !gSlideShow)
			while (gImageRoot) SendCloseWindow((*LastImage())->window);
		theErr = AECountItems(&theList, &theCount);
		if (gThePrefs.onlyOne) theCount = 1;
		if (theErr == noErr) {
			for (theIndex = 1; theIndex <= theCount; theIndex++) {
				theErr = AEGetNthPtr(&theList, theIndex, typeFSS, &theKey, 
						 &returnedType, (void *)&theFile, sizeof(FSSpec), &actualSize);
				if (theErr == noErr) {
					if (FSpGetFInfo(&theFile, &theInfo) != noErr || theInfo.fdType == kPrefsType || theInfo.fdType == kHelpType)
						continue;
					StartSpinning();
					theErr = DoOpenDocument(&theFile, theIndex == 1, openScreen, openFull,
							 autoExpand, openVisible, openPalette, drawQuality, openBitmaps,
							 autoComments, fixTypes, fixCreator);
					if (!noErrors) HandleAEError(theErr, errNoLoadMemory, theFile.name);
				} else {
					theErr = errAEBadListItem;
					break;
				}
			}
		} else theErr = errAEWrongDataType;
		AEDisposeDesc(&theList);
		StopSpinning(&qd.arrow);
	}
	EnableMenus();
	AEEnd();
	return theErr;
}

pascal OSErr HandlePrintDocuments(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(reply, refCon)
#endif
	OSErr theErr = noErr;
	ImageHandle theImage;
	AEDescList theList;
	
	AEBegin();
	theErr = AEGetParamDesc(theEvent, keyDirectObject, typeWildCard, &theList);
	if (theErr == noErr) {
		DisableMenus();
		StartSpinning();
		if (theList.descriptorType != typeObjectSpecifier) {
			long theCount, theIndex;
			DescType returnedType;
			AEKeyword theKey;
			Size actualSize;
			FSSpec theFile;
			
			theErr = AECountItems(&theList, &theCount);
			if (theErr == noErr) {
				for (theIndex = 1; theIndex <= theCount; theIndex++) {
					theErr = AEGetNthPtr(&theList, theIndex, typeFSS, &theKey, 
							 &returnedType, (void *)&theFile, sizeof(FSSpec), &actualSize);
					if (theErr == noErr) {
						theErr = DoOpenDocument(&theFile, false, kAEOSMain, kAEOFNever,
								 false, false, kAEOPScrn, kAEDQNormal, false,
								 false, false, false);
						if (theErr != noErr) 
							HandleAEError(theErr, errNoPrintMemory, theFile.name);
						else theImage = ImageFromSpec(&theFile);
						if (theImage) {
							theErr = DoPrintDocument(theImage, theIndex == 1);
							if (theErr != noErr) {
								if (theErr == codecAbortErr) {
									theErr = noErr;
									break;
								}
								HandleAEError(theErr, errNoPrintMemory, theFile.name);
							}
							DoCloseWindow((*theImage)->window, false);
						}
					} else {
						theErr = errAEBadListItem;
						break;
					}
				}
			} else theErr = errAEWrongDataType;
		} else {
			ObjectTokenHandle theToken;
			AEDesc theObject;
			short count, i;
			
			theErr = AEExtractObject(&theList, 0, &theObject);
			if (theErr == noErr) {
				theToken = (ObjectTokenHandle)theObject.dataHandle;
				if (theObject.descriptorType == cWindow || 
					theObject.descriptorType == cDocument) {
					count = (*theToken)->count;
					for (i = 0; i < count; i++) {
						if (theErr == noErr)
							theErr = DoPrintDocument(FindImage((WindowPtr)(*theToken)->object[i]),
									 i == 0);
					}
				} else theErr = errAEEventNotHandled;
				AEDisposeDesc(&theObject);
			}
		}
		AEDisposeDesc(&theList);
		StopSpinning(&qd.arrow);
	}
	EnableMenus();
	AEEnd();
	return theErr;
}

static ImageHandle ImageFromSpec(FSSpec *theFile)
{
	ImageHandle theImage;
	
	for (theImage = gImageRoot; theImage; theImage = (*theImage)->next)
		if (SameFile(&(*theImage)->file, theFile)) return theImage;
	return nil;
}

extern OSErr DoPrintDocument(ImageHandle theImage, Boolean first)
{
	NestedProgress progProc = { { nil, 0 }, 0, 0L, 0x10000L, false };
	TPrStatus printStatus;
	WindowPtr frontWindow;
	OSErr theErr = noErr;
	TPPrPort printPort;
	
	// initialize the progress proc, and save a reference to our current resource file
	progProc.prog.progressProc = gGenericProgress;
	progProc.prog.progressRefCon = (long)&progProc;
	gOurResFile = CurResFile();

	// open the driver and, if this is the first document, handle the print dialog 
	PrOpen();
	if (first) {
		theErr = GetPrintParams();
		if (theErr != noErr) return theErr;
	}

	// call the progress procedure to open the printing... dialog
	UpdateParamText(gString[strPrinting], gNullString, gNullString, (*theImage)->file.name);
	CallICMProgressProc(progProc.prog.progressProc, codecProgressOpen, kModalProgress, 
				progProc.prog.progressRefCon);

	// store a pointer to our idle procedure in the print record
	if (!gPrintIdle) gPrintIdle = NewPrIdleProc((ProcPtr)PrintIdleProc);
	(*gPrintRecord)->prJob.pIdleProc = gPrintIdle;
	
	// set up the proper document name
	if (frontWindow = FrontWindow()) {
		Str63 windowName;
		BlockMove((*theImage)->file.name, windowName, *(*theImage)->file.name + 1);
		SetWTitle(frontWindow, windowName);
		PrValidate(gPrintRecord);
	}
	
	// do the actual printing
	if ((printPort = PrOpenDoc(gPrintRecord, nil, nil)) && (theErr = PrError()) == noErr) {
		PrOpenPage(printPort, nil);
		if ((theErr = PrError()) == noErr) theErr = DrawToPrinter(theImage, &progProc);
		PrClosePage(printPort);
	}
	PrCloseDoc(printPort);

	// spool the printing, if necessary
	if ((*gPrintRecord)->prJob.bJDocLoop == bSpoolLoop && theErr == noErr) {
		PrPicFile(gPrintRecord, nil, nil, nil, &printStatus);
		theErr = PrError();
	}

	// close the outer progress, along with the printer driver, and exit
	CallICMProgressProc(progProc.prog.progressProc, codecProgressClose, kModalProgress, 
				progProc.prog.progressRefCon);
	PrClose();
	return theErr;
}

static OSErr DrawToPrinter(ImageHandle theImage, NestedProgressPtr progProc)
{
	Rect printRect, srcRect, dstRect, boundsRect;
	NestedProgress innerProg;
	OSErr theErr = noErr;

	// get the printing rectangle, and inset it by 1/2 inch on all sides
	printRect = (*gPrintRecord)->prInfo.rPage;
	InsetRect(&printRect, (*gPrintRecord)->prInfo.iHRes / 2, 
				(*gPrintRecord)->prInfo.iVRes / 2);

	// calculate the appropriate source/destination rectangles
	srcRect = (*theImage)->crect;
	MaxRect(&srcRect, &printRect, &dstRect);
	OffsetRect(&dstRect, (Width(&printRect) - Width(&dstRect)) / 2, 
				(Height(&printRect) - Height(&dstRect)) / 2);
	ClipRect(&dstRect);
	boundsRect = (*theImage)->grect;
	
	// set up the bounds of the progress procedure used during drawing
	innerProg = *progProc;
	innerProg.begin = 0x00001000L;
	innerProg.end = 0x0000F000L;

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

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

			// point to the dummy drawing port and start the inner progress proc
			MySetPort((*gPrintParams)->dummy);
			CallICMProgressProc((*gPrintParams)->progress.prog.progressProc, codecProgressOpen,
						kModalProgress, (*gPrintParams)->progress.prog.progressRefCon);
			
			// now do the drawing, either from the offscreen GWorld...
			if ((*theImage)->gworld && GWOrigSize(theImage))
				CopyGWorldToGWorld((*theImage)->gworld, (*gPrintParams)->dummy,
							&(*theImage)->gworld->portRect, &(*theImage)->gworld->portRect,
							srcCopy, nil);

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

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

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

static OSErr GetPrintParams(void)
{
	OSErr theErr = AEInteractWithUser(kAEDefaultTimeout, &gTheNotification, 
									gAEIdleProc);
	if (!gPrintRecord) {
		if (LoadPrefsHandle(kPrefsPrintType, 0, (Handle *)&gPrintRecord) != noErr) {
			if (!(gPrintRecord = (THPrint)NewHandle(sizeof(TPrint)))) return memFullErr;
			PrintDefault(gPrintRecord);
		} else PrValidate(gPrintRecord);
	}
	if (theErr != errAENoUserInteraction) {
		StopSpinning(&qd.arrow);
		FWDeactivate();
		if (!PrJobDialog(gPrintRecord)) {
			FWActivate();
			return codecAbortErr;
		}
		FWActivate();
		StartSpinning();
		SavePrefsHandle(kPrefsPrintType, 0, (Handle)gPrintRecord);
	}
	return theErr;
}

static pascal void PrintIdleProc(void)
{
	short oldResFile = CurResFile();
	UseResFile(gOurResFile);
	PushPort();
	if (CheckAbort(gPrintParams)) PrSetError(iPrAbort);
	PopPort();
	UseResFile(oldResFile);
}

/*
 * HandleQuitApplication(theEvent, reply, refCon)
 *
 *     Purpose: AppleEvent handler for the quit application event; we set the done flag,
 *					clean up all the windows, restore the menu bar, and return
 *      Inputs: theEvent = the AppleEvent record
 *				reply = the AppleEvent reply
 *				refCon = our personal work variable
 *     Returns: an OSErr with the overall result of the operation (i.e., noErr)
 *
 */

pascal OSErr HandleQuitApplication(AppleEvent *theEvent, AppleEvent *reply, long refCon) 
{
#if applec
#pragma unused(theEvent, reply, refCon)
#endif
	AEBegin();
	gDone = true;
	AEEnd();
	return noErr;
}
