#include "debug.h"

void            display (void);
void            printmoves (void);
void            flip_board (void);
void            setup (char *fen);
void            printmoves (void);
void            sortmvs (sMOVES * fm);
void            sorthmvs (sMOVES * fm);
int             hget (const uMOVE * m);
int				checkboard();

void 
show_pst (void)
{
	int             i, j;
	for (j = WP; j < WQ + 1; j += 2) {
		for (i = A1; i < H8 + 1; ++i) {
			if (i & 8) {
				i += 7;
				printf ("\n");
			} else
				printf ("%5d", pst[j][i]);
		}
		printf ("\n\n");
	}
}

void 
test (void)
{
	int             n1, n2, p = 0;
	char            fen[128];
	FILE           *f;
	if ((f = fopen ("./tests/flip.epd", "r")) == NULL) {
		printf ("Read error occurred: could not open ./tests/flip.epd\n");
		return;
	}
	while (fgets (fen, 128, f) != NULL) {
		if (fen[0] == '#' || fen[0] == '\0' || fen[0] == ' ');
		else {
			++p;
			setup (fen);
			n1 = evaluate (-MATE, MATE);
			flip_board ();
			n2 = evaluate (-MATE, MATE);
			if (n1 != n2) {
				printf ("\a %d: %d != %d\n", p, n1, n2);
			}
		}
	}
	fclose (f);
}

UI64 
perft (int d)
{
	UI64            c;
	sMOVES         *m;
	sMOVES         *e;
	uMOVE           t;
	nodes++;
	if (d < 1)
		return 1;
	mhs->threat = (mhs->threat & 14) | inCheck (stm);
	gen ();
	c = 0;
	m = last_move[ply - 1];
	e = last_move[ply];
	while (m < e) {
		t = m->move;
		domove (&t);
		
		if (!attacked (stm ^ 1, kingsq[stm ^ 1])) {
			c += perft (d - 1);
		}
		unmove ();
		m++;
	}
	return c;
}

void 
display (void)
{
	char            p[] = ".?#onNkK#?bBrRqQ";
	int             i;
	char            e[128];
	sHASHENTRY * 	h;
	uMOVE		hm;

	printf ("                           \n8  ");
	for (i = A1; i < nbSquares; ++i) {
		if (i & 8) {
			i += 8;
			printf (" \n%c  ", '8' - (i - A1) / 16);
		}
		printf (" %c ", p[board[i]]);
	}
	printf (" \n                           ");
	printf ("\n    a  b  c  d  e  f  g  h\n");
	buildepd (e);
	printf ("%s ", e);
	printmvw (&(mhs - 1)->move);

	h = probe();
	if(h) {	
		printf(" ");
		hm.asint = h->best_move;
		printmvw (&hm);
	}
	printf ("\n");
}

