
/* Not generated by Interface Builder */

#import "Animator.h"
#import <appkit/Control.h>
#import <appkit/Button.h>
#import <sys/time.h>

@implementation Animator

// These are our default values for a new Animator.
static float defaultTiming, defaultThreshold;
static SEL defaultAction;
BOOL defaultRunning, defaultContinuous;

+ initialize
{
  [self setDefaultTiming:0.0];
  [self setDefaultThreshold:NX_BASETHRESHOLD];
  [self setDefaultAction:@selector( animate:)];
  [self setDefaultRunning:NO];
  [self setDefaultContinuous:YES];
  return self;
}

// Where our DPSTimedEntry ends up.  data should be self, so that the correct
// Animator instance can be called to sendAction.
static void doAnimator( DPSTimedEntry te, double curTime, void *data)
{
  if( ![(id)data continuous])		// If one-shot,
    [(id)data stop:(id)data];		// stop it.
  [(id)data sendAction];
}

+ new				// create a new Animator.
{
    self = [super new];
    target=nil;
    [self setTiming:defaultTiming];
    [self setThreshold:defaultThreshold];
    running=NO;
    [self setRunning:defaultRunning];
    [self setAction:defaultAction];
    [self setContinuous:defaultContinuous];
    startTime=elapsed=-1.0;
    return self;
}
+ setDefaultTiming:(float)dtiming
{
  defaultTiming=dtiming;
  return self;
}
+ setDefaultThreshold:(float)dthreshold
{
  defaultThreshold=dthreshold;
  return self;
}
+ setDefaultAction:(SEL)daction
{
  defaultAction=daction;
  return self;
}
+ setDefaultRunning:(BOOL)state
{
  defaultRunning=state;
  return self;
}
+ setDefaultContinuous:(BOOL)state
{
  defaultContinuous=state;
  return self;
}

- free
{
    [self stop:self];
    return [super free];
}

- setTarget:anObject
{
    if( target==anObject)	// if already set,
      return self;		// just return.
    target = anObject;
    if( target && [anObject respondsTo:@selector( setAnimator:)])
      [anObject setAnimator:self];	// anObject can store us,
					// or just set us up.
    return self;
}
- target { return target; }

- setTiming:(float)t
{
  timing=t;
  if( running)
    [[self stop:self] start:self];
  return self;
}
-(float)timing { return timing; }

- setThreshold:(float)t
{
  threshold=t;
  if( running)
    [[self stop:self] start:self];
  return self;
}
-(float)threshold { return threshold; }

-(double)doubleValue		// return the amount of time since last call.
{
  return running ? elapsed : -1.0;
}
-(float)floatValue		// return as a float.
{
  return [self doubleValue];
}
-(int)intValue
{
  return [self doubleValue];
}
-(BOOL)continuous		// return the continuous flag.
{
  return continuous;
}
- setContinuous:(BOOL)state	// set the continuous flag.
{
  continuous=state;
}

- setAction:(SEL)theAction
{
  action=theAction;
  return self;
}
-(SEL)action { return action; }

- setRunning:(BOOL)state
{
  return state==running ? self : [self toggleRun:self];
}
- (BOOL)running	{ return running; }

// These both force non-continuous.
- callAfter:(double)sec		// wait and call me then.
{
  [self setContinuous:NO];
  [self setTiming:sec];
  [self start:self];
  return self;
}
- callAt:(double)absSec		// call at this time.
{
  [self resetValue];		// get the current time.
  if( absSec-startTime<0)
    absSec+=86400;
  return [self callAfter:absSec-startTime];
}

- sendAction:(SEL)theAction to:theTarget
{
  struct timeval curTime;
  double now;
  gettimeofday( &curTime, 0);
  now=(float)(curTime.tv_sec)+(float)(curTime.tv_usec)/1000000.0;
  elapsed=now-startTime;
  if( theAction && theTarget && [theTarget respondsTo:theAction])
    [theTarget perform:theAction with:self];
  return self;
}
- sendAction
{
  return [self sendAction:action to:target];
}

- resetValue:(double)value
{
  startTime=value;
  elapsed=0;
  return self;
}
- resetValue
{
    struct timeval curTime;
    gettimeofday( &curTime, 0);
    return [self resetValue:(float)(curTime.tv_sec)+(float)(curTime.tv_usec)/1000000.0];
}
- start:sender
{
    if( !running)
      te=DPSAddTimedEntry( timing, &doAnimator, self, threshold);
    running=YES;
    return [self resetValue];
}
- stop:sender
{
    if( running)
      DPSRemoveTimedEntry( te);
    running=NO;
    return self;
}
- toggleRun:sender
{
  return running ? [self stop:sender] : [self start:sender];
}
- takeTimingFrom:sender
{
  [self setTiming:[sender floatValue]];
  return self;
}
- takeRunningFrom:sender
{
  [self setRunning:[sender state]];
  return self;
}
- takeContinuousFrom:sender	// set continuous by sender.
{
  [self setContinuous:[sender state]];
  return self;
}

- copy
{
  id newAnimator=[super copy];
  [newAnimator setTarget:nil];
  [newAnimator setTiming:[self timing]];
  [newAnimator setThreshold:[self threshold]];
  [newAnimator setAction:[self action]];
  [newAnimator setRunning:[self running]];
  [newAnimator setContinuous:[self continuous]];
  return newAnimator;
}

- awake
{
    if( running)
      {
        running=NO;
        [self start:self];
      }
    return self;
}
- write:(NXTypedStream *)stream
{
    [super write:stream];
    NXWriteObjectReference( stream, target);
    NXWriteTypes(stream, "ffii:", &timing, &threshold, &running, &continuous,
				  &action);
    return self;
}
- read:(NXTypedStream *)stream
{
    [super read:stream];
    target=NXReadObject( stream);
    NXReadTypes(stream, "ffii:", &timing, &threshold, &running, &continuous,
				 &action);
    return self;
}

@end
