#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/times.h>
#include <sys/file.h>
#include <pwd.h>


#import <appkit/appkit.h>

#import "chess_strings.h"
#import "Chess.h"
#import "gnuchess.h"
#import "gnuglue.h"
#import "Clock.h"
#import "gnuchess.h"
#import "Board.h"
#import "ResponseMeter.h"

#define LEFT_CASTLE   0x1
#define RIGHT_CASTLE  0x2

extern short GameQueens[240];
extern short PieceList[2][16];
extern short PieceCnt[2];
extern algbr();
extern castle();
/* extern getsectdata(); */
extern distance();
extern InitializeStats();
extern SqAtakd();

struct tms tmbuf1,tmbuf2;

OutputMove()
{
  int i, r1, c1, r2, c2, castle_flag;
#ifdef NeXT_DEBUG
  printf( "OutputMove:  " );
  printf( "Computer move is %s, ", mvstr1 );
  printf( "mask 0x%04x\n", root->flags );
#endif
  if( root->flags & draw )
    [NXApp setFinished: DRAW_GAME];

  if( [[NXApp clockWindow] isVisible] ){  
    if( player == white )
      [[NXApp whiteMeter] displayFilled];
    else
      [[NXApp blackMeter] displayFilled];
  }

  if( !*mvstr1 ){
#ifdef NeXT_DEBUG
    printf( "NO COMPUTER MOVE\n" );
#endif
    return;
  }
  
  convert_algbr( mvstr1, &r1, &c1, &r2, &c2, &castle_flag );
  [[NXApp gameBoard] highlightSquareAt: r1 : c1];
  
  [[NXApp gameBoard] slidePieceFrom: r1 : c1 to: r2 : c2];
  [NXApp storePosition: r2 : c2];
  [[NXApp gameBoard] layoutBoard: board color: color];
  [[NXApp gameBoard] display];
  NXPing();

  [[NXApp gameBoard] highlightSquareAt: r2 : c2];

#ifdef NeXT_DEBUG
  printf( "White has %d pieces\n", PieceCnt[0] );
  for( i = 0; i <= PieceCnt[0]; i++ )
    printf( "%02d ", board[PieceList[0][i]] );
  printf( "\n" );
  printf( "Black has %d pieces\n", PieceCnt[1] );
  for( i = 0; i <= PieceCnt[1]; i++ )
    printf( "%02d ", board[PieceList[1][i]] );
  printf( "\n" );
#endif    
}
  
SetTimeControl()
{
#ifdef NeXT_DEBUG
  printf( "SetTimeControl\n" );
#endif
  if (TCflag)
    {
      TimeControl.moves[white] = TimeControl.moves[black] = TCmoves;
      TimeControl.clock[white] = TimeControl.clock[black] = 60*(long)TCminutes;
    }
  else
    {
      TimeControl.moves[white] = TimeControl.moves[black] = 0;
      TimeControl.clock[white] = TimeControl.clock[black] = 0;
      Level = 60*(long)TCminutes;
    }
  et = 0;
  ElapsedTime(1);
}

SelectLevel()
{
}

ElapsedTime( short iop ) 
/* 
   Determine the time that has passed since the search was started. If 
   the elapsed time exceeds the target (ResponseTime+ExtraTime) then set 
   timeout to true which will terminate the search. 
*/
{
  NXEvent peek_ev, *get_ev;
  
  et = time((long *)0) - time0;
  if (et < 0)
    et = 0;
  ETnodes += 50;
  if( !iop ){
    if( [[NXApp clockWindow] isVisible] ){
      if( player == white )
	[[NXApp whiteMeter] display];
      else
	[[NXApp blackMeter] display];
      NXPing();
    }
  }
#ifdef TIME_STUFF
  printf( "ResponseTime %d, ExtraTime %d, Sdepth %d, iop %d, et %d, et0 %d\n",
        ResponseTime, ExtraTime, Sdepth, iop, et, et0 );
#endif
  if (et > et0 || iop == 1){
    if (et > ResponseTime+ExtraTime && Sdepth > 1){
      timeout = true;
    }
    et0 = et;
    if (iop == 1){
      time0 = time((long *)0);
      et0 = 0;
    }
#ifdef PROFILE    
    (void) times(&tmbuf2);
    cputimer = 100*(tmbuf2.tms_utime - tmbuf1.tms_utime) / HZ;
    if (cputimer > 0) 
      evrate = (100*NodeCnt)/(cputimer+100*ft);
    else
      evrate = 0;
#endif      
    ETnodes = NodeCnt + 50;
    UpdateClocks();
  }

  if( [NXApp peekNextEvent: NX_MOUSEDOWNMASK into: &peek_ev] ){
    get_ev = [NXApp getNextEvent: NX_MOUSEDOWNMASK];
    [NXApp sendEvent: get_ev];
#ifdef NeXT_DEBUG
    printf( "mousedown event, sending to application\n" );
#endif
  }
}