void 
setup (char *fen)
{
	char            p[] = ".?pPnNkK#?bBrRqQ";
	int             i, f = 1;
	int             sq = A1;
	int             count[] = {1, 1};
	for (i = A1; i < nbSquares; ++i, i += (i & 8)) {
		board[i] = MT;
		indx[i] = 32;
	}
	for (i = 0; i < 16; ++i) {
		pieces[0][i] = 0;
		pieces[1][i] = 0;
	}
	force[0] = 0;
	force[1] = 0;
	for (i = 0; i < nbPieces; ++i)
		types[i] = 0;
	castle = 0;
	key = 0;
	fifty = 0;
	
	/* HACK - should use hclear() */
	/* memset (history, 0, sizeof (history)); */
	hclear();
	 
	memset (pv, 0, sizeof (pv));
	memset (pvl, 0, sizeof (pvl));
	memset (killer0, 0, sizeof (killer0));
	memset (killer1, 0, sizeof (killer1));
	memset (killer2, 0, sizeof (killer2));
	
	clearhash ();
	eclear ();
	
	while (*fen) {
		if (*fen == '\n')
			break;
		if (*fen == ' ') {
			f++;
			fen++;
			continue;
		}
		if (*fen == ';') {
			fen++;
			continue;
		}
		switch (f) {
		case 1:
			if (*fen == '/') {
				sq = (sq & 240) + 16;
				break;
			}
			if (*fen > '0' && *fen < '9') {
				sq += (*fen - '0');
				break;
			}
			for (i = 2; i < nbPieces; i++)
				if (*fen == p[i])
					break;
			board[sq] = i;
			force[i & 1] += fval[i];
			++types[i];
			key ^= zobrist[i][sq];
			if (i == WK) {
				kingsq[1] = sq;	/* remove these */
				pieces[1][0] = sq;
				indx[sq] = 0;
			} else if (i == BK) {
				kingsq[0] = sq;
				pieces[0][0] = sq;
				indx[sq] = 0;
			} else {
				pieces[i & 1][count[i & 1]] = sq;
				indx[sq] = count[i & 1];
				++count[i & 1];
			}
			sq++;
			break;
		case 2:	/* set stm */
			if (*fen == 'w')
				stm = 1;
			else if (*fen == 'b')
				stm = 0;
			else {
				printf ("error in fen: no stm");
				return;
			}
			break;
		case 3:	/* castle rights */
			if (*fen == 'K')
				castle |= 4;
			else if (*fen == 'Q')
				castle |= 8;
			else if (*fen == 'k')
				castle |= 1;
			else if (*fen == 'q')
				castle |= 2;
			else if (*fen == '-')
				castle = 0;
			else {
				printf ("error in fen: castle rights");
				return;
			}
			break;
		case 4:	/* epsquare */
			if (*fen >= 'a' && *fen <= 'h')
				epsquare = A8 + *fen - 'a';
			else if (*fen > '0' && *fen < '9')
				epsquare -= 16 * (*fen - '1');
			else if (*fen == '-')
				epsquare = 0;
			else {
				printf ("error in fen: ep sq");
				return;
			}
			break;
			/*
	                case 5: 
	                if(*fen >= '0' && *fen <= '9'){
	                    fifty *= 10;
	                    fifty += (*fen - '0');
	                }
	                else if(*fen == '-')
	                    fifty = 0;
	                else
	                {
	                    printf("error in fen: fifty");
	                    return;
	                }
	                break;
	                */
		default:
			break;
		}
		fen++;
	}
	key ^= zobrist[8][epsquare];
	key ^= zobrist[9][castle | A1];
	if (stm == BLACK)
		key = ~key;
	assert (!checkboard ());
	mhs = move_history;
	
	ply = 1;
	memset(scores, evaluate(), sizeof(scores));
	mhs->threat = (mhs->threat & (ALL_THREATS - IN_CHECK)) | inCheck (stm);
	gen ();
	/*
        for(i=0; i<16; ++i)
        	if(pieces[0][i] && indx[pieces[0][i]] != i) printf("%d %d %d\n", i, pieces[0][i], indx[pieces[0][i]]);
        for(i=0; i<16; ++i)
        	if(pieces[1][i] && indx[pieces[1][i]] != i) printf("%d %d %d\n", i, pieces[1][i], indx[pieces[1][i]]);
        for(i=A1; i<=H8; ++i, i+=(i&8)) {
        	f = board[i];
        	if(f && pieces[f&1][indx[i]] != i) printf("X");
        	else printf("_");
        }
        */
}

