/*----------------------------------------------------------------------------	 
	 This file is set up for a width of 130 columns, with tab stops
	 every two spaces.
	 
	 HISTORY 
	 
	 	10Oct93	DM	New
----------------------------------------------------------------------------*/

#import <mach/cthreads.h>

#import "AppDelegate.h"
#import "CircleView.h"

static any_t increasingFunction(self)
AppDelegate	*self;
{
	/*---------------------------------------------------------------
	   Simple function that just generates an increasing series
		 of numbers to display.  this runs as an independent
		 thread; we're forked and detached, then grab a connection to 
		 the main thread,and start sending messages to it.
		 
		 All four of these functions share some code.  It should
		 probably be factored out into a single method so the line
		 count can be reduced.
	---------------------------------------------------------------*/
	id		proxy;											// "proxy" object to the main server; essentially "self"
	int		i;													// generic int
		
		// Get a connection to the main server.  This allows us to be detached,
		// yet talk to appkit and the DPS in a thread-safe way.
		
	proxy = [NXConnection connectToName:"fooServer"];
	
		// If we got nothing on the connect attempt, or if the object we got back
		// does not speak our "language", then punt.
		
	if((proxy == nil) || (![proxy conformsTo:@protocol(ServerInterface)]))
		{
			printf("Couldn't get connection to server object, or protocol is hosed\n");
			cthread_exit(0);
		}
	
		// Loop forever, generating an increasing sequence.  We wrap after
		// some arbitrarily large number so we don't get overflow.
		
	while(YES)
		{	
  		i = 0;
			while(i < 128000)
				{
					[proxy showIncreasing:i];							// Tell the main thread to display this number
					i++;
					
					if(!self->isRunning)
						cthread_exit(0);
					cthread_yield();											// Be polite to the scheduler (not required)
				}
		}
		
	return 0;
}

static any_t fibFunction(self)
AppDelegate	*self;
{
 /*------------------------------------------------------------------------
    Much like the above.  We're running as a thread, and attach to the
		DO server and message that.  We just generate a series of fib 
		numbers for display.
 ------------------------------------------------------------------------*/
	id		proxy;															// Proxy to main server object
	int		i, j, sum;
	
		// Get connection to main server
			
	proxy = [NXConnection connectToName:"fooServer"];
	
			// If we got nothing on the connect attempt, or if the object we got back
			// does not speak our "language", then punt.
		
	if((proxy == nil) || (![proxy conformsTo:@protocol(ServerInterface)]))
		{
			printf("Couldn't get connection to server object\n");
			cthread_exit(0);
		}
	
		// Generate new fib numbers forever.  Roll over after some arbitrarily
		// large number to prevent overflow.
		
	while(YES)
		{	
  		i = 0; j = 1; sum = 0;
			[proxy showFib:i];
			
			while(i < 128000)
				{
					sum = i + j;
					[proxy showFib:sum];
					i = j;
					j = sum;
					
					if(!self->isRunning)
						cthread_exit(0);
					
					cthread_yield();										// Be polite to the scheduler (not required)

				}
		}
		
	return 0;
}