UpdateClocks()
{
}
  

ShowResults( short score, unsigned short bstline[], char ch )
{
#ifdef NeXT_DEBUG
  printf( "ShowResults:  " );
  printf("score = %d",score);
  printf("    %5s\n", mvstr1 );
#endif
}


GameEnd( short score )
{
  NXBeep();
  if( root->flags & draw ){
    [NXApp setFinished: DRAW_GAME];
  }
  else if( score == 9998 ){
    printf( "score %d, winner %d\n", score, winner );
    [NXApp setFinished: winner == black ? BLACK_MATE : WHITE_MATE];
  }
  else if( score == -9999 ){
    if( bothsides ){
      printf( "score %d, winner %d\n", score, winner );
      if( winner != -1 )
	[NXApp setFinished: winner == white ? WHITE_MATE : BLACK_MATE];
      else
        [NXApp setFinished: WHITE_MATE];
    }
    else
      [NXApp setFinished: OPPONENT_MATE ];
  } 
}

ClrScreen()
{
}

UpdateDisplay( short f, short t, short flag, short iscastle )
{
#ifdef NeXT_DEBUG
  printf( "Update Display:  from %d, to %d, flag 0x%04x, iscastle %d, InChk %d\n",
     f, t, flag, iscastle, InChk );
#endif
  [[NXApp gameBoard] layoutBoard: board color: color];
  [[NXApp gameBoard] display];
}

InCheck()
{
  int incheck = -1;
  
  if( SqAtakd( PieceList[computer][0], opponent ) )
    incheck = computer;
  if( SqAtakd( PieceList[opponent][0], computer ) )
    incheck = opponent;
  if( incheck == black )
    ShowMessage( "Black is in check" );
  else if( incheck == 0 )
    ShowMessage( "White is in check" );
}
    
GetOpenings()

/*
   Read in the Opening Book file and parse the algebraic notation for a 
   move into an unsigned integer format indicating the from and to 
   square. Create a linked list of opening lines of play, with 
   entry->next pointing to the next line and entry->move pointing to a 
   chunk of memory containing the moves. More Opening lines of up to 256 
   half moves may be added to gnuchess.book. 
*/

{
  int c,i,j,side;
  struct BookEntry *entry;
  unsigned short mv,*mp,tmp[100];
  int book_size;
  char *book_data;
  NXStream *book_stream;
  
  
  book_data = (char *)getsectdata( "__BOOK", "gnuchess.book", &book_size);
  if( book_data != NULL ){
    book_stream = NXOpenMemory( book_data, book_size, NX_READONLY );
    Book = NULL;
    i = 0; side = white;
    while((c = parse( book_stream, &mv, side)) >= 0 ){
      if(c == 1){
	tmp[++i] = mv;
	side = otherside[side];
      }
      else if (c == 0 && i > 0){
	entry = (struct BookEntry *)malloc(sizeof(struct BookEntry));
	mp = (unsigned short *)malloc((i+1)*sizeof(unsigned short));
	entry->mv = mp;
	entry->next = Book;
	Book = entry; 
	for (j = 1; j <= i; j++) *(mp++) = tmp[j];
	*mp = 0;
	i = 0; side = white;
      }
    }
    NXCloseMemory( book_stream, NX_SAVEBUFFER );
  }
}


int 
parse( NXStream *book_stream, unsigned short *mv, short side )
{
  int c,i,r1,r2,c1,c2;
  char s[100];
  
  while ((c = NXGetc( book_stream )) == ' ');
  i = 0; s[0] = c;
  while (c != ' ' && c != '\n' && c != EOF)
    s[++i] = c = NXGetc( book_stream );
  s[++i] = '\0';
  if (c == EOF)
    return(-1);
  if (s[0] == '!' || i < 3){
    while (c != '\n' && c != EOF) c = NXGetc( book_stream );
    return(0);
  }
  if (s[4] == 'o'){
    if (side == black)
      *mv = 0x3C3A;
    else
      *mv = 0x0402;
  }
  else if (s[0] == 'o'){
    if (side == black)
      *mv = 0x3C3E;
    else *mv = 0x0406;
  }
  else{
    c1 = s[0] - 'a'; r1 = s[1] - '1';
    c2 = s[2] - 'a'; r2 = s[3] - '1';
    *mv = (locn[r1][c1]<<8) + locn[r2][c2];
  }
  return(1);
}

