#import <string.h>
#import <appkit/Button.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/NXImage.h>
#import <appkit/TextField.h>
#import "Balder.h"
#import "RagnarokListener.h"
#import "RagnarokSpeaker.h"
#import "InvitationListener.h"
#import "ReplySpeaker.h"

@implementation Balder

const char *portname(appname)
const char *appname;
{
	static char result[40];
	
	strcpy(result,"Ragnarok");
	strcat(result,"(");
	strncat(result, appname,20);
	strcat(result,")");
	
	return result;
}

- appDidInit:sender
{
	publicListener = [[InvitationListener alloc] init];
	[publicListener setDelegate:self];
	[publicListener checkInAs:portname(appName)];
	[publicListener addPort];
	[statusText setStringValue:"Unconnected"];
	connected = NO;
	privateListener = [[RagnarokListener alloc] init];
	[privateListener usePrivatePort];
	[privateListener setDelegate:self];
	[privateListener addPort];
	privateSpeaker = [[RagnarokSpeaker alloc] init];
	return self;
}

- appWillTerminate:sender
{
	if (connected) {
		[self disconnect];
	}
	return self;
}

- (int)invitationFrom:(char *)username onHost:(char *)hostname RSVP:(port_t )rsvpPort Face:(char *)face length:(int)length
{
	id replySpeaker;
	char *ourImageData;
	int ourImageLength;
	
	replySpeaker = [[ReplySpeaker alloc] init];
	[replySpeaker setSendPort:rsvpPort];

	if (connected) { /* we're already connected, reject the invitation */
		[replySpeaker invitationRejected];
	} else { /* accept the invitation */
		ourImageData = [self writeImageToBuf:[NXImage findImageNamed:"app"] length:&ourImageLength];
		[replySpeaker invitationAcceptedMyFace:ourImageData length:ourImageLength];
	}
	return 0;
}

- (int)Game:(char *)game length:(int)length Side:(int)side YourPort:(port_t  *)yourPort MyPort:(port_t)myPort MyFace:(char *)myFaceData length:(int)mflen From:(char *)username onHost:(char *)hostname
{
	NXStream *gameStream;
	NXTypedStream *ts;

	/* free whatever old currentState there was */
	[currentState free];
	/* now we're connected */
	connected = YES;
	[statusText setStringValue:"Connected"];
	[opponentName setStringValue:username];
	[opponentFace setImage:[self readImageFromBuf:myFaceData length:mflen]];
	/* read the GameState object from the message */
	gameStream = NXOpenMemory(game,length,NX_READONLY);
	ts = NXOpenTypedStream(gameStream, NX_READONLY);
	currentState = NXReadObject(ts);
	NXCloseTypedStream(ts);
	NXClose(gameStream);
	/* tell them how to contact us */
	*yourPort = [privateListener listenPort];
	/* connect our speaker to them */
	[privateSpeaker setSendPort:myPort];
	/* which side we're playing */
	ourSide = !side;
	/* if it's our turn... */
	if (ourSide == currentState->whoseTurn) {
		[statusText setStringValue:"Thinking"];
		[self perform:@selector(decide:) with:self afterDelay:1 cancelPrevious:YES];
	}
	return 0;
}

- (int)submitMoveFrom:(int)from To:(int)to
{
	struct move theMove;
	
	theMove.from = from;
	theMove.to = to;
	[currentState makeMove:theMove];
	if (ourSide == currentState->whoseTurn) {
		[statusText setStringValue:"Thinking"];
		[self perform:@selector(decide:) with:self afterDelay:1 cancelPrevious:YES];
	}
	return 0;
}

- (int)submitTentativeMoveFrom:(int)from To:(int)to
{
	/* do nothing */
	return 0;
}

- (int)submitNoTentativeMove
{
	/* do nothing */
	return 0;
}

- (int)submitUndos:(int)howMany
{
	int counter;
	
	for (counter=0; counter<howMany; counter++) {
		[currentState undoMove];
	}
	if (ourSide == currentState->whoseTurn) {
		[statusText setStringValue:"Thinking"];
		[self perform:@selector(decide:) with:self afterDelay:1 cancelPrevious:YES];
	}
	return 0;
}

