//
//  MHSpriteTrack.m
//  wired
//
//  Created by Noah Sorscher on Sun May 18 2003.
//  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
//

#import "MHSpriteTrack.h"
#import "SpriteUtilities.h"


void QTTextCopyCStringToPascal (const char *theSrc, Str255 theDst);

@interface MHSpriteTrack (Internals)

- (BOOL) setTrackEnabledAction:(QTAtomContainer)theSprite trackType:(OSType)theType ident:(id)theIdent;
- (OSErr)createTrackAndMedia;
- (void)offsetTrack;
- (RGBColor)RGBColor;

@end

@implementation MHSpriteTrack

FSSpec fsSpecFromPath(NSString *path);
void QTWired_SetTrackProperties (Media theMedia, UInt32 theIdleFrequency, RGBColor myBackgroundColor);
OSErr SpriteUtils_AddPICTImageToKeyFrameSample (QTAtomContainer theKeySample, PicHandle myPicture, FSSpec *theFSSpec, RGBColor *theKeyColor, QTAtomID theID, FixedPoint *theRegistrationPoint, StringPtr theImageName);
OSErr QTWired_AddCursorChangeToSprite (QTAtomContainer theContainer, QTAtomID theID);
//OSErr QTWired_MakeSpriteDraggable (QTAtomContainer theContainer, QTAtomID theID);


- (MHSpriteTrack *)initWithMovie:(NSMovie *)movie
{
    theMovie = movie;
    theTrack = nil;
    bgColor = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
    first = YES;
    picIndex = spriteIndex = 1;
    theDuration = 1000;
    theKeyFrameSample = nil;
    sprites = [[NSMutableArray alloc] init];
    background = nil;
    bgImage = nil;
    self = [super init];
    return self;
}

- (NSDictionary *)defsEntry
{
    NSMutableDictionary *entry = [[NSMutableDictionary alloc] init];
    //[entry setObject:bgColor forKey:@"bgColor"];
    [entry setObject:[NSNumber numberWithLong:theDuration] forKey:@"theDuration"];
    if (background) [entry setObject:background forKey:@"background"];
    else {
        float r, g, b, a;
        [bgColor getRed:&r green:&g blue:&b alpha:&a];
        [entry setObject:[NSString stringWithFormat:@"%g %g %g %g", r, g, b, a] forKey:@"bgColor"];
    }
    int i, n = [sprites count];
    NSMutableArray *dSprites = [[NSMutableArray alloc] init];
    for (i=0;i<n;i++) {
        [dSprites addObject:[[sprites objectAtIndex:i] defsEntry]];
    }
    [entry setObject:dSprites forKey:@"sprites"];
    return entry;
}

- (MHSpriteTrack *)initWithDefsEntry:(NSDictionary *)entry movie:(NSMovie *)movie
{
    self = [self initWithMovie:movie];
    //[self setBackgroundColor:[entry objectForKey:@"bgColor"]];
    [self setDuration:[[entry objectForKey:@"theDuration"] longValue]];
    NSArray *s = [entry objectForKey:@"sprites"];
    int i, n = [s count];
    for (i=0;i<n;i++) {
        [sprites addObject:[[oSprite alloc] initWithDefsEntry:[s objectAtIndex:i] parent:self]];
    }
    NSString *bg = [entry objectForKey:@"background"];
    if (bg)
        background = [bg retain];
    else {
        bg = [entry objectForKey:@"bgColor"];
        float r, g, b, a;
        const char *cstring = [bg cString];
        sscanf(cstring, "%g %g %g %g", &r, &g, &b, &a);
        bgColor = [NSColor colorWithCalibratedRed:r green:g blue:b alpha:a];
    }
    return self;
}

- (MHSpriteSpec)newSpec
{
    MHSpriteSpec spec;
    spec.point = NSMakePoint(0, 0);
    spec.visible = YES;
    spec.layer = 1;
    spec.cursor = -1;
    spec.picImage = nil;
    spec.fileImage = nil;
    spec.picRollImage = nil;
    spec.fileRollImage = nil;
    spec.picClickImage = nil;
    spec.fileClickImage = nil;
    spec.action = -1;
    spec.longValue = 0;
    spec.stringValue = nil;
    
    return spec;
}

- (id)newSprite
{
    id newSprite = [[oSprite alloc] initWithParent:self];
    [sprites addObject:newSprite];
    return newSprite;
}

- (void)removeAllSprites
{
    [sprites removeAllObjects];
}

- (void)removeSprite:(oSprite *)sprite
{
    [sprites removeObject:sprite];
}

