/* GNU Chess 5.0 - epd.c - EPD position definition code
   Copyright (c) 1999-2002 Free Software Foundation, Inc.

   GNU Chess is based on the two research programs 
   Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.

   GNU Chess is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GNU Chess is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Chess; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Contact Info: 
     bug-gnu-chess@gnu.org
*/
/*
 *
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "util.h"
#include "eval.h"
#include "bitboard.h"
#include "material.h"
#include "piece.h"
#include "square.h"
#include "move.h"
#include "epd.h"
#include "log.h"
#include "board.h"
#include "atak.h"


#define EPDCLOSE 1

epd_iter_t EPDIter[1];

void EPDIterInit(epd_iter_t *epd_iter){
  epd_iter->fp=NULL;
}

int EPDIterStart(epd_iter_t *epd_iter, char *file){
  if(epd_iter->fp!=NULL){
    fclose(epd_iter->fp);
  }
  epd_iter->fp = fopen (file, "r");
  if (epd_iter->fp == NULL) {
      return false;
  }
  return true;
}

void EPDIterClose(epd_iter_t *epd_iter){
  if(!epd_iter->fp){
    return;
  }
  fclose (epd_iter->fp);
  epd_iter->fp=NULL;
}

int EPDIterNext(epd_iter_t *epd_iter, board_t *board, char *id, char *bm){
    char *dummy;
    char line[MAXSTR];
    int ret;
    if(epd_iter->fp==NULL){
	return false;
    }
 next_line:
    dummy=fgets (line, MAXSTR-1, epd_iter->fp);
    if (!feof(epd_iter->fp)) {
	ret = ParseEPD (board, line, bm, id);
	if (ret != EPD_SUCCESS) goto next_line;
	return (true);
    }else{ 
	fclose (epd_iter->fp);
	epd_iter->fp = NULL;
	return (false);
    }
}



/*
 * Returns EPD_SUCCESS on success, EPD_ERROR on error. We try to be
 * quite tough on the format. However, as of yet no legality checking
 * is done and the board is not reset on error, this should be done by
 * the caller.
 */

