/*>>> sanfen.c: SAN project Forsyth-Edwards position notation */

/* Revised: 1994.01.24 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

#include "sandef.h"
#include "sanvar.h"

#include "sanepd.h"
#include "sanfen.h"
#include "sanutl.h"
#include "sanval.h"

/*--> SANFENGen: generate Forsyth-Edwards notation for current position */
nonstatic
charptrT
SANFENGen (void)
{
  charptrT ptr;
  charptrT ebptr;
  siT bi;
  char bv[tL+512]={0}, nv[tL];

/* generate EPD basic */

  ebptr = SANEPDGenBasicCurrent ();
  strcpy (bv, ebptr);
  SANMemFree (ebptr);
  bi = strlen (bv);
  bv[bi++] = ' ';

/* output halfmove clock */

  (void) sprintf (nv, "%hd", curr_e.e_hmvc);
  (void) sprintf (&bv[bi], "%s", nv);
  bi += strlen (nv);
  bv[bi++] = ' ';

/* output fullmove number (last field) */

  (void) sprintf (nv, "%hd", curr_e.e_fmvn);
  (void) sprintf (&bv[bi], "%s", nv);
  bi += strlen (nv);

/* NUL termination */

  bv[bi++] = '\0';

  ptr = SANStrGrab (bv);

  return (ptr);
}

/*--> SANFENParseBD: FEN parse of board description */
static
  siT
SANFENParseBD (charptrT tptr, bptrT bptr)
{
  siT flag;
  cT c;
  pT p, piece;
  rankT rank = {0};
  fileT file;
  siT i, j, d;
  char ch;

  flag = 1;
  i = 0;
  rank = rank_8;
  file = file_a;

  while (flag && ((ch = *(tptr + i++)) != '\0'))
    switch (ch)
      {
      case '/':
        if ((file != fileL) || (rank == rank_1))
          flag = 0;
        else
          {
            rank--;
            file = file_a;
          };
        break;

      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
        d = ch - '0';
        if ((file + d) > fileL)
          flag = 0;
        else
          for (j = 0; j < d; j++)
            bptr->bm[rank][file++] = cp_v0;
        break;

      default:
        if (isupper (ch))
          c = c_w;
        else if (islower (ch))
          c = c_b;
        else
          {
            c = c_v;
            flag = 0;
          };

        if (flag)
          {
            p = p_nil;
            piece = p_p;
            while ((p == p_nil) && (piece <= p_k))
              if (map_upper (ch) == acpv[piece])
                p = piece;
              else
                piece++;
            if (p == p_nil)
              flag = 0;
            else
              bptr->bm[rank][file++] = cv_cp_cpv[c][p];
          };

        break;
      };

  if (flag)
    if ((file != fileL) || (rank != rank_1))
      flag = 0;

  return (flag);
}

/*--> SANFENParseAC: FEN parse of active color */
static
  siT
SANFENParseAC (charptrT tptr, cptrT cptr)
{
  siT flag;
  char ch;

  if (strlen (tptr) != 1)
    flag = 0;
  else
    {
      ch = *tptr;
      if (SANCIEqChar (ch, accv[c_w]))
        {
          flag = 1;
          *cptr = c_w;
        }
      else if (SANCIEqChar (ch, accv[c_b]))
        {
          flag = 1;
          *cptr = c_b;
        }
      else
        flag = 0;
    };

  return (flag);
}

/*--> SANFENParseCA: FEN parse of castling availability */
static
  siT
SANFENParseCA (charptrT tptr, castptrT castptr)
{
  siT flag;
  castT cast;
  char ch;
  siT i;

/* set default return value */

  flag = 1;

  if (strcmp (tptr, "-") == 0)
    *castptr = 0;
  else if (strlen (tptr) > 4)
    flag = 0;
  else
    {
      flag = 1;
      cast = 0;
      i = 0;
      while (flag && ((ch = *(tptr + i++)) != '\0'))
        if ((ch == map_upper (acpv[p_k])) && !(cast & cast_wk))
          cast |= cast_wk;
        else if ((ch == map_upper (acpv[p_q])) && !(cast & cast_wq))
          cast |= cast_wq;
        else if ((ch == map_lower (acpv[p_k])) && !(cast & cast_bk))
          cast |= cast_bk;
        else if ((ch == map_lower (acpv[p_q])) &&
                 !(cast & cast_bq))
          cast |= cast_bq;
        else
          flag = 0;

      if (flag)
        *castptr = cast;
    };

  return (flag);
}

/*--> SANFENParseEP: FEN parse of en passant target square */
static
  siT