void 
buildepd (char *p)
{
	char            t[] = ".?pPnNkK#?bBrRqQ";
	int             i = A1, spcs = 0;
	do {
		if (i & 8) {
			if (spcs) {
				*p = '0' + spcs;
				spcs = 0;
				p++;
			}
			*p = '/';
			p++;
			i += 8;
		}
		if (board[i] == MT)
			spcs++;
		else {
			if (spcs) {
				*p = '0' + spcs;
				spcs = 0;
				p++;
			}
			*p = t[board[i]];
			p++;
		}
		if (i == H8) {
			if (spcs)
				*p++ = '0' + spcs;
			break;
		}
		i++;
	}
	while (1);
	*p++ = ' ';
	if (stm == WHITE)
		*p++ = 'w';
	else
		*p++ = 'b';
	*p++ = ' ';
	if (castle & 15) {
		if (castle & 4)
			*p++ = 'K';
		if (castle & 8)
			*p++ = 'Q';
		if (castle & 1)
			*p++ = 'k';
		if (castle & 2)
			*p++ = 'q';
	} else
		*p++ = '-';
	*p++ = ' ';
	if (epsquare) {
		*p++ = 'a' + (epsquare & 7);
		*p++ = '0' + (8 - ((epsquare >> 4) - 4));
	} else
		*p++ = '-';
	*p++ = ' ';
	*p++ = '0' + fifty / 10;
	*p++ = '0' + fifty % 10;
	//*p++ = ';';
	//*p++ = '"';
	//sprintf(p, "W:%d(%d) B:%d(%d)", force[WHITE], types[WP], force[BLACK], types[BP] );
	//*p++ = '"';
	*p = 0;
}

