#include <Foundation/Foundation.h>

@interface TestClass : NSObject
{
  NSData         *bodyData;
  unsigned       accum;
  unsigned       count;
  unsigned       closeFrequency;
  NSArray        *headers;
  NSInputStream  *inStream;
  NSOutputStream *outStream;
}
- (int) runTest;
@end

@implementation TestClass

- (void)dealloc
{
  [headers release];
  [bodyData release];
  [inStream release];
  [outStream release];
  [super dealloc];
}

- (id) init
{
  return self;
}

- (int)runTest
{
  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
  NSRunLoop      *runLoop = [NSRunLoop currentRunLoop];
  NSString       *file,
                 *lengthHeader;
  NSHost         *host = [NSHost currentHost];
  NSStream       *serverStream;
  int            port = [[defs stringForKey: @"Port"] intValue];
  int            lifetime = [[defs stringForKey: @"Lifetime"] intValue];
  
  if (0 == port) port = 4322;
  if (0 == lifetime) lifetime = 30;

  accum = 0;

  closeFrequency = [[defs stringForKey: @"CloseFreq"] intValue];
  count = [[defs stringForKey: @"Count"] intValue];
  if (0 >= count) count=1;

  file = [defs stringForKey: @"FileName"];
  if (nil == file) file = @"ResponseBody.dat";
  bodyData = [[NSData alloc] initWithContentsOfFile: file];
  if (nil == bodyData)
    {
      NSLog(@"Unable to load data from '%@'",file);
      return 1;
    }

  lengthHeader = [NSString stringWithFormat: @"Content-Length: %d\r\n",
			                     [bodyData length]];
  headers = [[NSArray alloc] initWithObjects: @"HTTP/1.1 200 OK\r\n",
                                              @"Content-type: text/plain\r\n",
                                              lengthHeader,
                                              nil];
  serverStream = [GSServerStream serverStreamToAddr: [host address] port: port];
  if (nil == serverStream)
    {
      NSLog(@"Could not create server stream");
      return 1;
    }
  [serverStream setDelegate: self];
  [serverStream scheduleInRunLoop: runLoop forMode: NSDefaultRunLoopMode];
  [serverStream open];

  // only run for a fixed time anyway
  [runLoop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: lifetime]];
  
  return 0;
}

// delegate method for NSStream
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent)streamEvent
{
  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

  switch (streamEvent)
    {
    case NSStreamEventHasBytesAvailable:
      {
	if (theStream != inStream)
	  {
	    if (inStream != nil)
	      {
                [inStream close];
                [inStream removeFromRunLoop: runLoop
                                    forMode: NSDefaultRunLoopMode];
		inStream = nil;
	      }
	    if (outStream != nil)
	      {
		[outStream close];
		[outStream removeFromRunLoop: runLoop
                                     forMode: NSDefaultRunLoopMode];
		outStream = nil;
	      }
	    if (accum == count)
	      {
		[theStream close];
		[theStream removeFromRunLoop: runLoop
                                     forMode: NSDefaultRunLoopMode];
		break;
	      }
	    [(GSServerStream*)theStream acceptWithInputStream: &inStream
                                                 outputStream: &outStream];
	    if (inStream)
	      {
		RETAIN(inStream);
		RETAIN(outStream);
		[inStream scheduleInRunLoop: runLoop
                                    forMode: NSDefaultRunLoopMode];
		[inStream setDelegate: self];
		[inStream open];
	      }
	    else
	      {
		NSLog(@"Accept returned nothing");
	      }
	  }
	else
	  {
	    unsigned char buffer[BUFSIZ];
	    int           readSize;
	    readSize = [inStream read: buffer maxLength: BUFSIZ];
	    // make outStream available for writing
	    [outStream scheduleInRunLoop: runLoop
                                 forMode: NSDefaultRunLoopMode];
	    [outStream setDelegate: self];
	    [outStream open];
	  }
	break;
      }

    case NSStreamEventHasSpaceAvailable:
      {
	NSAssert(theStream == outStream, @"Wrong stream for writing");
	NSMutableData  *payload = [[[NSMutableData alloc] init] autorelease];
	NSMutableArray *headerArray = [[headers mutableCopy] autorelease];
	NSEnumerator  *headerEnumeration;
	id             header;
	BOOL           closeConnection = NO;
	int            written;

	accum++;
	if ((0 != closeFrequency) && (0 == accum % closeFrequency))
	  {
	    closeConnection = YES;
	    [headerArray addObject: @"Connection: close\r\n"];
	  }

	headerEnumeration = [headerArray objectEnumerator];

	while ((header = [headerEnumeration nextObject]))
	  [payload appendData: [header dataUsingEncoding: NSASCIIStringEncoding]];
	[payload appendData: [@"\n" dataUsingEncoding: NSASCIIStringEncoding]];
	[payload appendData: bodyData];

	// provide the reply
	written = [outStream write: [payload bytes] maxLength: [payload length]];
	if (written <=0)
	  {
	    // something seriously wrong....
	    closeConnection = YES;
	    NSLog(@"Unable to write the payload!");
	  }

	// remove from runloop, so we have to wait for another request
	[outStream removeFromRunLoop: runLoop
                             forMode: NSDefaultRunLoopMode];

	if (closeConnection)
	  {
	    [outStream close];
	    outStream = nil;

	    if (nil != inStream)
	      {
		[inStream close];
		[inStream removeFromRunLoop: runLoop
                                    forMode: NSDefaultRunLoopMode];
		inStream = nil;
	      }
	  }
	break;
      }

    case NSStreamEventEndEncountered:
      {
	NSAssert(inStream == theStream || outStream == theStream,
                 NSInternalInconsistencyException);
	[theStream close];
	[theStream removeFromRunLoop: runLoop forMode: NSDefaultRunLoopMode];
	if (theStream == inStream) inStream = nil;
	if (theStream == outStream) outStream = nil;
	break;
      }

    case NSStreamEventErrorOccurred:
      {
	int code = [[theStream streamError] code];
	NSLog(@"Received error %d on stream %p", code, theStream);
	[theStream close];
	[theStream removeFromRunLoop: runLoop forMode: NSDefaultRunLoopMode];
	if (theStream == inStream) inStream = nil;
	if (theStream == outStream) outStream = nil;
	break;
      }
    
    case NSStreamEventOpenCompleted:
      break;
    default:
      NSLog(@"Unknown event %d on stream %p", streamEvent, theStream);
      break;
    }
}

@end

int main(int argc, char **argv)
{
  int result;
  NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];

  result = [[[[TestClass alloc] init] autorelease] runTest];

  [arp release];
  return result;
}