- (OSErr)addWiredSprite:(MHSpriteSpec)spec
{
    Media 				myMedia;
    OSErr 				myErr = noErr;
    //RGBColor				myKeyColor;
    Point				myLocation;
    short				isVisible, myIndex;
    QTAtomID				mainID, rollID, clickID;
    BOOL 				mainImage = NO, rollover = NO, clicked = NO;
    
    if (first) {
        myErr = [self createTrackAndMedia];
        if (myErr) return myErr;
    }
    myMedia = GetTrackMedia(theTrack);
        
    //myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;		// white
    
    FSSpec fsSpec;
    if (spec.picImage || spec.fileImage) {
            mainImage = YES;
            mainID = picIndex++;
            if (spec.fileImage)
                fsSpec = fsSpecFromPath(spec.fileImage);
            SpriteUtils_AddPICTImageToKeyFrameSample(theKeyFrameSample, spec.picImage, &fsSpec, NULL /* &myKeyColor */, mainID, NULL, NULL);
    }
    if (spec.picRollImage || spec.fileRollImage) {
            rollover = YES;
            rollID = picIndex++;
            if (spec.fileRollImage)
                fsSpec = fsSpecFromPath(spec.fileRollImage);
            SpriteUtils_AddPICTImageToKeyFrameSample(theKeyFrameSample, spec.picRollImage, &fsSpec, NULL /* &myKeyColor */, rollID, NULL, NULL);
    }
    if (spec.picClickImage || spec.fileClickImage) {
            clicked = YES;
            clickID = picIndex++;
            if (spec.fileClickImage)
                fsSpec = fsSpecFromPath(spec.fileClickImage);
            SpriteUtils_AddPICTImageToKeyFrameSample(theKeyFrameSample, spec.picClickImage, &fsSpec, NULL /* &myKeyColor */, clickID, NULL, NULL);
    }
    if (!mainImage && !rollover && !clicked)
        goto bail;
	// assign group IDs to the images
	//SpriteUtils_AssignImageGroupIDsToKeyFrame(theKeyFrameSample);
        
        // add samples to the sprite track's media
        BeginMediaEdits(myMedia);
        QTAtomContainer myButton, myActionButton, myActions = NULL;
        myIndex = mainID;
        myErr = QTNewAtomContainer(&myButton);
        if (myErr) return myErr;
        
        myLocation.h = spec.point.x;
        myLocation.v = spec.point.y;
        isVisible = spec.visible;
        myErr = SpriteUtils_SetSpriteData(myButton, &myLocation, &isVisible, &spec.layer, &myIndex, NULL, NULL, myActions);
        if (myErr) return myErr;
        
        //if (theCursor != -1)
            //QTWired_AddCursorChangeToSprite(myButton, spriteIndex);
        
        //if (rollover || clicked || theAction != -1) {
            myErr = QTCopyAtom(myButton, kParentAtomIsContainer, &myActionButton);
            if (myErr) return myErr;
            if (rollover) {
                // add rollover atoms
                WiredUtils_AddSpriteSetImageIndexAction(myActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, rollID, NULL);
                if (mainImage) {
                    WiredUtils_AddSpriteSetImageIndexAction(myActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, mainID, NULL);
                } else { // if there's a rollover and no main, we have to make you visible on enter and vice versa
                    WiredUtils_AddSpriteSetVisibleAction(myActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
                    WiredUtils_AddSpriteSetVisibleAction(myActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
                }
            }
            if (clicked) {
                if (mainImage) {
                    // add clicked image atoms
                    WiredUtils_AddSpriteSetImageIndexAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, clickID, NULL);
                    if (rollover) {
                        WiredUtils_AddSpriteSetImageIndexAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, rollID, NULL);
                    } else if (mainImage) {
                        WiredUtils_AddSpriteSetImageIndexAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, mainID, NULL);
                    } else { // if there's a clicked and no main or rollover, we have to make you visible on click and vice versa
                        WiredUtils_AddSpriteSetVisibleAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, true, NULL);
                        WiredUtils_AddSpriteSetVisibleAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, false, NULL);
                    }
                }
            }
            if (spec.action != -1) {
                // add the real actions
                Str255 tmpStr;
                switch (spec.action) {
                    case gotoTime:
                                WiredUtils_AddMovieGoToTimeAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, spec.longValue);
                                break;
                    case gotoTimeAndPlay:
                                WiredUtils_AddMovieGoToTimeAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, spec.longValue);
                                WiredUtils_AddMovieSetRateAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, fixed1);
                                break;
                    case gotoTimeByName:
                                if (!spec.stringValue || ![spec.stringValue isKindOfClass:[NSString class]])
                                    break;
                                QTTextCopyCStringToPascal([spec.stringValue cString], tmpStr);
                                WiredUtils_AddMovieGoToTimeByNameAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, tmpStr);
                                WiredUtils_AddMovieSetRateAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, fixed1);
                                break;
                    case setAudioTrack:
                                [self setTrackEnabledAction:myActionButton trackType:SoundMediaType ident:[NSNumber numberWithInt:spec.longValue]];
                                break;
                    case setAudioTrackByName: 
                                [self setTrackEnabledAction:myActionButton trackType:SoundMediaType ident:spec.stringValue];
                                break;
                    case setTextTrack: 
                                [self setTrackEnabledAction:myActionButton trackType:TextMediaType ident:[NSNumber numberWithInt:spec.longValue]];
                                break;
                    case setTextTrackByName: 
                                [self setTrackEnabledAction:myActionButton trackType:TextMediaType ident:spec.stringValue];
                                break;
                    case gotoBeginning:
                                WiredUtils_AddMovieGoToBeginningAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd);
                                break;
                    case gotoEnd:
                                WiredUtils_AddMovieGoToEndAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd);
                                break;
                    case stepForward:
                                WiredUtils_AddMovieStepForwardAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd);
                                break;
                    case stepBackward:
                                WiredUtils_AddMovieStepBackwardAction(myActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd);
                                break;
                    default: break;
                }
            }
            SpriteUtils_AddSpriteToSample(theKeyFrameSample, myActionButton, spriteIndex);
            QTDisposeAtomContainer(myActionButton);
        //}
        
        //if (first)
            SpriteUtils_AddSpriteSampleToMedia(myMedia, theKeyFrameSample, theDuration, true, NULL);
        
        myErr = EndMediaEdits(myMedia);
	if (myErr != noErr)
		return myErr;
        
	// add the media to the track
	//if (first) {
        //TimeValue tv = GetTrackDuration(theTrack);
        
            DeleteTrackSegment(theTrack, 0, GetTrackDuration(theTrack));
            
        //tv = GetTrackDuration(theTrack);
        
    bail:
            InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
            
        TimeValue tv = GetTrackDuration(theTrack);
        tv = tv - tv / spriteIndex;
        //tv = (spriteIndex - 1) * GetTrackDuration(theTrack);
        //tv = (spriteIndex - 1) * theDuration;
        
            DeleteTrackSegment(theTrack, 0, tv);
        
        //tv = GetTrackDuration(theTrack);
            
	// set the sprite track properties
            QTWired_SetTrackProperties(myMedia, 1, [self RGBColor]);
        //}
        
        spriteIndex++;
        first = NO;
        
	return(myErr);
}