void 
printmv (const uMOVE * m)
{
	char            s[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	char            p[] = "  PPNNKK  BBRRQQ";
	int             i = -1;
	if (m->asint) {
		if (board[m->data.from] > WP)
			s[++i] = p[board[m->data.from]];
		if (board[m->data.dest] != MT || m->data.type == ENP) {
			if (board[m->data.from] <= WP)
				s[++i] = 'a' + (m->data.from & 7);
			s[++i] = 'x';
		}
		s[++i] = 'a' + (m->data.dest & 7);
		s[++i] = '8' - ((m->data.dest - 64) >> 4);
		if (m->data.type == PRO) {
			s[++i] = '=';
			s[++i] = p[m->data.piece];
		} else if (m->data.type == ENP) {
			s[++i] = '/';
			s[++i] = 'e';
			s[++i] = 'p';
		} else if (m->data.type == CAK) {
			s[0] = 'O';
			s[1] = 'O';
			s[2] = 0;
		} else if (m->data.type == CAQ) {
			s[0] = 'O';
			s[1] = 'O';
			s[2] = 'O';
			s[3] = 0;
		}
		s[++i] = 0;
		printf ("%s", s);
	} else
		printf ("NULL");
}

/* output move in Prfrf format for winboard */
void 
printmvw (const uMOVE * m)
{
	char            s[8] =
	{
		0, 0, 0, 0, 0, 0, 0, 0
	};
	char            p[] = "    nn    bbrrqq";
	if (m->asint) {
		s[0] = 'a' + (m->data.from & 7);
		s[1] = '8' - ((m->data.from - 64) >> 4);
		s[2] = 'a' + (m->data.dest & 7);
		s[3] = '8' - ((m->data.dest - 64) >> 4);
		if (m->data.type == PRO)
			s[4] = p[m->data.piece];
		printf ("%s", s);
	} else
		printf ("NULL");
}

void 
move2str (uMOVE * m, char *s)
{
	char            p[] = "  PPNNKK  BBRRQQ";
	int             i = -1;
	if (m->asint) {
		if (board[m->data.from] > WP)
			s[++i] = p[board[m->data.from]];
		if (board[m->data.dest] != MT || m->data.type == ENP) {
			if (board[m->data.from] <= WP)
				s[++i] = 'a' + (m->data.from & 7);
			s[++i] = 'x';
		}
		s[++i] = 'a' + (m->data.dest & 7);
		s[++i] = '8' - ((m->data.dest - 64) >> 4);
		if (m->data.type == PRO) {
			s[++i] = '=';
			s[++i] = p[m->data.piece];
		} else if (m->data.type == ENP) {
			s[++i] = '/';
			s[++i] = 'e';
			s[++i] = 'p';
		} else if (m->data.type == CAK) {
			s[0] = 'O';
			s[1] = 'O';
			s[2] = 0;
		} else if (m->data.type == CAQ) {
			s[0] = 'O';
			s[1] = 'O';
			s[2] = 'O';
			s[3] = 0;
		}
		s[++i] = 0;
	} else {
		s[0] = 'N';
		s[0] = 'U';
		s[0] = 'L';
		s[0] = 'L';
		s[0] = 0;
	}
}

/*
void move2str(uMOVE * m, char * s){
    char p[16] = "    nn    bbrrqq";
    if(m->asint){
        s[0] = 'a' + (m->data.from & 7);
        s[1] = '8' - ((m->data.from - 64) >> 4);
        s[2] = 'a' + (m->data.dest & 7);
        s[3] = '8' - ((m->data.dest - 64) >> 4);
        s[4] = 0;
        s[5] = 0;
        if(m->data.type == PRO)
            s[4] = p[m->data.piece];
    }
    else{
        s[0] = 'N';
        s[0] = 'U';
        s[0] = 'L';
        s[0] = 'L';
        s[0] = 0;
    }
}
*/

void 
printmoves ()
{
	sMOVES         *m = last_move[ply - 1];
	while (m < last_move[ply]) {
		printf ("%d.", (int)(1 + m - last_move[ply - 1]));
		//sorthmvs (m);
		printmv (&m->move);
		printf ("=%d\t%d\t", m->score, hget (&m->move));
		m++;
	}
	printf ("\n");
}

int 
checkboard ()
{
	int             sq, pc;
	UI64            hash = 0;
	int             k[2] =
	{
		0, 0
	};
	int             m[2] =
	{
		0, 0
	};
	for (sq = A1; sq < nbSquares; sq++, sq += (sq & 8)) {
		pc = board[sq];
		if (pc == OB)
			return 1;
		if (pc == BK) {
			if (kingsq[0] != sq)
				return 2;
			k[0] = sq;
		} else if (pc == WK) {
			if (kingsq[1] != sq)
				return 3;
			k[1] = sq;
		}
		if (pc == WP || pc == BP)
			if (sq < H1 + 1 || sq > A8 - 1)
				return 7;
		if (pc) {
			if (pc < 0)
				return 8;
			hash ^= zobrist[pc][sq];
			m[pc & 1] += fval[pc];
			
			if ( pieces[pc&1][indx[sq]] != sq ) {
				printf("pieces != sq %d       \n", sq);
				printf("pc=%d indx[sq]=%d\n", pc, indx[sq]);
				printf("pieces = %d\n", pieces[pc&1][indx[sq]]);
				display();
				return 9;
			}
		}
	}
	hash ^= zobrist[9][A1 | castle];
	hash ^= zobrist[8][epsquare];
	if (stm == 0)
		hash = ~hash;
	
	if(k[0] == 0 || k[1] == 0) return 4;

	if (force[0] != m[0] || force[1] != m[1]) {
		printf ("\nforces = %d %d  m = %d %d\n", force[0], force[1], m[0], m[1]);
		return 6;
	}
		
	/* Check for reciprical links in indx & pieces */
	for (pc = 0; pc < 16; ++pc) {
		if (pieces[BLACK][pc]) {
			if ( indx[pieces[BLACK][pc]] != pc ) {
				printf("indx != BLACK pc %d\n", pc);
				return 10;
			}
		}
		if (pieces[WHITE][pc]) {
			if ( indx[pieces[WHITE][pc]] != pc ) {
				printf("indx != WHITE pc %d\n", pc);
				return 11;
			}
		}
	}
	
	if (hash != key) {
		if (hash != makehashkey ())
			printf ("Hash key off in checkboard()!\n");
		return 5;
	}	
	
	return 0;
}

int 
checkmove (uMOVE * m)
{
	int             f = m->data.from;
	int             d = m->data.dest;
	int             p = m->data.piece;
	if (!((f & 8) == 0 && f >= A1 && f <= H8))
		return 1;
	if (!((d & 8) == 0 && d >= A1 && d <= H8))
		return 2;
	if (!(board[f] != MT))
		return 3;
	if (!(board[f] != OB))
		return 4;
	if ((p&1) != stm) 
		return 7;
	if (board[f] != (BP | stm) && board[f] != p) {
		display ();
		printf ("\n%d != %d @ %d to %d\n", board[f], p, f, d);
		return 5;
	}
	if (!pseudoev[128 + f - d])
		return 6;
	return 0;
}

void 
check_pst (void)
{
	int             p, s, s1, x, y;
	for (p = 1; p < 8; ++p) {
		for (y = 0; y < 7; ++y) {
			for (x = 0; x < 4; ++x) {
				s = A1 + 16 * y + x;
				s1 = A1 + 16 * y + 7 - x;
				if (pst[p * 2][s] != pst[p * 2][s1])
					printf ("symetry error in pst: %d %d %d\n", p, s, s1);
			}
			for (x = 0; x < 7; ++x) {
				s = A1 + 16 * y + x;
				s1 = A8 - 16 * y + x;
				if (pst[p * 2][s] != pst[p * 2 + 1][s1])
					printf ("side error in pst: %d %d %d\n", p, s, s1);
			}
		}
	}
}

void 
histograph ()
{
	int             x, y, s;
	for (x = A1; x < nbSquares; x++) {
		if (x & 8) {
			x += 8;
			printf ("\n");
		}
		s = 0;
		for (y = A1; y < nbSquares; y++, y += (y & 8)) {
			s += history[stm][Ox88to64 (y) * 64 + Ox88to64 (x)];
		}
		printf ("%d\t", s);
	}
	printf ("\n");
}

void 
flip_board (void)
{
	int             s, p1, p2;
	char            epd[256];
	for (s = 0; s < 56; ++s, s += (s & 8)) {
		p1 = board[A1 + s];
		if (p1) {
			key ^= zobrist[p1][A1 + s];
			p1 ^= 1;
			key ^= zobrist[p1][A1 + (s ^ 0x70)];
			if ((p1 & 14) == BK)
				kingsq[p1 & 1] = A1 + (s ^ 0x70);
		}
		p2 = board[A1 + (s ^ 0x70)];
		if (p2) {
			key ^= zobrist[p2][A1 + (s ^ 0x70)];
			p2 ^= 1;
			key ^= zobrist[p2][A1 + s];
			if ((p2 & 14) == BK)
				kingsq[p2 & 1] = A1 + s;
		}
		board[A1 + s] = p2;
		board[A1 + (s ^ 0x70)] = p1;
	}
	kingsq[0] -= A1;
	kingsq[0] ^= 0x70;
	kingsq[0] += A1;
	kingsq[1] -= A1;
	kingsq[1] ^= 0x70;
	kingsq[1] += A1;
	if (epsquare) {
		key ^= zobrist[8][epsquare];
		epsquare -= A1;
		epsquare ^= 0x70;
		epsquare += A1;
		key ^= zobrist[8][epsquare];
	}
	if (castle) {
		key ^= zobrist[9][castle | A1];
		p1 = (castle & 12) >> 2;
		p2 = (castle & 3) << 2;
		castle = p1 | p2;
		key ^= zobrist[9][castle | A1];
	}
	stm ^= 1;
	key = ~key;
	memset (history, 0, sizeof (history));
	clearhash ();
	p1 = force[0];
	force[0] = force[1];
	force[1] = p1;
	for (p2 = 0; p2 < 8; ++p2) {
		p1 = types[p2 * 2];
		types[p2 * 2] = types[p2 * 2 + 1];
		types[p2 * 2 + 1] = p1;
	}
	buildepd (epd);
	setup (epd);
}

void 
_failed (const char format[],...)
{
	va_list         ap;
	ASSERT (format != NULL);
	va_start (ap, format);
	vfprintf (stderr, format, ap);
	va_end (ap);
	exit (1);
}
