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

/*
 * ImageAccessor(classWanted, container, containerClass, keyForm, keyData,
 *				 resultToken, theRefCon)
 *
 *     Purpose: Finds an image according to the specified keyForm and returns the
 *					corresponding image token
 *      Inputs: classWanted = the element class desired (cImage)
 *				container = token for this element's container
 *				containerClass = class of this element's container (cDrawingArea/cDocument)
 *				keyForm = the search key type
 *				keyData = descriptor containing search key data
 *				resultToken = descriptor containing the resulting token
 *				theRefCon = reference constant
 *     Returns: an OSErr describing the result
 *
 */

pascal OSErr ImageAccessor(DescType classWanted, AEDesc *container, 
	DescType containerClass, DescType keyForm, AEDesc *keyData, 
	AEDesc *resultToken, long theRefCon)
{
#if applec
#pragma unused(classWanted, containerClass, theRefCon)
#endif
	ObjectTokenHandle theList = (ObjectTokenHandle)container->dataHandle;
	OSErr theErr = noErr;
	
	if (IsValidForOne(keyForm, keyData)) {
		if ((theErr = HandToHand((Handle *)&theList)) != noErr) return theErr;
	} else return errAENoSuchObject;
	if (!theList || !(*theList)->count || (theErr != noErr)) {
		DisposeHandle((Handle)theList);
		if (theErr == noErr) theErr = errAENoSuchObject;
	} else {
		theErr = AECreateHandleDesc(cImage, (Handle)theList, resultToken);
		DisposeHandle((Handle)theList);
	}
	return theErr;
}

/*
 * ImagePropertyAccessor(classWanted, container, containerClass, keyForm, keyData,
 *						 resultToken, theRefCon)
 *
 *     Purpose: Finds an image property and returns the corresponding property token
 *      Inputs: classWanted = the element class desired (typeProperty)
 *				container = token for this element's container
 *				containerClass = class of this element's container (cDocument)
 *				keyForm = the search key type
 *				keyData = descriptor containing search key data
 *				resultToken = descriptor containing the resulting token
 *				theRefCon = reference constant
 *     Returns: an OSErr describing the result
 *
 */

pascal OSErr ImagePropertyAccessor(DescType classWanted, AEDesc *container, 
	DescType containerClass, DescType keyForm, AEDesc *keyData, 
	AEDesc *resultToken, long theRefCon)
{
#if applec
#pragma unused(classWanted, containerClass, theRefCon)
#endif
	ObjectTokenHandle theList = (ObjectTokenHandle)container->dataHandle;
	DescType propertyType;
	OSErr theErr;
	
	if ((keyForm != formPropertyID) || (keyData->descriptorType != typeType))
		return errAECantSupplyType;
	propertyType = *(DescType *)*keyData->dataHandle;
	switch (propertyType) {
		// Non-modifiable properties
		case pBestType:
		case pClass:
		case pComments:
		case pCompression:
		case pDefaultType:
		case pDisplayTime:
		case pHasImageColorTable:
		case pHasQuantColorTable:
		case pImageColorTable:
		case pImageFormat:
		case pIsAborted:
		case pIsBanded:
		case pIsCompressed:
		case pIsCorrupt:
		case pIsCropped:
		case pLength:
		case pOriginalBounds:
		case pPixelDepth:
		case pQuantColorTable:
		// Modifiable properties
		case pBounds:
			break;
		default:
			return errAEEventNotHandled;
	}
	theErr = HandToHand((Handle *)&theList);
	if (theErr != noErr) return theErr;
	(*theList)->property = propertyType;
	(*theList)->objclass = cImage;
	theErr = AECreateHandleDesc(typeProperty, (Handle)theList, resultToken);
	DisposeHandle((Handle)theList);
	return theErr;
}

/*
 * GetImageData(theWindow, typeWanted, theData)
 *
 *     Purpose: Extracts the data from the image
 *      Inputs: theWindow = the window associated with this object
 *				typeWanted = the type we're asking for
 *				theData = the data we save
 *     Returns: an OSErr describing the result
 *
 */

OSErr GetImageData(WindowPtr theWindow, DescType typeWanted, AEDesc *theData)
{
	if (typeWanted == typeWildCard) typeWanted = typeObjectSpecifier;
	if (typeWanted == typeBest) typeWanted = typeObjectSpecifier;
	if (typeWanted != typeObjectSpecifier) return errAECantSupplyType;
	return MakeImageObject(theWindow, theData);
}

/*
 * GetImagePropertyData(theWindow, theProperty, typeWanted, theData)
 *
 *     Purpose: Extracts the data from the image's properties
 *      Inputs: theWindow = the window associated with this object
 *				theProperty = the property wanted
 *				typeWanted = the type we're asking for
 *				theData = the data we save
 *     Returns: an OSErr describing the result
 *
 */