ShowDepth( char ch )
{
#ifdef NeXT_DEBUG_X
  printf( "ShowDepth:  %d%c   max %d\n", Sdepth, ch, MaxSearchDepth );
#endif
}

ShowCurrentMove( short pnt, short f, short t )
{
#ifdef NeXT_DEBUG
  printf( "ShowCurrentMove\n" );
  algbr(f,t,false);
  printf("(%2d) %4s",pnt,mvstr1);
#endif
}


ShowSidetomove()
{
#ifdef NeXT_DEBUG
  printf( "ShowSidetomove\n" );
  if (player == white) 
    printf("%2d:   WHITE\n",1+(GameCnt+1)/2);
  else 
    printf("%2d:   BLACK\n",1+(GameCnt+1)/2);
#endif
}

ShowMessage( char *s )
{
#ifdef NeXT_DEBUG
  printf( "ShowMessage:  %s\n", s );
#endif
  [NXApp setTitleMessage: s];
}

void
ExitChess()
{
  [NXApp terminate: NXApp];
  exit(0);
}

extern char playing;

void
Die()
{
  if( NXRunAlertPanel( 0, "Do you want to exit chess?", "Yes", "No", 0 ) ){
    signal(SIGINT,SIG_IGN);
    signal(SIGQUIT,SIG_IGN);
    ExitChess();
  }
}

void
TerminateSearch()
{
  signal(SIGINT,SIG_IGN);
  signal(SIGQUIT,SIG_IGN);
  timeout = true;
  if( bothsides )
    playing = 0;
  signal( SIGINT, Die ); 
  signal( SIGQUIT, Die );
}

SearchStartStuff( short side )
{
  signal(SIGINT,TerminateSearch);
  signal(SIGQUIT,TerminateSearch);
}

static char convert_buf[8];
char *
convert_rc( int row, int col, int row2, int col2, int type )
{
  
  if( type == king && col == 4){
    if( col2 == 6 ){
      strcpy( convert_buf, "o-o" );
      return( convert_buf );
    }
    else if( col2 == 2 ){
      strcpy( convert_buf, "o-o-o" );
      return( convert_buf );
    }
  }      

  convert_buf[0] = 'a' + col;
  convert_buf[1] = '0' + row+1;
  convert_buf[2] = 'a' + col2;
  convert_buf[3] = '0' + row2+1;
  convert_buf[4] = 0;

  return( convert_buf );
}

convert_algbr(char *algbr, int *r1, int *c1, int *r2, int *c2,int *castle_flag)
{
  int type;
  
  *castle_flag = 0;
  if( strcmp( algbr, "o-o" ) == 0 )
    *castle_flag = RIGHT_CASTLE;
  else if( strcmp( algbr, "o-o-o" ) == 0 )
    *castle_flag = LEFT_CASTLE;
  else{
    *c1 = algbr[0] - 'a';
    *r1 = algbr[1] - '0' - 1;
    *c2 = algbr[2] - 'a';
    *r2 = algbr[3] - '0' - 1;
  }
  type = [[NXApp gameBoard] typeAt: *r1 : *c1];
  if( type ){

#ifdef NeXT_DEBUG
    printf( "type: %d\n", type );
#endif
    if( type == king ){
      if( *c1 == 4 && *c2 == 6 )
	*castle_flag = RIGHT_CASTLE;
      else if( *c1 == 4 && *c2 == 2 )
	*castle_flag = LEFT_CASTLE;
    }
  }	
  return;
}

