/*
 *  SpriteExtras.c
 *  Hootenanny
 *
 *  Created by Noah Sorscher on Sun Dec 21 2003.
 *  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
 *
 */

#include "SpriteExtras.h"

OSErr SpriteExtras_AddCompressedImageToKeyFrameSample (QTAtomContainer theKeySample, Handle myCompressedPicture, QTAtomID theID, FixedPoint *theRegistrationPoint, StringPtr theImageName, ImageDescriptionHandle myImageDesc) {

    HLock(myCompressedPicture);
    return SpriteUtils_AddCompressedImageToKeyFrameSample(theKeySample, myImageDesc, GetHandleSize(myCompressedPicture), *myCompressedPicture, theID, theRegistrationPoint, theImageName);
}

//////////
//
// SpriteUtils_AddPICTImageToKeyFrameSample
// Compress a PICT with the animation compressor and add the image data to a sprite key sample's images container atom.
//
//////////

OSErr SpriteUtils_AddPICTImageToKeyFrameSample (QTAtomContainer theKeySample, PicHandle myPicture, FSSpec *theFSSpec, RGBColor *theKeyColor, QTAtomID theID, FixedPoint *theRegistrationPoint, StringPtr theImageName)
{
	//PicHandle				myPicture = NULL;
	Handle					myCompressedPicture = NULL;
	ImageDescriptionHandle			myImageDesc = NULL;
	OSErr					myErr = noErr;
	
	DetachResource((Handle)myPicture);
	
	// convert it to image data compressed by the animation compressor
        if (myPicture)
            myErr = ICUtils_RecompressPictureWithTransparency(myPicture, theKeyColor, NULL, &myImageDesc, &myCompressedPicture);
        else if (theFSSpec)
            myErr = ICUtils_RecompressPictureFileWithTransparency(theFSSpec, theKeyColor, NULL, &myImageDesc, &myCompressedPicture);
        else goto bail;
	if (myErr != noErr)
		goto bail;

	// add it to the key sample
	SpriteExtras_AddCompressedImageToKeyFrameSample(theKeySample, myCompressedPicture, theID, theRegistrationPoint, theImageName, myImageDesc);
	
bail:
	if (myPicture != NULL)
		KillPicture(myPicture);
		
	if (myCompressedPicture != NULL)
		DisposeHandle(myCompressedPicture);
		
	if (myImageDesc != NULL)
		DisposeHandle((Handle)myImageDesc);
		
	return(myErr);
}


//////////
//
// QTWired_SetTrackProperties
// Set the track properties for the specified sample sprite movie.
//
//////////

void QTWired_SetTrackProperties (Media theMedia, UInt32 theIdleFrequency, RGBColor myBackgroundColor)
{
	QTAtomContainer		myTrackProperties;
	//RGBColor			myBackgroundColor;
	Boolean				hasActions;
	UInt32				myFrequency;
	OSErr				myErr = noErr;
		
	// add a background color to the sprite track
        //myBackgroundColor = [self RGBColor];
	//myBackgroundColor.red = EndianU16_NtoB(0xffff);
	//myBackgroundColor.green = EndianU16_NtoB(0xffff);
	//myBackgroundColor.blue = EndianU16_NtoB(0xffff);
	
	myErr = QTNewAtomContainer(&myTrackProperties);
	if (myErr == noErr) {
		QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyBackgroundColor, 1, 1, sizeof(myBackgroundColor), &myBackgroundColor, NULL);

		// tell the movie controller that this sprite track has actions
		hasActions = true;
		QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyHasActions, 1, 1, sizeof(hasActions), &hasActions, NULL);
	
		// tell the sprite track to generate QTIdleEvents
		myFrequency = EndianU32_NtoB(theIdleFrequency);
		QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyQTIdleEventsFrequency, 1, 1, sizeof(myFrequency), &myFrequency, NULL);

		SetMediaPropertyAtom(theMedia, myTrackProperties);
		
		QTDisposeAtomContainer(myTrackProperties);
	}
}

void MHWired_SetTrackProperties(Media theMedia, RGBColor myBackgroundColor, Boolean isVisible) {
    QTAtomContainer myTrackProperties;
    Boolean hasActions = true;
    OSErr myErr = QTNewAtomContainer(&myTrackProperties);
    if (myErr) return;
    QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyBackgroundColor, 1, 1, sizeof(myBackgroundColor), &myBackgroundColor, NULL);
    QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyHasActions, 1, 1, sizeof(hasActions), &hasActions, NULL);
    QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyVisible, 1, 1, sizeof(isVisible), &isVisible, NULL);
    SetMediaPropertyAtom(theMedia, myTrackProperties);
    QTDisposeAtomContainer(myTrackProperties);
}


//////////
//
// QTWired_AddCursorChangeToSprite
// Add cursor-change-on-mouse-over and -down actions to the sprite having the specified ID
// in the specified atom container.
//
// Here we use the new "sprite behaviors atom" introduced in QuickTime 4.0, which
// simplifies adding button-like capabilities to sprites.
//
//////////