- (BOOL) setTrackEnabledAction:(QTAtomContainer)theSprite trackType:(OSType)theType ident:(id)theIdent
{
    Track aTrack, targetTrack = nil;
    int i, n = GetMovieTrackCount([theMovie QTMovie]);
    BOOL enabled, byNumber;
    OSType aType;
    NSString *aTrackName;
    char *buffer;
    Handle h;
    OSErr myErr = noErr;
    if ([theIdent isKindOfClass:[NSNumber class]]) {
        byNumber = YES;
        targetTrack = GetMovieIndTrack([theMovie QTMovie], [theIdent intValue]);
    } else
        byNumber = NO;
    for (i=1; i<n; i++) {
        aTrack = GetMovieIndTrack([theMovie QTMovie], i);
        GetMediaHandlerDescription(GetTrackMedia(aTrack), &aType, 0, 0);
        if (aType == theType) {
            if (byNumber) { // by number
                if (aTrack == targetTrack)
                    enabled = YES;
                else
                    enabled = NO;
            } else { // by name
                h = NewHandleClear(0);
                if (![theIdent isKindOfClass:[NSString class]]) return NO;
                GetUserData(GetTrackUserData(aTrack), h, kUserDataName, 1);
                //buffer = malloc(sizeof(char) * GetHandleSize(h));
                //BlockMoveData(h, &buffer, GetHandleSize(h));
                buffer = (char *)*h;
                aTrackName = [NSString stringWithCString:buffer length:GetHandleSize(h)];
                if (aTrackName)
                    enabled = [theIdent isEqualToString:aTrackName];
                else
                    enabled = NO;
                //DisposeHandle(h);
                //NSLog(@"set track %@ enabled: %d", aTrackName, enabled);
            }
            myErr = WiredUtils_AddTrackSetEnabledAction(theSprite, kParentAtomIsContainer, kQTEventMouseClickEnd, kTargetTrackID, (void *)i, 0, enabled);
            if (myErr) return NO;
        }
    }
    return YES;
}

