static char rcsid[] = "$Id: TestCounter.m,v 1.2 1997/04/07 01:13:47 croehrig Exp $";
/*
 *  Counter:  simple counter object that runs in its own thread.
 */
#import <libc.h>
#import <math.h>
#import <appkit/Button.h>
#import <appkit/Slider.h>
#import <misckit/ThreadedApp.h>
#import "Counter.h"
#import "tprintf.h"

#define INITIAL_INTERVAL	100	// in milliseconds

@interface Counter(CounterPrivate)
- (int)incCount;
- (void)counter:sender;
@end

@implementation Counter : Object

// number of counters created
static int counters;

// global variable shared among all Counter instances
static volatile int *countp;

// lock to synchronize access to countp
static MiscConditionLock *inclock;

#define STOPPED 0
#define RUNNING 1

#define myrandom()	(random()/(double)MAXINT)
+ initialize
{
    if( self == [Counter class] ){
	srandom(0);
	counters = 1;
	inclock = [[MiscConditionLock alloc] initWithCondition:0];
	tprintf_init();
    }
    return self;
}


- init
{
    NXScreen *screen;
    NXRect frame;


    [super init];
    interval = INITIAL_INTERVAL;
    running = NO;
    mynum = counters++;

    lastcount = -1;
    count = 0;
    if( [NXApp loadNibSection:"Counter.nib" owner:self] ){
	// set window title...
	title = (char *) malloc( 20*sizeof(char) );  // should be enough...
	sprintf( title, "Counter %d\n", mynum );
	[window setTitle:title];

	// move to random location
	[window getFrame:&frame andScreen:&screen];
	frame.size.width = screen->screenBounds.size.width 
			  - frame.size.width;
	frame.size.height = screen->screenBounds.size.height
			  - frame.size.height;
	frame.origin = screen->screenBounds.origin;
	frame.origin.x += myrandom()*frame.size.width;
	frame.origin.y += myrandom()*frame.size.height;
	[window moveTo:frame.origin.x:frame.origin.y];
	[window makeKeyAndOrderFront:self];

	// set values
	[slider setIntValue:interval];
	[intervalField setIntValue:interval];
	[countField setIntValue:count];

	runlock = [[MiscConditionLock alloc] initWithCondition:STOPPED];
	// go!
	[self start:self];
    }
    return self;
}

- free
{
    if( running )
	[self stop:self];
    
    [runlock free];
    if(title){
	free(title);
    }
    return [super free];
}


- windowWillClose:sender
{
    [self stop:self];
    [NXApp delayedFree:self];

    return self;
}


// "private" methods

- dispCount:(int)i
{
    if( i != lastcount+1 ){
	tprintf("Counter %d:  lost %d message(s)\n", mynum, i-lastcount-1);
    }
    [countField setIntValue:i];
    lastcount = i;
    return self;
}


- (int)incCount
// a pretty dumb way to increment a counter, but it sure tests
// the callback and lock mechanism...
{
    [inclock lockWhenCondition:0];
    count++;
    countp = &count;
    [inclock unlockWithCondition:mynum];

    return count;
}

    
- (void)counter:sender
{
    int i;

    [runlock lockWhenCondition:RUNNING];
    i = count;
    while( running ){
	// update countField under main thread
	[NXApp callbackTarget:self perform:@selector(dispCount:) with:(id)i];

	// increment the counter...
	[NXApp safeCallbackTarget:self perform:@selector(incCount)];
	[inclock lockWhenCondition:mynum];
	i = *countp;
	[inclock unlockWithCondition:0];
	if( i != count ){
	    tprintf("Counter %d: count mismatch! ret=%d; count=%d\n", 
		mynum, i, count );
	}

	if( interval > 0 )
	    [NXApp sleepCurrentThread:interval];
    }

    [runlock unlockWithCondition:STOPPED];
}


// Actions...

- start:sender
{
    if( !running ){
	[runlock lock];
	[NXApp detachNewThreadSelector:@selector(counter:) toTarget:self
	    withObject:self];
	running = YES;
	[runlock unlockWithCondition:RUNNING];
    }
    return self;
}


- stop:sender
{
    if( running ){
	running = NO;
	// wait for thread to finish
	[runlock lockWhenCondition:STOPPED];
	[runlock unlock];
    }

    return self;
}

- startOrStop:sender
{
    if( [sender state] == 0 ){
	[self start:sender];
    } else {
	[self stop:sender];
    }
    return self;
}


- reset:sender
{
    count = 0;
    return self;
}


- setInterval:sender
{
    int i, max;

    i = [sender intValue];
    max = (int) [slider maxValue];
    if( i < 0 ){
	i = 0;
    } else if( i > max ){
	i = max;
    }
    [slider setIntValue:i];
    [intervalField setIntValue:i];

    interval = i;

    return self;
}

@end