OSErr QTWired_AddCursorChangeToSprite (QTAtomContainer theContainer, QTAtomID theID)
{
	QTAtom					mySpriteAtom = 0;
	QTAtom					myBehaviorAtom = 0;
	QTSpriteButtonBehaviorStruct		myBehaviorRec;
	OSErr					myErr = paramErr;
	
	// find the sprite atom with the specified ID in the specified container
	mySpriteAtom = QTFindChildByID(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, NULL);
	if (mySpriteAtom == 0)
		goto bail;
	
	// insert a new sprite behaviors atom into the sprite atom
	myErr = QTInsertChild(theContainer, mySpriteAtom, kSpriteBehaviorsAtomType, 1, 1, 0, NULL, &myBehaviorAtom);
	if (myErr != noErr)
		goto bail;
	
	// set the sprite cursor behavior; -1 means: no change associated with this state transition
	myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(kQTCursorPointingHand);
	myBehaviorRec.overPressedStateID = EndianS32_NtoB(kQTCursorRightArrow);
	myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);

	myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteCursorBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);

bail:
	return(myErr);
}

//////////
//
// QTText_CopyCStringToPascal
// Convert the source C string to a destination Pascal string as it's copied.
//
// The destination string will be truncated to fit into a Str255 if necessary.
// If the C string pointer is NULL, the Pascal string's length is set to zero.
//
// This routine is borrowed from CGlue.c, by Nick Kledzik.
//
//////////

void QTTextCopyCStringToPascal (const char *theSrc, Str255 theDst)
{
	short					myLength = 0;
	
	// handle case of overlapping strings
	if ((void *)theSrc == (void *)theDst) {
		unsigned char		*myCurDst = &theDst[1];
		unsigned char		myChar;
				
		myChar = *(const unsigned char *)theSrc++;
		while (myChar != '\0') {
			unsigned char	myNextChar;
			
			// use myNextChar so we don't overwrite what we are about to read
			myNextChar = *(const unsigned char *)theSrc++;
			*myCurDst++ = myChar;
			myChar = myNextChar;
			
			if (++myLength >= 255)
				break;
		}
	} else if (theSrc != NULL) {
		unsigned char		*myCurDst = &theDst[1];
		short 				myOverflow = 255;		// count down, so test it loop is faster
		register char		myTemp;
	
		// we can't do the K&R C thing of ‚Äúwhile (*s++ = *t++)‚Äù because it will copy the trailing zero,
		// which might overrun the Pascal buffer; instead, we use a temp variable
		while ((myTemp = *theSrc++) != 0) {
			*(char *)myCurDst++ = myTemp;
				
			if (--myOverflow <= 0)
				break;
		}
		myLength = 255 - myOverflow;
	}
	
	// set the length of the destination Pascal string
	theDst[0] = myLength;
}


//////////
//
// QTUtils_ConvertCToPascalString
// Convert a C string into a Pascal string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////

StringPtr QTUtils_ConvertCToPascalString (char *theString)
{
	StringPtr	myString = malloc(strlen(theString) + 1);
	short		myIndex = 0;

	while (theString[myIndex] != '\0') {
		myString[myIndex + 1] = theString[myIndex];
		myIndex++;
	}
	
	myString[0] = (unsigned char)myIndex;
	
	return(myString);
}


//////////
//
// QTWired_AddCursorChangeOnMouseOver
// Add a cursor-change-on-mouse-over action to the sprite having the specified ID
// in the specified atom container.
//
// Here we use the new "sprite behaviors atom" introduced in QuickTime 4.0, which
// simplifies adding button-like capabilities to sprites.
//
//////////

OSErr QTWired_AddCursorChangeOnMouseOver (QTAtomContainer theContainer, QTAtomID theID)
{
	QTAtom								mySpriteAtom = 0;
	QTAtom								myBehaviorAtom = 0;
	QTSpriteButtonBehaviorStruct		myBehaviorRec;
	OSErr								myErr = noErr;
	
	// find the sprite atom with the specified ID in the specified container
	mySpriteAtom = QTFindChildByID(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, NULL);
	if (mySpriteAtom == 0) {
		// if there is none, insert a new sprite atom into the specified container
		myErr = QTInsertChild(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, 0, 0, NULL, &mySpriteAtom);
		if (myErr != noErr)
			goto bail;
	}
	
	// insert a new sprite behaviors atom into the sprite atom
	myErr = QTInsertChild(theContainer, mySpriteAtom, kSpriteBehaviorsAtomType, 1, 1, 0, NULL, &myBehaviorAtom);
	if (myErr != noErr)
		goto bail;

	//////////
	//
	// insert three atoms into the sprite behaviors atom; these three atoms specify what to do on each
	// of the four defined state transitions for the (1) sprite image, (2) cursor, and (3) status string
	//
	//////////
	
	// set the sprite image behavior; -1 means: no change associated with this state transition
	myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);

	myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteImageBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
	if (myErr != noErr)
		goto bail;
	
	// set the sprite cursor behavior; -1 means: no change associated with this state transition
	myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(kQTCursorPointingHand);
	myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);

	myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteCursorBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
	if (myErr != noErr)
		goto bail;
	
	// set the status string behavior; -1 means: no change associated with this state transition
	myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);

	myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteStatusStringsBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);
	if (myErr != noErr)
		goto bail;
			
bail:
	return(myErr);
}

