/*>>> sancmd.c: SAN project command processing */

/* Revised: 1994.02.19 */

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

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

#include "sancmd.h"
#include "sandci.h"
#include "sandsp.h"
#include "sangtc.h"
#include "sanmnd.h"
#include "sanmpu.h"
#include "sanpce.h"
#include "sanrst.h"
#include "sansel.h"
#include "sanutl.h"

/**** Locals */

#if (defined(THINK_C))
pascal void SystemTask (void) = 0xA9B4;
#endif

/* the command input file type */

typedef struct cifS
  {
    charptrT cif_name;          /* alllocated file name */
    FILEptrT cif_fptr;          /* file pointer */
    struct cifS *cif_next;      /* next file */
    struct cifS *cif_prev;      /* previous file */
  }
cifT, *cifptrT;

/* head and tail pointers of the command input file list */

static cifptrT head_cifptr, tail_cifptr;

/* the command text input buffer */

static char ctiv[tL];

/* clock bracketing for command timing */

static secT cmdtime0, cmdtime1;

/*--> SANCMDOpen: open a new command input file */
nonstatic
siT
SANCMDOpen (charptrT s)
{
  siT flag;
  cifptrT cifptr;
  FILEptrT fileptr;

/* set default return value */

  flag = 1;

/* open the file */

  if (strlen (s) == 0)
    fileptr = stdin;
  else
    {
      fileptr = SANTextFileOpen (s, "r");
      if (fileptr == NULL)
        flag = 0;
    };

/* allocate, set, and thread a list entry */

  if (flag)
    {
      cifptr = SANMemGrab (sizeof (cifT));
      cifptr->cif_name = SANStrGrab (s);
      cifptr->cif_fptr = fileptr;
      cifptr->cif_next = NULL;
      cifptr->cif_prev = tail_cifptr;
      if (tail_cifptr == NULL)
        head_cifptr = cifptr;
      else
        tail_cifptr->cif_next = cifptr;
      tail_cifptr = cifptr;
    };

  return (flag);
}

/*--> SANCMDClose: close the current command input file */
nonstatic
void
SANCMDClose (void)
{
  cifptrT cifptr;

/* close the file */

  cifptr = tail_cifptr;
  if (cifptr->cif_fptr != stdin)
    SANTextFileClose (cifptr->cif_fptr);
  SANMemFree (cifptr->cif_name);

/* dethread */

  tail_cifptr = cifptr->cif_prev;
  if (tail_cifptr == NULL)
    head_cifptr = NULL;
  else
    tail_cifptr->cif_next = NULL;
  SANMemFree (cifptr);

  return;
}

/*--> SANCMDLoad: load the command input buffer */
static
  siT
SANCMDLoad (void)
{
  siT flag, done, eofflag;

/* set up */

  flag = 1;
  done = 0;

  while (flag && !done)
    if (tail_cifptr == NULL)
      flag = 0;
    else
      {
        /* handle prompt */

        if (tail_cifptr->cif_fptr == stdin)
          (void) fprintf (stderr, ": ");

        /* get a line */

        eofflag = (fgets (ctiv, tL, tail_cifptr->cif_fptr) == NULL);
        if (eofflag)
          SANCMDClose ();
        else
          done = 1;
      };

  return (flag);
}