int ParseEPD (board_t *board, const char *p, char *bm, char *id)
/**************************************************************************
 *   
 *  Parses an EPD input line.  A few global variables are updated e.g.
 *  current board, side to move, en passant, castling status, etc.
 *
 **************************************************************************/
{
   int r, c, sq;
   BitBoard kw,rw,kb,rb;
   char *str_p;
   int hmclock,movenb;
   int i;

   r = 56;
   c = 0;
   memset (board, 0, sizeof (board_t));

   while (p && *p != ' ')
   {
     sq = r + c;
     switch (*p)
     {
        case 'P' :  SETBIT (board->b[white][pawn], sq);
		    board->material[white] += ValueP;
		    break;	
        case 'N' :  SETBIT (board->b[white][knight], sq);
		    board->material[white] += ValueN;
		    break;	
        case 'B' :  SETBIT (board->b[white][bishop], sq);
		    board->material[white] += ValueB;
		    break;	
        case 'R' :  SETBIT (board->b[white][rook], sq);
		    board->material[white] += ValueR;
		    break;	
        case 'Q' :  SETBIT (board->b[white][queen], sq);
		    board->material[white] += ValueQ;
		    break;	
        case 'K' :  SETBIT (board->b[white][king], sq);
		    break;	
        case 'p' :  SETBIT (board->b[black][pawn], sq);
		    board->material[black] += ValueP;
		    break;	
        case 'n' :  SETBIT (board->b[black][knight], sq);
		    board->material[black] += ValueN;
		    break;	
        case 'b' :  SETBIT (board->b[black][bishop], sq);
		    board->material[black] += ValueB;
		    break;	
        case 'r' :  SETBIT (board->b[black][rook], sq);
		    board->material[black] += ValueR;
		    break;	
        case 'q' :  SETBIT (board->b[black][queen], sq);
                    board->material[black] += ValueQ;
		    break;	
        case 'k' :  SETBIT (board->b[black][king], sq);
		    break;	
        case '/' :  r -= 8;
	 	    c = -1;
		    break;
        default  :  break;
     }
     if (isdigit (*p))
        c += (*p - '0');
     else
        c++;

     /* 
      * Special case, a trailing "/" is accepted on the
      * end of the board settings.
      */

     if (r == -8 && p[1] == ' ')
	     r = 0;

     if (r < 0 || c > 8){
       Log("ParseEPD(): Error 1\n");
       return EPD_ERROR;
     }
     if (c == 8 && p[1] != '/' && p[1] != ' '){
       Log("ParseEPD(): Error 2\n");
       return EPD_ERROR;
     }
     p++;
   }

   board->pmaterial[white] = board->material[white] - 
				nbits(board->b[white][pawn]) * ValueP;
   board->pmaterial[black] = board->material[black] - 
				nbits(board->b[black][pawn]) * ValueP;
   board->king[white] = leadz (board->b[white][king]);
   board->king[black] = leadz (board->b[black][king]);
   BoardUpdateFriends (board); 
   BoardUpdateCBoard (board);
   BoardUpdateMvboard (board);
   BoardUpdateMaterialIndex(board);

   /*  Get side to move  */
   if (!++p) return EPD_ERROR;
   if      (*p == 'w') board->side = white; 
   else if (*p == 'b') board->side = black;
   else {
     Log("ParseEPD(): Error 3\n");
     return EPD_ERROR;
   }

   /* Isn't this one cute? */
   if (!++p || *p != ' ' || !++p){
     Log("ParseEPD(): Error 4\n");
     return EPD_ERROR;
   }
  
   /*  Castling status  */

   rw = board->b[white][rook];
   kw = board->b[white][king];
   rb = board->b[black][rook];
   kb = board->b[black][king];

   while (p && *p != ' ') {
     if (false); 
     else if (*p == 'K'){
       if((rw & bitboards.BitPosArray[H1]) && 
	  (kw & bitboards.BitPosArray[E1])
	  ) {
	 board->flag |= WKINGCASTLE;
       }else{ 
	 OutputConsole("Correcting invalid castling rights '%c'!\n",*p);
       }
       
     }
     else if (*p == 'Q'){
       if((rw & bitboards.BitPosArray[A1]) && 
	  (kw & bitboards.BitPosArray[E1])
	  ){
	 board->flag |= WQUEENCASTLE;
       }else{
	 OutputConsole("Correcting invalid castling rights '%c'!\n",*p);
       }
     }
     else if (*p == 'k'){
       if((rb & bitboards.BitPosArray[H8]) && 
	      (kb & bitboards.BitPosArray[E8])
	  ){
	 board->flag |= BKINGCASTLE;
       }else{
	 OutputConsole("Correcting invalid castling rights '%c'!\n",*p);
       }
     }
     else if (*p == 'q'){
       if((rb & bitboards.BitPosArray[A8]) && 
	  (kb & bitboards.BitPosArray[E8])
	  ){
	 board->flag |= BQUEENCASTLE;
       }else{
	 OutputConsole("Correcting invalid castling rights '%c'!\n",*p);
       }
     }
     else if (*p == '-') { p++; break; }
     else {
       Log("ParseEPD(): Error 5\n");
       return EPD_ERROR;
     }
     p++;
   }


   if (!p || *p != ' ' || !++p){
     Log("ParseEPD(): Error 6\n");
     return EPD_ERROR;
   }

   /*
    * En passant square, can only be '-' or [a-h][36]
    * In fact, one could add more sanity checks here.
    */
   if (*p != '-') {
      if (!p[1] || *p < 'a' || *p > 'h' ||
	  !(p[1] == '3' || p[1] == '6')){
	Log("ParseEPD(): Error 7\n");
	return EPD_ERROR;
      }
      board->ep = (*p - 'a') + (p[1] - '1')*8;
      p++;
   } else {
      board->ep = -1;
   }

   /* 
    *  Set ep passant square if necessary. 
    *  This deviates from the fen standard but
    *  many programs sin in this way.
    */

   if(board->ep!=-1 && !(board->b[board->side][pawn] & bitboards.MoveArray[ptype[1^board->side]][board->ep])){
       board->ep=-1;
   }

   board->in_check=SqAtakd (board,board->king[board->side], 1^board->side);
   board->xin_check=SqAtakd (board,board->king[1^board->side], board->side);



   p++;
   if(!(*p)|| (*p!=' ')){
     movenb=1;
     hmclock=0;
     goto epd_style;
     
   }
   p++;

   hmclock=0;
   movenb=0;

   // Read half move clock 
   if('0'<=*p && *p<='9'){   
     while(*p && *p!=' '){
       if('0'<=*p && *p<='9'){
	 hmclock*=10;
	 hmclock+=*p-'0';
	 p++;
       }else{
	 Log("ParseEPD(): Error 9\n");
	 return EPD_ERROR;
       }
     }

     // Read move number

     if(!(*p) || (*p!=' ') ){
       Log("ParseEPD(): Error 10\n");
       return EPD_ERROR;
     }
     p++;

     while(*p && !isspace(*p)){
       if('0'<=*p && *p<='9'){
	 movenb*=10;
	 movenb+=*p-'0';
	 p++;
       }else{
	 Log("ParseEPD(): Error 11\n");
	 return EPD_ERROR;
       }
     }   
   }

 epd_style:

   // Sometimes FEN's have move number zero which is strictly speaking illegal


   if(movenb==0) movenb=1;

   if(board->side==white){
     board->GameCnt=-2+2*movenb;
   }else{
     board->GameCnt=-1+2*movenb;
   }
   board->Game50=board->GameCnt-hmclock; 
   
   for(i=board->Game50;i<board->GameCnt;i++){
     board->stack[i-board->Game50]=0;
   }

   BoardHashKeyUpdate (board);
   board->phase = Material[board->material_index].phase;

   ASSERT(BoardValidate(board)==0);


   if(bm==NULL || id==NULL){
     return EPD_SUCCESS;
   }
   
   

   bm[0] = '\0';
   id[0] = '\0';



   /* The opcodes are optional, so we should not generate errors here */

   /*  Read in best move; "bm" operator */
   str_p = strstr(p, "bm");
   if (str_p) sscanf (str_p, "bm %63[^;];", bm); 

   /*  Read in the description; "id" operator */
   str_p = strstr(p, "id");
   if (str_p) sscanf (p, "id %31[^;];", id);

   return EPD_SUCCESS;
}