- (int)submitResetGame
{
	[currentState resetState];
	if (ourSide == currentState->whoseTurn) {
		[statusText setStringValue:"Thinking"];
		[self perform:@selector(decide:) with:self afterDelay:1 cancelPrevious:YES];
	}
	return 0;
}

- (int)submitMessage:(char *)aString
{
	/* do nothing */
	return 0;
}

- (int)goodbye
{
	[self disconnect];
	return 0;
}

- (int)pleaseAllow:(int)tag Undo:(int)howMany
{
	int errorStatus;

	/* always allow */
	errorStatus = [privateSpeaker allow:tag];
	if (errorStatus != 0)
		[self disconnect];
	return 0;
}

- (int)pleaseAllow:(int)tag StartOver:(int)ignored
{
	int errorStatus;

	/* always allow */
	errorStatus = [privateSpeaker allow:tag];
	if (errorStatus != 0)
		[self disconnect];
	return 0;
}

- (int)pleaseHurryUp:(int)tag
{
	/* do nothing */
	return 0;
}

- (int)allow:(int)tag
{
	/* do nothing */
	return 0;
}

- (int)refuse:(int)tag
{
	/* do nothing */
	return 0;
}

- (int)ok:(int)tag
{
	/* do nothing */
	return 0;
}

- disconnect
{
	[privateSpeaker goodbye];
	connected = NO;
	port_deallocate(task_self(),[privateSpeaker sendPort]);
	[statusText setStringValue:"Unconnected"];
	[opponentName setStringValue:NULL];
	[opponentFace setImage:nil];
	return self;
}

- (const char *)writeImageToBuf:theImage length:(int *)length
{
	static NXStream *s = NULL;
	char *buf;
	int ignored;
	NXBitmapImageRep *theRep;
	
	if (s) { /* free last usage */
		NXCloseMemory(s, NX_FREEBUFFER);
	} 
	s = NXOpenMemory(NULL, 0, NX_WRITEONLY);
	theRep = (NXBitmapImageRep *)[theImage bestRepresentation];
	[theRep writeTIFF:s];
	NXGetMemoryBuffer(s, &buf, length, &ignored);
	return buf;
}

- readImageFromBuf:(const char *)theBuf length:(int)length
{
	NXStream *s;
	id theRep, theImage;
	
	s = NXOpenMemory(theBuf, length, NX_READONLY);
	theRep = [[NXBitmapImageRep alloc] initFromStream:s];
	theImage = [[NXImage alloc] init];
	[theImage useRepresentation:theRep];
	NXCloseMemory(s, NX_FREEBUFFER);
	return theImage;
}

- doMove:(struct move)theMove
{
	int errorStatus;
	
	errorStatus = [privateSpeaker submitMoveFrom:(theMove.from) To:(theMove.to)];
	[statusText setStringValue:"Connected"];
	if (errorStatus == 0) {
		[currentState makeMove:theMove];
	} else {
		[self disconnect];
	}
	return self;
}

- decide:sender
{
	int fromX, fromY, toX, toY;
	struct move theMove;
	
	/* just do the first legal move we find */
	for (fromX=0; fromX<11; fromX++) {
	 for (fromY=0; fromY<11; fromY++) {
	  for (toX=0; toX<11; toX++) {
	   for (toY=0; toY<11; toY++) {
		 theMove.from = XYTONUM(fromX,fromY);
		 theMove.to = XYTONUM(toX,toY);
		 if (ourSide == WHITE) { /* look for white moves */
		 	if ((currentState->pieces[theMove.from].who == W_PAWN) &&
					([currentState checkMove:theMove])) {
		 		[self doMove:theMove];
				return self;
			}
		 } else { /* look for black moves */
		 	if (((currentState->pieces[theMove.from].who == B_PAWN) ||
					(currentState->pieces[theMove.from].who == LOKI)) &&
					([currentState checkMove:theMove])) {
		 		[self doMove:theMove];
				return self;
			}
		 }}}}}
	/* we should never get here... */
	return self;
}

@end