/*--> SANCommand: read and process a command */
nonstatic
void
SANCommand (void)
{
  siT flag, i, done;
  mptrT mptr;
  charptrT s;
  cmdT cmd={0};
  gtimT gtim;
  gtcT new_gtc;

#if (defined(THINK_C))
  SystemTask ();
#endif

/* load the command input buffer */

  flag = SANCMDLoad ();

/* set program finished if no data available */

  if (!flag)
    progdone = 1;
  else
    {
      /* tokenize */

      i = 0;
      while ((s = SANToken (ctiv, &i)) != NULL)
        {
          if (tkn < tkL)
            tkv[tkn++] = s;
          else
            SANMemFree (s);
        };

      /* handle empty entry */

      if (tkn == 0)
        tkv[tkn++] = SANStrGrab (civ[cmd_noop].ci_name);

      /* attempt a command match */

      if (!SANCheckCommand (tkv[0]))
        done = 0;
      else
        {
          done = 1;
          cmd = SANEvalCommand (tkv[0]);
        };

      /* dispatch if command found */

      if (done)
        {
          /* argument count tests */

          if (tkn < civ[cmd].ci_amin)
            SANDspError ("Not enough arguments");
          else if (tkn > civ[cmd].ci_amax)
            SANDspError ("Too many arguments");
          else
            {
              cmdtime0 = time (NULL);
              (civ[cmd].ci_func) ();
              cmdtime1 = time (NULL);
              if (optnv[optn_trpt])
                SANDspStrLINL ("PT: ", cmdtime1 - cmdtime0);
            };
        };

      /* test for game termination input marker */

      if (!done && (tkn == 1))
        {
          gtim = SANMatchGTIM (tkv[0]);
          if (gtim != gtim_nil)
            {
              done = 1;
              new_gtc = SANGTCDetermine (gtim);
              if (!SANGTCConsistent (new_gtc))
                SANDspError ("Inconsistent game termination marker");
              else
                SANGTCSet (new_gtc);
            };
        };

      /* if not a command or termination marker, try a move match */

      if (!done)
        if (tkn == 1)
          {
            mptr = SANDecodeAux (tkv[0]);
            if (mptr != NULL)
              {
                done = 1;
                SANPlayMove (mptr);
                if ((SANStatus () == gs_norm) && !optnv[optn_napr])
                  {
                    SANSelect ();
                    SANPlayMove (&select_m);
                  };
              };
          };

      /* if no move match, signal error */

      if (!done)
        SANDspError ("Unrecognized input; try \"help\" if needed");

      /* deallocate tokens */

      while (tkn > 0)
        SANMemFree (tkv[--tkn]);
    };

  return;
}

/*--> SANCMDInitCI1: initialize a command infomation record (part one) */
static
void
SANCMDInitCI1 (cmdT cmd, charptrT help)
{
  civ[cmd].ci_help = help;

  return;
}

/*--> SANCMDInitCI2: initialize a command infomation record (part two) */
static
void
SANCMDInitCI2 (cmdT cmd, charptrT name, frvptrT func, siT amin, siT amax)
{
  civ[cmd].ci_name = name;
  civ[cmd].ci_func = func;
  civ[cmd].ci_amin = amin;
  civ[cmd].ci_amax = amax;

  return;
}

