//** Craig Laurent
#import "IconAnimator.h"

@implementation IconAnimator

/*  This is the callback function for the DPS Timed Entry. 
It in turn calls our animateIcon method.  DPS Timed Entries require functions not methods, as callbacks. */
void Animate (DPSTimedEntry timedEntry, double  now, void *who)
{
	[(id)who animateIcon];
}


- init
{
	return [self initWithImageArray:[NSMutableArray array] animationPattern:[NSMutableArray array] andPatternLoops:0];
}

//** designated initializer
- initWithImageArray:(NSArray*)anArray animationPattern:(NSArray*)patternArray andPatternLoops:(int)loops
{
	if (self = [super init]) {
		//** set default value for instance variables
		appTile = nil;
		appIconContentView = nil;
		imageArray = nil;
		animationPattern = nil;
		patternLoops = 0;
		timeInterval = 0.2;
		indexOfStopIcon = 0;
		indexOfEndIcon = 1;
		timedEntry = 0;
		patternIterator = 0;
		loopCount = 0;
		patternEnd = 0;
		offset = 0;

		//** set value for instance variables
		appTile = [NXImage findImageNamed:"NXAppTile"];
		appIconContentView = [[NXApp appIcon] contentView];
		[self setImageArray:anArray];
		[self setAnimationPattern:patternArray];
		[self setPatternLoops:loops];
		
		return self;
	}
	return nil;
}


- (void)dealloc
{
    [imageArray autorelease];
    [animationPattern autorelease];
	[super dealloc];
}


//** instance methods
- (NSArray*)imageArray
{ return imageArray; }
- (void)setImageArray:(NSArray*)anArray
{
	[imageArray autorelease];
	imageArray = [anArray retain];
}

- (NSArray*)animationPattern
{ return animationPattern; }
- (void)setAnimationPattern:(NSArray*)anArray
{
	[animationPattern autorelease];
	animationPattern = [anArray retain];

	patternEnd = [animationPattern count];
}

- (int)patternLoops
{	return patternLoops; }
- (void)setPatternLoops:(int)numLoops
{	patternLoops = numLoops; }

- (float)timeInterval
{	return timeInterval; }
- (void)setTimeInterval:(float)interval
{	timeInterval = interval; }

- (int)indexOfEndIcon
{	return indexOfEndIcon; }
- (void)setIndexOfEndIcon:(int)endIcon
{	indexOfEndIcon = endIcon; }

- (int)indexOfStopIcon
{	return indexOfStopIcon; }
- (void)setIndexOfStopIcon:(int)stopIcon
{	indexOfEndIcon = stopIcon; }


//** methods to change Animation
/* startAnimation - starts running the animation sequence */
- (void)startAnimation:sender
{
//  if none exists, add a timed entry to begin the animation sequence...
	
	if (!timedEntry) {
	    timedEntry = DPSAddTimedEntry([self timeInterval], Animate, (void*)self, NX_MODALRESPTHRESHOLD);
	}
}

/* stopAnimation - stops the animation sequence and sets the application icon to a standard image */
- (void)stopAnimation
{
//  Indicate Animation stopped by setting image to image one.  Do necessary housekeeping.
	
	[self removeTimedEntry];
	if ([[self imageArray] count]) {
		[self displayImage: (NXImage*)[[self imageArray] objectAtIndex: [self indexOfStopIcon]]];
	}
	patternIterator = 0;
	loopCount = 0;
}


//** Internal methods
/* removeTimedEntry - removes the timedEntry if it is running */
- (void)removeTimedEntry
{
	if (timedEntry) {
		DPSRemoveTimedEntry(timedEntry);
		timedEntry = 0;
	}
}

/* cornerPointForImage: -  calculates and returns the point that should be used as the corner for this image.  This is used to make sure the image is centered on the application icon. */
- (NXPoint)cornerPointForImage:(NXImage*)anImage
{
//*  You must center the composited image within
//*   the contentView of the appIcon window.
	NXSize	appTileSize;
	NXSize	imageSize;
	NXPoint	cornerPoint = { 0.0, 0.0 };

	[appTile getSize: &appTileSize];
	[anImage getSize: &imageSize];
	
	if ( imageSize.width < appTileSize.width ) 
		cornerPoint.x = (appTileSize.width - imageSize.width ) / 2.0;
	
	if ( imageSize.height < appTileSize.height ) 
		cornerPoint.y = (appTileSize.height - imageSize.height ) / 2.0;
		
	return cornerPoint;
}

/* displayImage: - displays the given image into the application icon. */
- (void)displayImage:(NXImage*)anImage
{
//*  NXAppTile is composited first at 0,0 of the icon window's content view 
//*  (this is required in order to maintain the NeXT icon look).
//*  'anImage' is then composited at center (centering is also a requirement).

	NXPoint	contentViewOrigin = { 0.0, 0.0 };
	NXPoint	cornerPoint = [self cornerPointForImage:anImage];
	
	[appIconContentView lockFocus];
	[appTile composite:NX_SOVER toPoint:&contentViewOrigin];
	[anImage composite:NX_SOVER toPoint:&cornerPoint];
	[appIconContentView unlockFocus];
	[appIconContentView display];
}

/* animateIcon - called every time a timedEntry executes.  This contains logic for the icon animation (image sequencing).  The images contained in imageArray are used.  The animationPattern provides an index into that list, giving us the right image at the right time. */
- (void)animateIcon
{
	[self displayImage: (NXImage*)[[self imageArray] objectAtIndex:[[[self animationPattern] objectAtIndex:patternIterator++] intValue]]];

	if (patternIterator == (patternEnd - offset)) {
		[self removeTimedEntry];
		patternIterator = 0;
		loopCount++;
		if (loopCount < [self patternLoops]) {
			offset = 1;
//** the following only works for Object, not NSObject
//			[self perform: @selector(startAnimation:) with:nil afterDelay:500 cancelPrevious:YES];
			[self perform: @selector(startAnimation:) withObject:nil];
		} else {
			loopCount = offset = 0;
			[self displayImage:(NXImage*)[imageArray objectAtIndex: [self indexOfEndIcon]]];
		}			
	}
}



@end