OSErr GetImagePropertyData(WindowPtr theWindow, DescType theProperty, DescType typeWanted, 
	AEDesc *theData)
{
	ImageHandle theImage = FindImage(theWindow);
	Boolean theBoolean = true;
	CTabHandle theColors;
	Handle theHandle;
	DescType theType;
	AEDesc bestData;
	short theShort;
	long theLong;
	Rect theRect;
	OSErr theErr;
	
	if (!theImage) return errAEReadDenied;
	switch (theProperty) {
		case pBestType:
		case pDefaultType:
			theType = typeObjectSpecifier;
			theErr = AECreateDesc(typeType, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pBounds:
			theRect = (*theImage)->crect;
			theErr = AECreateDesc(typeQDRectangle, (void *)&theRect, sizeof(Rect), &bestData);
			break;
		case pClass:
			theType = cImage;
			theErr = AECreateDesc(typeType, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pComments:
			theHandle = (*theImage)->comments;
			if (theLong = theHandle ? GetHandleSize(theHandle) : 0)
				theErr = HandToHand(&theHandle);
			if (!theLong || theErr == noErr) {
				if (theLong) SetHandleSize(theHandle, theLong + 4);
				else theHandle = NewHandle(4);
				if ((theErr = MemError()) == noErr) {
					if (theLong) BlockMove(*theHandle, *theHandle + 4, theLong);
					*(short *)*theHandle = smCurrentScript;
					*(short *)(*theHandle + 2) = currentCurLang;
					theErr = AECreateHandleDesc(typeIntlText, theHandle, &bestData);
				}
				DisposeHandle(theHandle);
			}
			break;
		case pCompression:
			theType = (*theImage)->compression;
			if (!(*theImage)->compression) theType = '    ';
			theErr = AECreateDesc(typeType, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pDisplayTime:
			theLong = (*theImage)->dtime;
			theErr = AECreateDesc(typeLongInteger, (void *)&theLong, sizeof(long), &bestData);
			break;
		case pHasImageColorTable:
			theBoolean = ((*theImage)->ipalette != nil);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pHasQuantColorTable:
			theBoolean = ((*theImage)->qpalette != nil);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pImageColorTable:
			theColors = (CTabHandle)NewHandle(4);
			if (theColors) Palette2CTab((*theImage)->ipalette, theColors);
			theErr = AECreateHandleDesc(typeColorTable, (Handle)theColors, &bestData);
			DisposeHandle((Handle)theColors);
			break;
		case pImageFormat:
			theType = (*theImage)->format->inType;
			theErr = AECreateDesc(typeType, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pIsAborted:
			theBoolean = Aborted(theImage);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pIsBanded:
			theBoolean = Banded(theImage);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pIsCompressed:
			theBoolean = ((*theImage)->compression != 0);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pIsCorrupt:
			theBoolean = Corrupt(theImage);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pIsCropped:
			theBoolean = Cropped(theImage);
			theErr = AECreateDesc(typeBoolean, (void *)&theBoolean, sizeof(Boolean), &bestData);
			break;
		case pLength:
			theLong = GetHandleSize((*theImage)->data);
			theErr = AECreateDesc(typeLongInteger, (void *)&theLong, sizeof(long), &bestData);
			break;
		case pOriginalBounds:
			theRect = (*theImage)->grect;
			theErr = AECreateDesc(typeQDRectangle, (void *)&theRect, sizeof(Rect), &bestData);
			break;
		case pPixelDepth:
			theShort = (*theImage)->depth;
			theErr = AECreateDesc(typeShortInteger, (void *)&theShort, sizeof(short), &bestData);
			break;
		case pQuantColorTable:
			theColors = (CTabHandle)NewHandle(4);
			if (theColors) Palette2CTab((*theImage)->qpalette, theColors);
			theErr = AECreateHandleDesc(typeColorTable, (Handle)theColors, &bestData);
			DisposeHandle((Handle)theColors);
			break;
		default:
			theErr = errAETypeError;
			break;
	}
	if (theErr != noErr) return theErr;
	if ((bestData.descriptorType == typeWanted) ||
		(typeWanted == typeWildCard) || (typeWanted == typeBest))
		theErr = AEDuplicateDesc(&bestData, theData);
	else theErr = AECoerceDesc(&bestData, typeWanted, theData);
	AEDisposeDesc(&bestData);
	return theErr;
}

/*
 * SetImagePropertyData(theWindow, theProperty, theData)
 *
 *     Purpose: Sets the data for the images's properties
 *      Inputs: theWindow = the associated window
 *				theProperty = the property wanted
 *				theData = the data to set it to
 *     Returns: an OSErr describing the result
 *
 */

OSErr SetImagePropertyData(WindowPtr theWindow, DescType theProperty, AEDesc *theData)
{
	ImageHandle theImage = FindImage(theWindow);
	Rect theRect;
	OSErr theErr;

	if (!theImage) return errAEWriteDenied;
	switch (theProperty) {
		case pBounds:
			theErr = AEExtractQDRectangle(theData, 0, &theRect);
			if (theErr == noErr) theErr = DoSetImageBounds(theImage, &theRect);
			break;
		default:
			theErr = errAEWriteDenied;
			break;
	}
	return theErr;
}

/*
 * MakeImageObject(theWindow, theObject)
 *
 *     Purpose: Creates an image object descriptor for the given window
 *      Inputs: theWindow = pointer to the window, or nil for all windows
 *				theObject = pointer to an AEDesc to store the result
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr MakeImageObject(WindowPtr theWindow, AEDesc *theObject)
{
	AEDesc theKey, theContainer;
	long index = kAEFirst;
	OSErr theErr = noErr;
	
	theErr = MakeDocumentObject(theWindow, &theContainer);
	if (theErr == noErr) {
		theErr = AECreateDesc(typeAbsoluteOrdinal, (void *)&index, sizeof(long), &theKey);
		if (theErr == noErr) {
			theErr = CreateObjSpecifier(cImage, &theContainer, formAbsolutePosition, 
					 &theKey, false, theObject);
			AEDisposeDesc(&theKey);
		}
		AEDisposeDesc(&theContainer);
	}
	return theErr;
}

/*
 * MakeImagePropertyObject(theWindow, theProperty, theObject)
 *
 *     Purpose: Creates a property object descriptor for the given images's property
 *      Inputs: theWindow = pointer to the window, or nil for all windows
 *				theObject = pointer to an AEDesc to store the result
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr MakeImagePropertyObject(WindowPtr theWindow, DescType theProperty, AEDesc *theObject)
{
	AEDesc theKey, theContainer;
	OSErr theErr = noErr;
	
	theErr = MakeImageObject(theWindow, &theContainer);
	if (theErr == noErr) {
		theErr = AECreateDesc(typeType, (void *)&theProperty, sizeof(DescType), &theKey);
		if (theErr == noErr) {
			theErr = CreateObjSpecifier(typeProperty, &theContainer, formPropertyID, 
					 &theKey, false, theObject);
			AEDisposeDesc(&theKey);
		}
		AEDisposeDesc(&theContainer);
	}
	return theErr;
}

/*
 * DoSetImageBounds(theImage, theRect)
 *
 *     Purpose: Crops an image to the given bounds
 *      Inputs: theImage = pointer to the image
 *				theRect = the new bounds
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr DoSetImageBounds(ImageHandle theImage, Rect *theRect)
{
	Boolean delImage = true;

	if (Width(theRect) < 1) theRect->right = theRect->left + 1;
	if (Height(theRect) < 1) theRect->bottom = theRect->top + 1;
/*	if (Cropped(theImage) && !Banded(theImage) &&
		((*theImage)->format->inType == kPICTType) && ((*theImage)->compression == kJPEGCompression)) {
		picRect = (*(PicHandle)(*theImage)->data)->picFrame;
		if ((theRect->right > picRect.right) || (theRect->left < picRect.left) ||
			(theRect->bottom > picRect.bottom) || (theRect->top < picRect.top)) {
			if (!MakeMemAvailable((*theImage)->desc.dataSize * 2)) 
				return gIntError = errNoMemory, memFullErr;
			if (theData = UnwrapJPEG((*theImage)->data)) {
				if (newData = WrapJPEG(theData, (*theImage)->depth)) {
					for (image = gImageRoot; image; image = (*image)->next)
						if ((image != theImage) && ((*image)->data == (*theImage)->data)) 
							delImage = false;
					if (delImage) DisposeHandle((*theImage)->data);
					(*theImage)->data = newData;
					(*theImage)->qrect = (*theImage)->grect;
				} else {
					DisposeHandle(theData);
					return gIntError = errNoMemory, memFullErr;
				}
				DisposeHandle(theData);
			} else return gIntError = errNoMemory, memFullErr;
		}
	}*/
	(*theImage)->crect = *theRect;
	if (!GWOrigSize(theImage)) KillGWorld(theImage);
	(*theImage)->wrect = (*theImage)->crect;
	ScaleRect(&(*theImage)->wrect, (*theImage)->dmon, Full(theImage), gThePrefs.expandSmall);
	ResizeWindow(theImage);
	return noErr;
}
