
/* Generated by Interface Builder */

#import "Board.h"
#import <appkit/obsoleteBitmap.h>
#import <appkit/Application.h>
#import <dpsclient/dpsclient.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <libc.h>
#import "ics_filter.h"
#import "spawn.h"

id myself;

char *pieceNames[] = {"  ", "WP", "WR", "WN", "WB", "WQ", "WK", 
                            "BP", "BR", "BN", "BB", "BQ", "BK" };

#define SQUARESIZE 64.0
#define PIECESIZE 64.0

#define WHITE 'W'
#define BLACK 'B'

@interface BoardDelegate:Object
{
}

- pieceMoved:(int)x1 :(int)y1 :(int)x2 :(int)y2;
@end

void GotABoard( char *line )
{
   [myself parseBoard:line];
}

@implementation Board

- initFrame:(NXRect *)tF
{
  [super initFrame:tF];
  [self initBoard];
  delegate = NULL;
  myself = self;
  who = WHITE;
  clockEntry = NULL;
  setBoardLineFunc( GotABoard );
  isFlipped = NO;
  lastX = -1;
  lastY = -1;
  return self;
}

- (BOOL)acceptsFirstResponder
{
  return YES;
}

- keyDown:(NXEvent *)theEvent
{
  char c;
  
  c = theEvent->data.key.charCode;
  WriteChars( &c, 1 );
  return self;
}

- (BOOL)acceptsFirstMouse
{
    return YES;
}