- (OSErr)setBackgroundColor:(NSColor *)color
{
    bgColor = [color retain];
    return noErr;
}

- (void)setBackgroundImage:(NSImage *)image source:(NSString *)input
{
    background = [input retain];
    bgImage = [image retain];
}

- (NSImage *)backgroundImage
{
    return bgImage;
}

- (NSString *)backgroundImageSource
{
    return background;
}

- (TimeValue)duration
{
    return theDuration;
}

- (void)setDuration:(TimeValue)dur
{
    theDuration = dur;
}

FSSpec fsSpecFromPath(NSString *path)
{
    FSRef fsRef;
    FSSpec fsSpec;
    OSErr myErr;
    (OSStatus)myErr = FSPathMakeRef([path fileSystemRepresentation],&fsRef, NULL /* isDirectory */);
    myErr = FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL /*catalogInfo*/, NULL /*outName*/, &fsSpec, NULL /*parentRef*/);
    return fsSpec;
}

//////////
//
// 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
	HLock(myCompressedPicture);
	myErr = SpriteUtils_AddCompressedImageToKeyFrameSample(theKeySample, myImageDesc, GetHandleSize(myCompressedPicture), *myCompressedPicture, theID, theRegistrationPoint, theImageName);
	
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);
	}
}


//////////
//
// 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);
}



- (OSErr)createTrackAndMedia
{
    OSErr myErr = noErr;
    SampleDescriptionHandle aDesc = (SampleDescriptionHandle)NewHandle(sizeof(SampleDescription));
    Movie myMovie = [theMovie QTMovie];
    int movieWidth = 0, movieHeight = 0;
    Track videoTrack = GetMovieIndTrackType(myMovie, 1, VideoMediaType, movieTrackMediaType);
    Media aMedia = GetTrackMedia(videoTrack);
    GetMediaSampleDescription(aMedia, 1, aDesc);
    if (nil != aDesc) {
        movieHeight = (*(ImageDescriptionHandle)aDesc)->height;
        movieWidth = (*(ImageDescriptionHandle)aDesc)->width;
    }
    if (0 == movieWidth)
        movieWidth = kIconSpriteTrackWidth;
    if (0 == movieHeight)
        movieHeight = kIconSpriteTrackHeight;
    
    Fixed myWidth = Long2Fix(movieWidth);
    Fixed myHeight = Long2Fix(movieHeight);
        
    if (nil == theTrack) {
        theTrack = NewMovieTrack(myMovie, myWidth, myHeight, kNoVolume);
        Media myMedia;
        myMedia = NewTrackMedia(theTrack, SpriteMediaType, GetMovieTimeScale(myMovie), NULL, 0);
        [self offsetTrack];
	myErr = QTNewAtomContainer(&theKeyFrameSample);
    }
    return myErr;
}

- (void)offsetTrack
{
        long index;
        Movie myMovie = [theMovie QTMovie];
        TimeValue timevalue = theDuration;
        Track aTrack = GetMovieIndTrack(myMovie, 1);
        for (index = 2; aTrack != nil; index++) {
            if (aTrack != theTrack)
                SetTrackOffset(aTrack, timevalue + GetTrackOffset(aTrack));
            aTrack = GetMovieIndTrack(myMovie, index);
        }
}

- (RGBColor)RGBColor
{
    RGBColor color;
    NSColor *newColor = [bgColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"];
    color.red = [newColor redComponent] * (double)0xffff;
    color.green = [newColor greenComponent] * (double)0xffff;
    color.blue = [newColor blueComponent] * (double)0xffff;
    return color;
}

- (void)refreshView:(NSView *)aView
{
    NSRect frameRect = [aView frame];
    frameRect.origin.x++;
    [aView setFrame:frameRect];
    [[aView superview] display];
    frameRect.origin.x--;
    [aView setFrame:frameRect];
    [[aView superview] setNeedsDisplay:YES];
}

- (NSString *)description	{	return @"Page";	}
- (id)childAtIndex:(int)index	{	return [sprites objectAtIndex:index];	}
- (int)numberOfChildren		{	return [sprites count];	}
- (BOOL)isExpandable		{	return YES;	}
- (id)objectForKey:(id)key	{	return nil;	}
- (BOOL)shouldEditKey:(id)key	{	return NO;	}

//////////
//
// 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;
}

@end
