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

/*>>> 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], 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;
	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;
	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 */
