/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * This file is part of SanKit -- A multiplatform set of C code
 * for reading and writing PGN formatted chess games. In addition
 * it can be used as an engine to determine in real time if proposed
 * moves are legal or not. It appears to generate near perfect PGN
 * from incorrectly formatted files.
 *
 * Copyright 1994 Steven J. Edwards All Rights Reserved.
 * Copyright 2009-2011 David Hoerl All Rights Reserved.
 *
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY David Hoerl ''AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David Hoerl OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

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

/* Revised: 1994.02.19 */

#ifdef CLI

#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 */

/* 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;
	gtimT gtim;
	gtcT new_gtc;

/* 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: ", (liT)(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) {
				printf("SANDecodeAux\n");
				mptr = SANDecodeAux(tkv[0]);
				if(mptr != NULL) {
					printf("DECODED!!!\n");
					done = 1;
					SANPlayMove(mptr);
					if((SANStatus() == gs_norm) && !optnv[optn_napr]) {
						SANSelect();
						SANPlayMove(&select_m);
					}
				} else {printf("Darn!\n"); }
			}
		}

		/* 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_encp, "encp", SANDcENCP, 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;
}

#endif

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