void SaveEPD (board_t *board, char *p)
/**************************************************************************
 *   
 *  This routine appends the current position in EPD format into a file.
 *
 **************************************************************************/
{
   char file[MAXSTR];
   char fen[MAXSTR];
   FILE *fp;
   int r, c, sq, k;
   char c1;

   sscanf (p, "%s ", file);
   fp = fopen (file, "a");
   BoardToFen(board, fen);
   fprintf(fp,"%s",fen);
   fprintf (fp, " bm 1; id 1;\n");
   fclose(fp);
   return;

   for (r = A8; r >= A1; r -= 8)
   {
      k = 0;
      for (c = 0; c < 8; c++)
      {
         sq = r + c;
	 if (board->cboard[sq] == empty)
	    k++;
	 else
	 {
	    if (k) 
	       fprintf (fp, "%1d", k);
	    k = 0;
	    c1 = notation[board->cboard[sq]];
	    if (bitboards.BitPosArray[sq] & board->friends[black])
	       c1 = tolower (c1);
	    fprintf (fp, "%c", c1);
	 }
      }
      if (k)
	 fprintf (fp, "%1d", k);
      if (r > A1)
         fprintf (fp, "/");
   }

   /* Print other stuff */
   fprintf (fp, (board->side == white ? " w " : " b "));

   if (board->flag & WKINGCASTLE)
      fprintf (fp, "K");
   if (board->flag & WQUEENCASTLE)
      fprintf (fp, "Q");
   if (board->flag & BKINGCASTLE)
      fprintf (fp, "k");
   if (board->flag & BQUEENCASTLE)
      fprintf (fp, "q");
   if (!(board->flag & (WCASTLE | BCASTLE)))
      fprintf (fp, "-");

   fprintf (fp, " %s", (board->ep > -1 ? algbr[board->ep] : "-"));
   fprintf (fp, " bm 1; id 1;");
   fprintf (fp, "\n");
   fclose (fp);
}