- mouseDown:(NXEvent *)theEvent
{
  int oldMask;
  int si, sj, ei, ej;
  NXPoint offset, zerozero = {0.0,0.0}, newoffset;
  id pieceBitmap;
  NXRect wFrame, myWindowFrame;
  int thePiece;
  
  /* There are two no-retained windows to try and make the dragging less
     flashy, it doesn't work. I don't know how to appropriately drag a 
     piece */
  id theWindow[2];
  int onWindow, offWindow, i;
  char moveString[20];

  if (theEvent->data.mouse.click == 2) {
    return self;
  }
  offset = theEvent->location;
  [self convertPoint:&offset fromView:nil];
  si = offset.x / SQUARESIZE;
  sj = offset.y / SQUARESIZE;
  pieceBitmap = NULL;
  wFrame.origin.x = si * SQUARESIZE;
  wFrame.origin.y = sj * SQUARESIZE;
  if (isFlipped) {
    si = 7 - si;
    sj = 7 - sj;
  }
  if (squares[si][sj] != EMPTY)
    pieceBitmap = [Bitmap findBitmapFor:pieceNames[squares[si][sj]]];

  thePiece = squares[si][sj];
  squares[si][sj] = EMPTY;
  [self display];
  [window getFrame:&myWindowFrame];
  wFrame.size.width = wFrame.size.height = SQUARESIZE;
  [self convertRect:&wFrame toView:nil];
  wFrame.origin.x += myWindowFrame.origin.x;
  wFrame.origin.y += myWindowFrame.origin.y;
  
  for (i=0;i<2;i++) {
    theWindow[i] = [Window newContent:&wFrame style:NX_PLAINSTYLE
		    backing:NX_NONRETAINED buttonMask:0 defer: NO];
    [theWindow[i] _setWindowLevel:9];
    [theWindow[i] setFreeWhenClosed:NO];
    [[theWindow[i] contentView] setFlip:YES];
    [[theWindow[i] contentView] lockFocus];
    PSsetautofill(NO, [theWindow[i] windowNum]);
    [[theWindow[i] contentView] unlockFocus];
  }
  [theWindow[0] orderFront:self];
  [[theWindow[0] contentView] lockFocus];
  [pieceBitmap composite:NX_SOVER toPoint:&zerozero];
  [[theWindow[0] contentView] unlockFocus];
  onWindow = 0; offWindow = 1;
  oldMask = [window addToEventMask:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
  while (1) {
      theEvent = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
      newoffset = theEvent->location;
      [self convertPoint:&newoffset fromView:nil];
      [theWindow[offWindow] moveTo:wFrame.origin.x + (newoffset.x - offset.x) 
                                  :wFrame.origin.y + (newoffset.y - offset.y)];
      PSorderwindow(NX_OUT, 0, [theWindow[onWindow] windowNum]);
      PSorderwindow(NX_ABOVE, 0, [theWindow[offWindow] windowNum]);
      [[theWindow[offWindow] contentView] lockFocus];
      [pieceBitmap composite:NX_SOVER toPoint:&zerozero];
      [[theWindow[offWindow] contentView] unlockFocus];

      
      i = onWindow;
      onWindow = offWindow;
      offWindow = i;

      if (theEvent->type == NX_MOUSEUP) break;
  }
  for (i=0;i<2;i++) {
    [theWindow[i] orderOut:self];
    [theWindow[i] free];
  }
  [window setEventMask:oldMask];
  offset = theEvent->location;
  [self convertPoint:&offset fromView:nil];
  ei = offset.x / SQUARESIZE;
  ej = offset.y / SQUARESIZE;
  if (isFlipped) {
    ei = 7 - ei;
    ej = 7 - ej;
  }
  squares[ei][ej] = thePiece;
  [self display];

  if ((ei == si) && (ej == sj)) return self; /* Same square! */
  if ((ei < 0) || (ei > 7) || (ej < 0) || (ej > 7))
    return self; /* Off the board */
    
  if ((si == 4) && (ei == 2) &&
      ((squares[si][sj] == WHITE_KING) || (squares[si][sj] == BLACK_KING))) {
      /* Castle queen side */
    strcpy( moveString, "o-o-o\n" );
  } else
    if ((si == 4) && (ei == 6) &&
      ((squares[si][sj] == WHITE_KING) || (squares[si][sj] == BLACK_KING))) {
      /* Castle king side */
    strcpy( moveString, "o-o\n" );
  } else
    sprintf( moveString, "%c%0d-%c%0d\n", si+'a', sj+1, ei+'a', ej+1 );
  WriteChars( moveString, strlen(moveString) );
  lastX = ei;
  lastY = ej;
  return self;
}

- blinkLast:sender
{
   int piece;
   
  if (lastX < 0) return self;
  piece = squares[lastX][lastY];
  squares[lastX][lastY] = EMPTY;
  [self display];
  NXPing();
  [window flushWindow];
  squares[lastX][lastY] = piece;
  [self display];
  NXPing();
  [window flushWindow];
  return self;
}

- stopClock
{
  if (clockEntry) 
    DPSRemoveTimedEntry(clockEntry);
  clockEntry = NULL;
  return self;
}

- clockTick
{
  int min, sec;
  char tstring[20];
  
  if (who == WHITE)
    strcpy( tstring, [whiteTime stringValue] );
  else
    strcpy( tstring, [blackTime stringValue] );
  sscanf( tstring, "%d:%d", &min, &sec );
  if (sec == 0) {
    min--;
    sec = 59;
  } else sec--;
  sprintf(tstring, "%d:%02d", min, sec);
  if ((min == 0) && (sec == 0)) [self stopClock];
  if (who == WHITE)
    [whiteTime setStringValue:tstring];
  else
    [blackTime setStringValue:tstring];
  return self;
}

void clockTick( DPSTimedEntry teNumber, double now,  id self )
{
   [self clockTick];
}

- startClock
{
  [self stopClock];
  clockEntry = DPSAddTimedEntry(1.0, (DPSTimedEntryProc)clockTick, self,
                                NX_MODALRESPTHRESHOLD);
  return self;
}

- maybeDisplay
{
  if ([self isAutodisplay])
    [self display];
  else
    [self setNeedsDisplay:YES];
  return self;
}

void SwitchPosition(id view1, id view2)
{
  NXRect f1, f2;
  
  [view1 getFrame:&f1];
  [view2 getFrame:&f2];
  [view1 setFrame:&f2];
  [view2 setFrame:&f1];
  [view1 display];
  [view2 display];
}

- flipBoard:sender
{
  [window disableFlushWindow];
  isFlipped = !isFlipped;
  [self maybeDisplay];
  SwitchPosition( whiteName, blackName );
  SwitchPosition( whiteTime, blackTime );
  SwitchPosition( whiteStrength, blackStrength );
  [[window reenableFlushWindow] flushWindow];
  return self;
}

- (BOOL)isFlipped
{
  return isFlipped;
}

- parseBoard:(char *)line
{
  int i,j;
  int k;
  int piece;
  char templine[1024];
  int tmpint;
  
  [window disableFlushWindow];
  k = 40;
  for (j=0;j<8;j++)
    for (i=0;i<8;i++) {
      switch(line[k]) {
        case 'p':
	  piece = BLACK_PAWN;
	  break;
        case 'r':
	  piece = BLACK_ROOK;
	  break;
        case 'n':
	  piece = BLACK_KNIGHT;
	  break;
        case 'b':
	  piece = BLACK_BISHOP;
	  break;
        case 'q':
	  piece = BLACK_QUEEN;
	  break;
        case 'k':
	  piece = BLACK_KING;
	  break;
        case 'P':
	  piece = WHITE_PAWN;
	  break;
        case 'R':
	  piece = WHITE_ROOK;
	  break;
        case 'N':
	  piece = WHITE_KNIGHT;
	  break;
        case 'B':
	  piece = WHITE_BISHOP;
	  break;
        case 'Q':
	  piece = WHITE_QUEEN;
	  break;
        case 'K':
	  piece = WHITE_KING;
	  break;
        case ' ':
	  piece = EMPTY;
	  break;
	default:
	  fprintf( stderr, "Problem parsing board string\n" );
	  return self;
      }
      squares[i][j] = piece;
      k++;
    }
  strncpy(templine, &line[104], 3);
  templine[3] = '\0';
  sscanf(templine, "%d", &tmpint);
  sprintf( templine, "%d", tmpint );
  [moveNumber setStringValue:templine];

  if (line[107] == WHITE) {
    who = WHITE;
    [toMove setStringValue:"White To Move"];
  } else {
    who = BLACK;
    [toMove setStringValue:"Black To Move"];
  }
    
  strncpy(templine, &line[112], 5);
  templine[5] = '\0';
  sscanf(templine, "%d", &tmpint);
  sprintf(templine, "%d:%02d", tmpint/60, tmpint % 60);
  [whiteTime setStringValue:templine];
  if ((who == WHITE) && (tmpint != 0)) [self startClock];

  strncpy(templine, &line[117], 5);
  templine[5] = '\0';
  sscanf(templine, "%d", &tmpint);
  sprintf(templine, "%d:%02d", tmpint/60, tmpint % 60);
  [blackTime setStringValue:templine];
  if ((who == BLACK) && (tmpint != 0)) [self startClock];

  for (i=0;line[6+i] != ' ';i++)
    templine[i] = line[6+i];
  templine[i] = '\0';
  [whiteName setStringValue:templine];
  
  for (i=0;line[23+i] != ' ';i++)
    templine[i] = line[23+i];
  templine[i] = '\0';
  [blackName setStringValue:templine];
  
  strncpy(templine, &line[108], 2);
  templine[2] = '\0';
  sscanf(templine, "%d", &tmpint);
  sprintf( templine, "%d", tmpint );
  [whiteStrength setStringValue:templine];

  strncpy(templine, &line[110], 2);
  templine[2] = '\0';
  sscanf(templine, "%d", &tmpint);
  sprintf( templine, "%d", tmpint );
  [blackStrength setStringValue:templine];

  for (i=0;line[122+i] != '@';i++)
    templine[i] = line[122+i];
  templine[i] = '\0';
  [lastMove setStringValue:templine];
  if (!strcmp(templine, "o-o" ) ) {
    lastX = 6;
    if (who == BLACK)
      lastY = 0;
    else
      lastY = 7;
  } else if (!strcmp(templine, "o-o-o" ) ) {
    lastX = 2;
    if (who == BLACK)
      lastY = 0;
    else
      lastY = 7;
  } else {
    lastX = templine[5]-'a';
    lastY = templine[6]-'1';
  }

  
  strncpy(templine, &line[3], 3);
  templine[3] = '\0';
  sscanf(templine, "%d", &tmpint);
  sprintf( templine, "%d", tmpint );
  [gameNumber setStringValue:templine];
  
  [self maybeDisplay];
  [[window reenableFlushWindow] flushWindow];
  return self;
}

- drawSelf:(NXRect *)rects : (int)count
{
  int i,j;
  NXPoint ll;

  for (i=0;i<8;i++)
    for (j=0;j<8;j++) {
      ll.x = i * SQUARESIZE;
      ll.y = j * SQUARESIZE;
      if ((j+i) & 0x1)
        PSsetgray(NX_WHITE);
      else
        PSsetgray(NX_DKGRAY);
      PSrectfill(ll.x, ll.y, SQUARESIZE, SQUARESIZE);
    }
  for (i=0;i<8;i++)
    for (j=0;j<8;j++) {
      if (isFlipped) {
	ll.x = (7-i) * SQUARESIZE;
	ll.y = (6-j) * SQUARESIZE;
      } else {
	ll.x = i * SQUARESIZE;
	ll.y = j * SQUARESIZE;
      }
      if (squares[i][j] != EMPTY) {
        [[Bitmap findBitmapFor:pieceNames[squares[i][j]]] composite:NX_SOVER
                                                            toPoint:&ll];
      }
    }
  return self;
}

- initBoard
{
  int i,j;

  for (i=0;i<8;i++)
    for (j=0;j<8;j++)
      squares[i][j] = EMPTY;
  for (i=0;i<8;i++) squares[i][1] = WHITE_PAWN;
  squares[0][0] = squares[7][0] = WHITE_ROOK;
  squares[1][0] = squares[6][0] = WHITE_KNIGHT;
  squares[2][0] = squares[5][0] = WHITE_BISHOP;
  squares[3][0] = WHITE_QUEEN;
  squares[4][0] = WHITE_KING;
  for (i=0;i<8;i++) squares[i][6] = BLACK_PAWN;
  squares[0][7] = squares[7][7] = BLACK_ROOK;
  squares[1][7] = squares[6][7] = BLACK_KNIGHT;
  squares[2][7] = squares[5][7] = BLACK_BISHOP;
  squares[3][7] = BLACK_QUEEN;
  squares[4][7] = BLACK_KING;
  [self maybeDisplay];
  return self;
}

- setBoard:(int **)newb
{
  int i,j;
  
  for (i=0;i<8;i++)
    for (j=0;j<8;j++)
      squares[i][j] = newb[i][j];
  [self maybeDisplay];
  return self;
} 

- setDelegate:anObject
{
  delegate = anObject;
  return self;
}

- appDidInit:sender
{
   [window makeKeyAndOrderFront:self];
   [window makeFirstResponder:self];
   return self;
}


@end