static any_t primeFunction(self)
AppDelegate	*self;
{
 /*------------------------------------------------------------------------
    Much like the above.  We're running as a thread, and attach to the
		DO server and message that.  We just generate a series of prime 
		numbers for display.  I have no idea how the algo here works; just
		cut & pasted from something else.
 ------------------------------------------------------------------------*/
	id		proxy;															// Proxy to main server object
	int	 counter, possiblePrime = 5, testLimit;
	BOOL isPrime;
	List *primeList;
	
		// Get connection to main server
			
	proxy = [NXConnection connectToName:"fooServer"];
	
		// If we got nothing on the connect attempt, or if the object we got back
		// does not speak our "language", then punt.
		
	if((proxy == nil) || (![proxy conformsTo:@protocol(ServerInterface)]))
		{
			printf("Couldn't get connection to server object\n");
			cthread_exit(0);
		}
	
		// Sleaze alert: Lists are intended to hold ids, pointers to objects.  If
		// we squint, and don't care a lot about nicities, we can also store ints
		// in them.  The access is pretty slow, since it takes a function call
		// to find the contents of an address.  But we could fix that if we liked
		// by just doing some pointer arithmetic into the List.
		
	primeList = [[List alloc] init];
	[primeList addObject:(id)2];
	[primeList addObject:(id)3];
	
	 while (YES) 														// Repeat forever
    {
			isPrime = YES;
			testLimit = sqrt(possiblePrime);
		 	for (counter=1; isPrime && ((int)[primeList objectAt:counter]<=testLimit); counter++)
            if (possiblePrime % (int)[primeList objectAt:counter] == 0)
                isPrime = NO;
								
        if (isPrime)
					{
						[primeList addObject:(id)possiblePrime];
						[proxy showPrime:possiblePrime];
					}
		 possiblePrime += 2;
		 
		 if(!self->isRunning)
						cthread_exit(0);
		
		cthread_yield();											// Be polite to the scheduler (not required)

		 if(possiblePrime > 128000)						// Begin again to prevent overflow
		 	{
		 		possiblePrime = 5;
				[primeList free];
				primeList = [[List alloc] init];
				[primeList addObject:(id)2];
				[primeList addObject:(id)3];
			}
	}
 
 return 0;
};																				// end of primeFunction

static any_t circleFunction(self)
AppDelegate	*self;
{
 /*------------------------------------------------------------------------
    Much like the above.  We're running as a thread, and attach to the
		DO server and message that.  We generate a series of points and
		radii, and send those to the main thread server.  that draws
		the circles. 
 ------------------------------------------------------------------------*/
	id			proxy;															// Proxy to main server object
	int			maxHeight = 156, maxWidth = 255;		// aprox. dimensions of the View
	NXPoint	center;
	float		radius;
	int			x,y,r;															// int versions of x, y, radius

		// Get connection to main server.  Yeah, this common code ought to
		// be factored out into its own method.  Sue me.
			
	proxy = [NXConnection connectToName:"fooServer"];
	
		// If we got nothing on the connect attempt, or if the object we got back
		// does not speak our "language", then punt.
		
	if((proxy == nil) || (![proxy conformsTo:@protocol(ServerInterface)]))
		{
			printf("Couldn't get connection to server object\n");
			cthread_exit(0);
		}
	
		// Generate a series of points and radii, and pass them off 
		// to the main thread for drawing. 
			
	srandom(1);														// initialize rng
	
	while(YES)														// loop forever
		{
			x = random(); y = random(); r = random();
						
				// Get them scaled back to about the size of the view.
		
			center.x = (float)(((double)x/(double)MAXINT) * (double)maxWidth);
			center.y = (float)(((double)y/(double)MAXINT) * (double)maxHeight);
			radius   = (float)((((double)r/(double)MAXINT)*2.0) * (double)maxHeight);
		
			[proxy drawRandomCircleAt:center withRadius:radius];
			
			if(!self->isRunning)
						cthread_exit(0);
			
			cthread_yield();								// Be polite to the scheduler.

		}
	
	return 0;
}	
	

@implementation AppDelegate

	// The main methods called by the threads, which output things to the screen.  
	// These are the implementations of the protocol declaration.
	
- showIncreasing												// Show an increasing number in the window
	:(int)pNumber;												// INPUT: the number to show
{ 
	/*-------------------------------------------------------------------------
	   Make an addition to the scrolling display of data in the ScrollView
		 for the "increasing number" display.
	-------------------------------------------------------------------------*/
	Text		*textObj;
	char		theNumber[256];
	
	sprintf(theNumber, "%d\n", pNumber);
	textObj = [increasingDisp docView];
	
	[self addToText:textObj string:theNumber];
	
	return self;														
}																				// end of showIncreasing
	
- showPrime															// Show a prime number in the window
	:(int)pPrime;													// INPUT: the prime to show
{ 
/*-------------------------------------------------------------------------
	   Make an addition to the scrolling display of data in the ScrollView
		 for the "increasing prime" display.
	-------------------------------------------------------------------------*/

	Text		*textObj;
	char		theNumber[256];
	
	sprintf(theNumber, "%d\n", pPrime);
	textObj = [primeDisp docView];
	
	[self addToText:textObj string:theNumber];

	return self;														
}																				// end of showPrime:
	
