#import "SwappingTableView.h"


float easeFunction(float t);

const float sliderAnimationInterval = 1/10;  // This is basically the "frame rate" for animating sliders.
const float sliderAnimationSeconds = 1; 	// This is how long sliders should take to get where they're going.  A second seemed like a good start.

static NSString *fromRowsKey = @"fromRowsKey", *toRowsKey = @"toRowsKey", *startDateKey = @"startDate";

@interface SwappingTableView ( _private_ )

- (void) doAnimationStep: sender;  // This should only ever be sent by the NSTimer created in -animateToFloatValue:.
- (void) stopAnimating;   // Used to clean up any stray timers.

@end

@implementation SwappingTableView

- (void)dealloc
{
	[mTimer release];
	[super dealloc];
}

- (void) setTimer:(NSTimer *)inNewValue
{
	[inNewValue retain];
	[mTimer invalidate];
	[mTimer release];
	mTimer = inNewValue;
}

/*"	Takes an array of row NSNumbers of the starting positions, and an array of row numbers of ending positions.  Examples of a 10-item array:
0,1 moved after element 4 : (0,1,2,3,4) -> (3,4,0,1,2) meaning 0 goes to position 3, 1 to 4, 3 to 0, etc.
9 moved after item 2: (3,4,5,6,7,8,9) -> (4,5,6,7,8,9,2) meaning 3 goes to 4, ..., 8 to 9, and 9 to 2.
All items are moved simultaneously.
"*/
- (void) rearrangeRows:(NSArray *)fromRowNumbers toRows:(NSArray *)toRowNumbers
{
	NSTimer *timer;

	NSDictionary *animationInfo;   // Everything we need to know while animating..

	[self stopAnimating];  // This ensures that we don't get multiple timers trying to move the slider at the same time..

	/*   NSTimer has a "userInfo" method that can hand us back whatever information we want to associate with a timer.  In this case, we'll use a dictionary  to hold multiple parameters.  To do the math for our spiffy ease-in/ease-out motion smoothing, at each step in the animation we need to know what value we started with, what value we're aiming for, and when we started to animate. */
	animationInfo =
		[NSDictionary dictionaryWithObjectsAndKeys:
			fromRowNumbers, fromRowsKey,
			toRowNumbers, toRowsKey,
			[NSDate date], startDateKey, nil];

	timer = // Make a new timer..
		[NSTimer scheduledTimerWithTimeInterval:sliderAnimationInterval // how often it fires
																	target:self   // what object it will message when it fires
															   selector:@selector(doAnimationStep:) // What method will it invoke when it fires
															   userInfo: animationInfo  // The NSDictionary we just built above..
																repeats:YES];
	[self setTimer:timer];
}

- (void)drawRow:(int)rowIndex clipRect:(NSRect)clipRect
{
	NSLog(@"drawRow:%d clipRect:%@  visibleRect = %@",rowIndex, NSStringFromRect(clipRect), NSStringFromRect([self visibleRect]));
	[super drawRow:rowIndex clipRect:clipRect];
}

- (NSRect)rectOfRow:(int)rowIndex
{
	if (nil == mTimer)
	{
		return [super rectOfRow:rowIndex];	// if no timer, we're not animating, so everything is normal
	}
	else
	{
		return [super rectOfRow:rowIndex];	// if no timer, we're not animating, so everything is normal
	}
}


- (void) doAnimationStep:(id)sender
{
	id parms = [sender userInfo];  // This the dictionary that we created above in -animateToFloatValue:
	NSArray *fromRows	= [parms valueForKey:fromRowsKey];
	NSArray *toRows		= [parms valueForKey:toRowsKey];
	NSDate *startDate	= [parms valueForKey:startDateKey];
	NSTimeInterval sinceNow = [startDate timeIntervalSinceNow];

	NSEnumerator *fromEnum	= [fromRows objectEnumerator];
	NSEnumerator *toEnum	= [toRows objectEnumerator];
	NSNumber *fromRowNumber;

	while (nil != (fromRowNumber = [fromEnum nextObject]) )
	{
		int fromRow = [fromRowNumber intValue];
		int toRow = [[toEnum nextObject] intValue];
		NSRect fromRect = [self rectOfRow:fromRow];
		NSRect toRect = [self rectOfRow:toRow];
		float t, currentValue;

		t = MIN(1.0 , fabs(sinceNow) / sliderAnimationSeconds);  //Looking for a value from zero to one..
		t = easeFunction(t);  // comment this out to make it linear.

		currentValue = fromRect.origin.y + (t * (toRect.origin.y - fromRect.origin.y));
		NSLog(@"row %d position %f", fromRow, currentValue);

		if (fabs(currentValue - toRect.origin.y) <= 0.01)  // allow a bit of "servo jitter" to allow for floating-point messiness
		{
			[self stopAnimating];
			break;
		}
	}
}


- (void) stopAnimating  //This method removes any NSTimer that's trying to move the slider.
{
	[self setTimer:nil];
}

@end

float easeFunction(float t)  // This function implements a sinusoidal ease-in/ease-out for t = 0 to 1.0.  T is scaled to represent the interval of one full period of the sine function, and transposed to lie above the X axis.
{
	return (sin((t * M_PI) - M_PI_2) + 1.0 ) / 2.0;
}