SANFENParseEP (charptrT tptr, sqptrT sqptr)
{
  siT flag;
  rankT r, rank={0};
  fileT f, file;
  char ch;

/* set default return value */

  flag = 1;

  if (strcmp (tptr, "-") == 0)
    *sqptr = sq_nil;
  else if (strlen (tptr) != 2)
    flag = 0;
  else
    {
      file = file_nil;
      f = file_a;
      ch = *(tptr + 0);
      while ((file == file_nil) && (f <= file_h))
        if (SANCIEqChar (ch, acfv[f]))
          file = f;
        else
          f++;
      if (file == file_nil)
        flag = 0;

      if (flag)
        {
          rank = rank_nil;
          r = rank_1;
          ch = *(tptr + 1);
          while ((rank == rank_nil) && (r <= rank_8))
            if (ch == acrv[r])
              rank = r;
            else
              r++;
          if (rank == rank_nil)
            flag = 0;
        };

      if (flag)
        *sqptr = map_sq_rank_file (rank, file);
    };

  return (flag);
}

/*--> SANFENParseHC: FEN parse of halfmove clock */
static
  siT
SANFENParseHC (charptrT tptr, siptrT siptr)
{
  siT flag;
  siT hmvc;
  siT i;
  char ch;

  flag = 1;
  hmvc = 0;
  i = 0;
  while (flag && ((ch = *(tptr + i++)) != '\0'))
    if (isdigit (ch))
      {
        hmvc *= 10;
        hmvc += ch - '0';
      }
    else
      flag = 0;

  if (flag)
    *siptr = hmvc;

  return (flag);
}

/*--> SANFENParseFN: FEN parse of fullmove number */
static
  siT
SANFENParseFN (charptrT tptr, siptrT siptr)
{
  siT flag;
  siT fmvn;
  siT i;
  char ch;

  flag = 1;
  fmvn = 0;
  i = 0;
  while (flag && ((ch = *(tptr + i++)) != '\0'))
    if (isdigit (ch))
      {
        fmvn *= 10;
        fmvn += ch - '0';
      }
    else
      flag = 0;

  if (flag)
    if (fmvn < 1)
      flag = 0;

  if (flag)
    *siptr = fmvn;

  return (flag);
}

/*--> SANFENSet: set the current position from Forsyth-Edwards notation */
nonstatic
siT
SANFENSet (charptrT s)
{
  siT flag;
  siT si;
  charptrT tptr;
  bT new_b;
  cT new_actc;
  castT new_cast;
  sqT new_epsq;
  siT new_hmvc, new_fmvn;
  sqT sq;

  flag = 1;
  si = 0;

/* parse the board */

  if (flag)
    if ((tptr = SANToken (s, &si)) == NULL)
      flag = 0;
    else
      {
        flag = SANFENParseBD (tptr, &new_b);
        SANMemFree (tptr);
      };

/* parse the active color */

  if (flag)
    if ((tptr = SANToken (s, &si)) == NULL)
      flag = 0;
    else
      {
        flag = SANFENParseAC (tptr, &new_actc);
        SANMemFree (tptr);
      };

/* parse the castling availability */

  if (flag)
    if ((tptr = SANToken (s, &si)) == NULL)
      flag = 0;
    else
      {
        flag = SANFENParseCA (tptr, &new_cast);
        SANMemFree (tptr);
      };

/* parse the en passant target square */

  if (flag)
    if ((tptr = SANToken (s, &si)) == NULL)
      flag = 0;
    else
      {
        flag = SANFENParseEP (tptr, &new_epsq);
        SANMemFree (tptr);
      };

/* parse the halfmove clock */

  if (flag)
    if ((tptr = SANToken (s, &si)) == NULL)
      flag = 0;
    else
      {
        flag = SANFENParseHC (tptr, &new_hmvc);
        SANMemFree (tptr);
      };

/* parse the fullmove number */

  if (flag)
    if ((tptr = SANToken (s, &si)) == NULL)
      flag = 0;
    else
      {
        flag = SANFENParseFN (tptr, &new_fmvn);
        SANMemFree (tptr);
      };

/* copy the new values */

  if (flag)
    {
      for (sq = sq_a1; sq <= sq_h8; sq++)
        gb.bv[sq] = new_b.bv[sq];
      curr_e.e_actc = new_actc;
      curr_e.e_pasc = inv_cv[new_actc];
      curr_e.e_cast = new_cast;
      curr_e.e_epsq = new_epsq;
      curr_e.e_hmvc = new_hmvc;
      curr_e.e_fmvn = new_fmvn;
      SANZapUp ();
    };

  return (flag);
}

/*<<< sanfen.c: EOF */