- showFib																// Show a fibonaci number in the window
	:(int)pFib;														// INPUT: the fib to show
{ 
	/*-------------------------------------------------------------------------
	   Make an addition to the scrolling display of data in the ScrollView
		 for the "increasing fibonacci" display.
	-------------------------------------------------------------------------*/

	Text		*textObj;
	char		theNumber[256];
	
	sprintf(theNumber, "%d\n", pFib);
	textObj = [fibDisp docView];
	
	[self addToText:textObj string:theNumber];
		
	return self;														
}																				// end of showFib:
	
- drawRandomCircleAt										// Draw a circle at the given point with the given radius
	:(NXPoint)pPoint											// INPUT: where to draw it at (the center)
	withRadius:(float)pRadius;						// INPUT: the radius
{ 
	/*-------------------------------------------------------------------------------
	   Add a circle to the view that displays the random circles.  This also adds
		 a circle to the display list kept in the CircleView, which is probably
		 overkill.  But we will be able to redraw it in its current state anywhere.
	-------------------------------------------------------------------------------*/
	[circleView addCircleAt:pPoint withRadius:pRadius];
	[circleView display];
	
	return self;
}

// App initialization delegate method

- appDidInit:sender;
{	
  	// Vend ourselves to the universe (and our threads, too.)
		// This is run from the appkit; the idea is to allow anyone
		// to call us, and we handle all talking to the appkit.
	
	[NXConnection setDefaultTimeout:200000];				// Timeout period, in milliseconds
		
	connection = [NXConnection registerRoot:self withName:"fooServer"];
	[connection runFromAppKit];
			
	return self;
}

- doThreads:sender;											// Starts doing things--slamming numbers to the screen
{
  /*---------------------------------------------------------------
	   Do some stuff. Namely, start the threads that display
		 things to the screen. We have functions that do nothing 
		 but generate output; they message us via distributed objects
		 and tell us what to display, and we do it.
	---------------------------------------------------------------*/
	
		// Clear out any existing cruft in the views.
	
	[circleView emptyCircles];
	[circleView display];
	
	[[increasingDisp docView] selectAll:self];
	[[increasingDisp docView] delete:self];
	
	[[fibDisp docView] selectAll:self];
	[[fibDisp docView] delete:self];

	[[primeDisp docView] selectAll:self];
	[[primeDisp docView] delete:self];

	
		// Break off all the threads for the stuff we want to do-- draw circles,
		// calculate primes, whatever.

		// "I woke up this morning with a bad hangover, and my cthread was
		//  missing.  This happens all the time.  It's detachable."
		//       -- Not King Missile

	isRunning = YES;
	
	cthread_detach(cthread_fork(increasingFunction, self));
	cthread_detach(cthread_fork(fibFunction, self));
	cthread_detach(cthread_fork(primeFunction, self));
	cthread_detach(cthread_fork(circleFunction, self));
		
	return self;
}

- stopThreads:sender;										// Stops all the treads
{
  /*-----------------------------------------------------------------------
	   Stops all the threads, while leaving the display as is.
	-----------------------------------------------------------------------*/
	
	isRunning = NO;
	
	return self;
}	
	
	
- addToText															// Utility method--adds text at end of given text obj
	:(Text*)pTextObj											// INPUT: text obj to add to
	string:(char*)pString;								// INPUT: the text we're adding
{
  /*------------------------------------------------------------------------------
	   This would be better implemented as a category method to the Text object,
		 but that would probably just confuse things for the rookies out there.
		 We have very similar code in three of our functions, that just add
		 a number to the end of a scrolling text display.  This does that, given
		 the text object passed in and the text to append.
		 
		 If we _did_ use a category method, everyone could use this method, which
		 is pretty useful.
	------------------------------------------------------------------------------*/
	int		length;													// How many chars are in the text object
		
	length = [pTextObj textLength];				// find out how long...
	[pTextObj setSel:length :length];			// get a null selection at the end...
	[pTextObj replaceSel:pString];				// and replace it with our new text
	
	[pTextObj scrollSelToVisible];				// make sure we can see it.
	
	return self;
}

@end