GetGame( const char *fname )
{
FILE *fd;
int r, c;
short sq;
unsigned short m;

  if ((fd = fopen(fname,"r")) != NULL){
    fscanf(fd,"%hd%hd%hd",&computer,&opponent,&Game50);
    fscanf(fd,"%hd%hd%hd%hd",
	   &castld[white],&castld[black],
	   &kingmoved[white],&kingmoved[black]);
    fscanf(fd,"%hd%hd",&TCflag,&OperatorTime);
    fscanf(fd,"%ld%ld%hd%hd",
	   &TimeControl.clock[white],&TimeControl.clock[black],
	   &TimeControl.moves[white],&TimeControl.moves[black]);
    for (sq = 0; sq < 64; sq++){
      fscanf(fd,"%hd",&m);
      board[sq] = (m >> 8);
      color[sq] = (m & 0xFF);
      if(color[sq] == 0) 
	color[sq] = neutral;
      else
	--color[sq];
    }
    [[NXApp gameBoard] layoutBoard: board color: color];
    [[NXApp gameBoard] display];
    GameCnt = -1; c = '?';
    while (c != EOF)
      {
	++GameCnt;
	c = fscanf(fd,"%hd%hd%hd%ld%hd%hd%hd",&GameList[GameCnt].gmove,
		   &GameList[GameCnt].score,&GameList[GameCnt].depth,
		   &GameList[GameCnt].nodes,&GameList[GameCnt].time,
		   &GameList[GameCnt].piece,&GameList[GameCnt].color);
	if (GameList[GameCnt].color == 0) GameList[GameCnt].color = neutral;
	else --GameList[GameCnt].color;
      }
    GameCnt--;
    if (TimeControl.clock[white] > 0) TCflag = true;
    computer--; opponent--;
  }
  fclose(fd);
  InitializeStats();
  UpdateDisplay(0,0,1,0);
  Sdepth = 0;
}


SaveGame( const char *fname )
{
  FILE *fd;
  short sq,i,c;

  fd = fopen(fname,"w");
  if( !fd )
    return( 0 );
    
  fprintf(fd,"%d %d %d\n",computer+1,opponent+1,Game50);
  fprintf(fd,"%d %d %d %d\n",
          castld[white],castld[black],kingmoved[white],kingmoved[black]);
  fprintf(fd,"%d %d\n",TCflag,OperatorTime);
  fprintf(fd,"%ld %ld %d %d\n",
          TimeControl.clock[white],TimeControl.clock[black],
          TimeControl.moves[white],TimeControl.moves[black]);
  for (sq = 0; sq < 64; sq++)
    {
      if (color[sq] == neutral) c = 0; else c = color[sq]+1;
      fprintf(fd,"%d\n",256*board[sq] + c);
    }
  for (i = 0; i <= GameCnt; i++)
    {
      if (GameList[i].color == neutral) c = 0;
      else c = GameList[i].color + 1;
      fprintf(fd,"%d %d %d %ld %d %d %d\n",
              GameList[i].gmove,GameList[i].score,GameList[i].depth,
              GameList[i].nodes,GameList[i].time,
              GameList[i].piece,c);
    }
  fclose(fd);
  return( 1 );
}

ListGame( const char *filename )
{
  FILE *fd;
  short i,f,t;
  
  fd = fopen(filename,"w");
  if( !fd )
    return( 0 );
    
  fprintf(fd,"\n");
  fprintf(fd,"       score  depth  nodes  time         ");
  fprintf(fd,"       score  depth  nodes  time\n");
  for (i = 0; i <= GameCnt; i++){
    f = GameList[i].gmove>>8; t = (GameList[i].gmove & 0xFF);
    algbr(f,t,false);
    if ((i % 2) == 0)
      fprintf(fd,"\n");
    else
      fprintf(fd,"         ");
    fprintf(fd,"%5s  %5d     %2d %6ld %5d",mvstr1,
	    GameList[i].score,GameList[i].depth,
	    GameList[i].nodes,GameList[i].time);
  }
  fprintf(fd,"\n\n");
  fclose(fd);
  return( 1 );
} 


Undo()
/*
   Undo the most recent half-move.
*/
{
short f,t;
  f = GameList[GameCnt].gmove>>8;
  t = GameList[GameCnt].gmove & 0xFF;
  if (board[t] == king && distance(t,f) > 1) {
    castle(GameList[GameCnt].color,f,t,2);
  } else {
      board[f] = board[t];
      color[f] = color[t];
      board[t] = GameList[GameCnt].piece;
      color[t] = GameList[GameCnt].color;
      if ( GameQueens[GameCnt] ) {
	  board[f] = GameQueens[GameCnt];
      }
      if (board[f] == king) --kingmoved[color[f]];
  }
  if (TCflag) ++TimeControl.moves[color[f]];
  GameCnt--; mate = false; Sdepth = 0;
  if ( [NXApp finished] ) {
      [NXApp setFinished: false];
  }
  UpdateDisplay(0,0,1,0);
  InitializeStats();
}