/*--> SANCMDInitTable: command table initialization */
static
void
SANCMDInitTable (void)
{
/* initialize the commands (part one) */

  SANCMDInitCI1 (cmd_afdo, "append file: dribble output");
  SANCMDInitCI1 (cmd_auto, "perform automatic competetion");
  SANCMDInitCI1 (cmd_beep, "emit ASCII BEL");
  SANCMDInitCI1 (cmd_call, "call command subfile");
  SANCMDInitCI1 (cmd_cfdo, "close file: dribble output");
  SANCMDInitCI1 (cmd_cvca, "clear value: castling availability");
  SANCMDInitCI1 (cmd_cvcb, "clear value: chess board");
  SANCMDInitCI1 (cmd_cvep, "clear value: en passant target square");
  SANCMDInitCI1 (cmd_cvgt, "clear value: game termination");
  SANCMDInitCI1 (cmd_cvop, "clear value: option");
  SANCMDInitCI1 (cmd_cvsq, "clear value: square");
  SANCMDInitCI1 (cmd_cvtf, "clear value: tag field");
  SANCMDInitCI1 (cmd_dvas, "display value: hash address and signature");
  SANCMDInitCI1 (cmd_dvcb, "display value: chess board");
  SANCMDInitCI1 (cmd_dvcr, "display value: correspondence report");
  SANCMDInitCI1 (cmd_dver, "display value: event report");
  SANCMDInitCI1 (cmd_dvfe, "display value: Forsyth-Edwards notation");
  SANCMDInitCI1 (cmd_dvhg, "display value: hash generation tables");
  SANCMDInitCI1 (cmd_dvss, "display value: search strategies");
  SANCMDInitCI1 (cmd_dvte, "display value: tag environment");
  SANCMDInitCI1 (cmd_dvtf, "display value: tag field");
  SANCMDInitCI1 (cmd_dvts, "display value: timestamp");
  SANCMDInitCI1 (cmd_echo, "echo arguments");
  SANCMDInitCI1 (cmd_enum, "enumerate descendents");
  SANCMDInitCI1 (cmd_exit, "exit program");
  SANCMDInitCI1 (cmd_exsm, "execute supplied move");
  SANCMDInitCI1 (cmd_game, "autoplay to end of game");
  SANCMDInitCI1 (cmd_help, "display help");
  SANCMDInitCI1 (cmd_lfer, "load file: event report");
  SANCMDInitCI1 (cmd_newg, "new game");
  SANCMDInitCI1 (cmd_noop, "no operation");
  SANCMDInitCI1 (cmd_ofdo, "open file: dribble output");
  SANCMDInitCI1 (cmd_play, "select and play N moves");
  SANCMDInitCI1 (cmd_rtmv, "retract N played moves");
  SANCMDInitCI1 (cmd_rtrn, "return from command subfile");
  SANCMDInitCI1 (cmd_sfcr, "save file: correspondence report");
  SANCMDInitCI1 (cmd_sfer, "save file: event report");
  SANCMDInitCI1 (cmd_srch, "search for a target move");
  SANCMDInitCI1 (cmd_stat, "status report");
  SANCMDInitCI1 (cmd_svac, "set value: active color");
  SANCMDInitCI1 (cmd_svca, "set value: castling availability");
  SANCMDInitCI1 (cmd_svep, "set value: en passant target square");
  SANCMDInitCI1 (cmd_svfe, "set value: Forsyth-Edwards notation");
  SANCMDInitCI1 (cmd_svhc, "set value: halfmove clock");
  SANCMDInitCI1 (cmd_svfn, "set value: fullmove number");
  SANCMDInitCI1 (cmd_svop, "set value: option");
  SANCMDInitCI1 (cmd_svpl, "set value: playing level");
  SANCMDInitCI1 (cmd_svrt, "set value: average response time");
  SANCMDInitCI1 (cmd_svsq, "set value: square");
  SANCMDInitCI1 (cmd_svss, "set value: search strategies");
  SANCMDInitCI1 (cmd_svtf, "set value: tag field");
  SANCMDInitCI1 (cmd_test, "developer test");
  SANCMDInitCI1 (cmd_tfgc, "translate file: games to code (PGC)");
  SANCMDInitCI1 (cmd_tfge, "translate file: games to EPD");
  SANCMDInitCI1 (cmd_tfgg, "translate file: games to games (PGN)");
  SANCMDInitCI1 (cmd_tfgv, "translate file: games to variations");

/* initialize the commands (part two) */

  SANCMDInitCI2 (cmd_afdo, "afdo", SANDcAFDO, 2, 2);
  SANCMDInitCI2 (cmd_auto, "auto", SANDcAUTO, 2, 2);
  SANCMDInitCI2 (cmd_beep, "beep", SANDcBEEP, 1, 1);
  SANCMDInitCI2 (cmd_call, "call", SANDcCALL, 2, 2);
  SANCMDInitCI2 (cmd_cfdo, "cfdo", SANDcCFDO, 1, 1);
  SANCMDInitCI2 (cmd_cvca, "cvca", SANDcCVCA, 1, 5);
  SANCMDInitCI2 (cmd_cvcb, "cvcb", SANDcCVCB, 1, 1);
  SANCMDInitCI2 (cmd_cvep, "cvep", SANDcCVEP, 1, 1);
  SANCMDInitCI2 (cmd_cvgt, "cvgt", SANDcCVGT, 1, 1);
  SANCMDInitCI2 (cmd_cvop, "cvop", SANDcCVOP, 1, tkL);
  SANCMDInitCI2 (cmd_cvsq, "cvsq", SANDcCVSQ, 1, tkL);
  SANCMDInitCI2 (cmd_cvtf, "cvtf", SANDcCVTF, 1, tkL);
  SANCMDInitCI2 (cmd_dvas, "dvas", SANDcDVAS, 1, 1);
  SANCMDInitCI2 (cmd_dvcb, "dvcb", SANDcDVCB, 1, 1);
  SANCMDInitCI2 (cmd_dvcr, "dvcr", SANDcDVCR, 1, 1);
  SANCMDInitCI2 (cmd_dver, "dver", SANDcDVER, 1, 1);
  SANCMDInitCI2 (cmd_dvfe, "dvfe", SANDcDVFE, 1, 1);
  SANCMDInitCI2 (cmd_dvhg, "dvhg", SANDcDVHG, 1, 1);
  SANCMDInitCI2 (cmd_dvss, "dvss", SANDcDVSS, 1, 1);
  SANCMDInitCI2 (cmd_dvte, "dvte", SANDcDVTE, 1, 1);
  SANCMDInitCI2 (cmd_dvtf, "dvtf", SANDcDVTF, 1, tkL);
  SANCMDInitCI2 (cmd_dvts, "dvts", SANDcDVTS, 1, 1);
  SANCMDInitCI2 (cmd_echo, "echo", SANDcECHO, 1, tkL);
  SANCMDInitCI2 (cmd_enum, "enum", SANDcENUM, 2, 2);
  SANCMDInitCI2 (cmd_exit, "exit", SANDcEXIT, 1, 1);
  SANCMDInitCI2 (cmd_exsm, "exsm", SANDcEXSM, 2, 2);
  SANCMDInitCI2 (cmd_game, "game", SANDcGAME, 1, 1);
  SANCMDInitCI2 (cmd_help, "help", SANDcHELP, 1, 1);
  SANCMDInitCI2 (cmd_lfer, "lfer", SANDcLFER, 2, 2);
  SANCMDInitCI2 (cmd_newg, "newg", SANDcNEWG, 1, 1);
  SANCMDInitCI2 (cmd_noop, "noop", SANDcNOOP, 1, tkL);
  SANCMDInitCI2 (cmd_ofdo, "ofdo", SANDcOFDO, 2, 2);
  SANCMDInitCI2 (cmd_play, "play", SANDcPLAY, 1, 2);
  SANCMDInitCI2 (cmd_rtmv, "rtmv", SANDcRTMV, 1, 2);
  SANCMDInitCI2 (cmd_rtrn, "rtrn", SANDcRTRN, 1, 1);
  SANCMDInitCI2 (cmd_sfcr, "sfcr", SANDcSFCR, 2, 2);
  SANCMDInitCI2 (cmd_sfer, "sfer", SANDcSFER, 2, 2);
  SANCMDInitCI2 (cmd_srch, "srch", SANDcSRCH, 2, tkL);
  SANCMDInitCI2 (cmd_stat, "stat", SANDcSTAT, 1, 1);
  SANCMDInitCI2 (cmd_svac, "svac", SANDcSVAC, 2, 2);
  SANCMDInitCI2 (cmd_svca, "svca", SANDcSVCA, 1, 5);
  SANCMDInitCI2 (cmd_svep, "svep", SANDcSVEP, 2, 2);
  SANCMDInitCI2 (cmd_svfe, "svfe", SANDcSVFE, 7, 7);
  SANCMDInitCI2 (cmd_svfn, "svfn", SANDcSVFN, 2, 2);
  SANCMDInitCI2 (cmd_svhc, "svhc", SANDcSVHC, 2, 2);
  SANCMDInitCI2 (cmd_svop, "svop", SANDcSVOP, 1, tkL);
  SANCMDInitCI2 (cmd_svpl, "svpl", SANDcSVPL, 2, 2);
  SANCMDInitCI2 (cmd_svrt, "svrt", SANDcSVRT, 2, 2);
  SANCMDInitCI2 (cmd_svsq, "svsq", SANDcSVSQ, 1, tkL);
  SANCMDInitCI2 (cmd_svss, "svss", SANDcSVSS, 3, 3);
  SANCMDInitCI2 (cmd_svtf, "svtf", SANDcSVTF, 2, 3);
  SANCMDInitCI2 (cmd_test, "test", SANDcTEST, 1, tkL);
  SANCMDInitCI2 (cmd_tfgc, "tfgc", SANDcTFGC, 3, 3);
  SANCMDInitCI2 (cmd_tfge, "tfge", SANDcTFGE, 3, 3);
  SANCMDInitCI2 (cmd_tfgg, "tfgg", SANDcTFGG, 3, 3);
  SANCMDInitCI2 (cmd_tfgv, "tfgv", SANDcTFGV, 3, 3);

  return;
}

/*--> SANCMDInit: one time command initialization */
nonstatic
void
SANCMDInit (void)
{
/* initilaize the command table */

  SANCMDInitTable ();

/* clear the token count */

  tkn = 0;

/* initialize the command input file list */

  head_cifptr = tail_cifptr = NULL;
  (void) SANCMDOpen ("");

/* try the command initialization file */

  (void) SANCMDOpen (dfn_init);

  return;
}

/*--> SANCMDTerm: one time command termination */
nonstatic
void
SANCMDTerm (void)
{
/* release the command input file list */

  while (head_cifptr != NULL)
    SANCMDClose ();

  return;
}

/*<<< sancmd.c: EOF */
