/*
 * Copyright (C) 1999, 2008 Robert Wilhelm
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, see .
 */
#include 
#include 
#include 
#include "chess_position.h"
#include "chess_notation.h"
static char piece_to_ascii_t[]= {' ','N','B','R','Q','K'};
static int norm_piece (Piece piece);
static void
file_to_ascii (char **move, Square square)
{
	*(*move)++ = square - square / 10 * 10 + 96;	/* a - h */
}
static void
rank_to_ascii (char **move, Square square)
{
	*(*move)++ = square / 10 + 47 ;			/* 1 - 8 */
}
void
square_to_ascii(char **move, Square square)
{
	file_to_ascii (move, square);
	rank_to_ascii (move, square);
}
static int
same_rank (Square square, Square square2)
{
	char *s1;
	char *s2;
	char same1;
	char same2;
	s1 = &same1;
	s2 = &same2;
	rank_to_ascii (&s1, square);
	rank_to_ascii (&s2, square2);
	if (same1 == same2)
		return 1;
	return 0;
}
static int
same_file (Square square, Square square2)
{
	char *s1;
	char *s2;
	char same1;
	char same2;
	s1 = &same1;
	s2 = &same2;
	file_to_ascii (&s1, square);
	file_to_ascii (&s2, square2);
	if (same1 == same2)
		return 1;
	return 0;
}
static void
delete_x (char *str)
{
	char *p = strchr (str, 'x');
	if (p)
		while ((*p = *(p+1)))
			p++;
}
static void
delete_plus (char *str)
{
	char *p = strchr (str, '+');
	if (p)
		while ((*p = *(p+1)))
			p++;
}
static void
delete_ep (char *str)
{
	char *p = strstr (str, "ep");
	if (p)
		while ((*p = *(p+2)))
			p++;
}
static void
delete_equal (char *str)
{
	char *p = strstr (str, "=");
	if (p)
		while ((*p = *(p+1)))
			p++;
}
static void
delete_hash (char *str)
{
	char *p = strstr (str, "#");
	if (p)
		while ((*p = *(p+1)))
			p++;
}
char *
move_to_ascii (char *p, Square from, Square to)
{
	Square a;
	file_to_ascii (&p, from);
	rank_to_ascii (&p, from);
	if (to & 128) {
                /* promotion */
		a = to;
		if (from > E4)
			a = (a & 7) + A8; /* white */
		else
			a = (a & 7) + A1; /* black */
		*p++    = a - a / 10 * 10 + 96;     /*  a - h       */
		*p++    = a / 10 + 47 ;             /*  1 - 8       */
		*p++    = '=';
		*p++ = piece_to_ascii_t[((to >> 3) & 7)-1];
	} else {
		file_to_ascii (&p, to);
		rank_to_ascii (&p, to);
	}
	*p = '\0';
	return p;
}
int
ascii_to_piece (char p)
{
	if (p == 'q')
		return WQ-WP;
	if (p == 'r')
		return WR-WP;
	if (p == 'b')
		return WB-WP;
	if (p == 'n')
		return WN-WP;
	if (p == 'Q')
		return WQ-WP;
	if (p == 'R')
		return WR-WP;
	if (p == 'B')
		return WB-WP;
	if (p == 'N')
		return WN-WP;
	g_assert_not_reached ();
	return -1;
}
char
piece_to_ascii (int piece)
{
  static char piece_to_ascii_full[]= {'P','N','B','R','Q','K'};
  int i;
  if(piece == EMPTY)
    return(' ');
  i = norm_piece (piece);
  if(WPIECE(piece))
    return piece_to_ascii_full[i];
  else
    return tolower(piece_to_ascii_full[i]);
  g_assert_not_reached ();
  return -1;
}
void
ascii_to_move (Position *pos, char *p, Square *from, Square *to)
{
	delete_x (p);
	if (*p == 'o') {
		/* Castling */
		if (!strcmp (p, "o-o-o")) {
			if (position_get_color_to_move (pos) == WHITE) {
				*from = E1;
				*to   = C1;
			} else {
				*from = E8;
				*to   = C8;
			}
		} else {
			if (position_get_color_to_move (pos) == WHITE) {
				*from = E1;
				*to   = G1;
			} else {
				*from = E8;
				*to   = G8;
			}
		}
		return;
	}
	*from = (*p - 'a' + 1) + (*(p + 1) - '1' + 2 ) * 10;
	p += 2;
	*to = (*p - 'a' + 1) + (*(p + 1) - '1' + 2 ) * 10;
	p += 2;
	if (*p == 'q' || *p == 'r' || *p == 'b' || *p =='n' ||
	    *p =='Q' || *p =='R' || *p == 'B' || *p == 'N' ) {
                /* Promotion */
		if (*to < A2)
			*to = 128 +  *to - A1 + (ascii_to_piece (*p) + 1) * 8;
		else if (*to > A7)
			*to = 128 +  *to - A8 + (ascii_to_piece (*p) + 1) * 8;
		else
			g_assert_not_reached ();
	}
}
int
san_to_move (Position *pos, char *str, Square *from, Square *to)
{
	Square zugliste[AB_ZUGL];
	Square *ap, *aq;
	gshort anz, anz_n, anz_s;
	gshort i;
	gchar *p;
	gchar liste[100][10];
	delete_x(str);
	delete_plus(str);
	delete_ep(str);
	delete_equal(str);
	delete_hash(str);
	ap =  zugliste + AB_ZUG_S;
	anz = position_legal_move (pos, &ap, &anz_s, &anz_n);
	for (aq = ap, i = 0; i < anz; i++, aq += 2) {
		p = liste[i];
		piece_move_to_ascii (p, pos->square[*aq], *aq, *(aq + 1));
		if (*p == ' ') {
                        /* pawn move */
                        /* e.g. e2e4 */
			p++;
			if (!strcmp (p, str)) {
				*from = *aq;
				*to   = *(aq+1);
				return 0;
			}
                        /* e.g. ed5 */
			p[1]=p[2];
			p[2]=p[3];
			p[3]=p[4];
			p[4]=p[5];
                        /* not e.g. bb3 */
			if (p[0] != p[1])
				if (!strcmp(p,str)) {
					*from = *aq;
					*to   = *(aq+1);
					return 0;
				}
                        /* e.g. d5 */
			p++;
			if (!strcmp(p,str)) {
				*from = *aq;
				*to   = *(aq+1);
				return 0;
			}
		} else {
			/* no pawn move */
			char  tmp;
                        /* e.g. Ng1f3 */
			if (!strcmp (p, str)) {
				*from = *aq;
				*to   = *(aq+1);
				return 0;
			}
                        /* Ngf3 */
			tmp =p[2];
			p[2]=p[3];
			p[3]=p[4];
			p[4]=p[5];
			if (!strcmp(p,str)) {
				*from = *aq;
				*to   = *(aq+1);
				return 0;
			}
			/* N1f3 */
			p[1]=tmp;
			if (!strcmp(p,str)) {
				*from = *aq;
				*to   = *(aq+1);
				return 0;
			}
			/* Nf3 */
			p[1]=p[2];
			p[2]=p[3];
			p[3]=p[4];
			if (!strcmp(p,str)) {
				*from = *aq;
				*to   = *(aq+1);
				return 0;
			}
		}
	}
	return 1;
}
static int
norm_piece (Piece piece)
{
	if (WPIECE (piece))
		return piece - WP;
	if (BPIECE (piece))
		return piece - BP;
	return piece;
}
void
piece_move_to_ascii (char *p, Piece piece, Square from, Square to)
{
	int i;
        if ((piece == WK || piece == BK) && abs (from - to) == 2) {
		if (to % 10 == 3) {
			strcpy (p,"O-O-O");
			return;
		}
		if (to % 10 == 7) {
			strcpy (p,"O-O");
			return;
		}
		g_assert_not_reached ();
	}
        i = norm_piece (piece);
        *p++ = piece_to_ascii_t[i];
        move_to_ascii (p, from, to);
}
char *
move_to_san (Position *pos, Square from, Square to)
{
	Square checksquare, checkto;
	Piece piece, promote;
	int norm, desrank, desfile, inc;
	int i, tempdesfile, tempdesrank;
	char *san;
	char *temp;
	const int jump[]={ 8, 12,19, 21,-8,-12,-19,-21};
	san = g_new0 (char, 12);
	temp = san;
	desrank = desfile = promote = 0;
	/* Handle Promotion */
	if (to & 128) {
		promote = ((to >> 3) & 7)-1;
     		if ( from > E4) {
			to = (to & 7) + A8;
			piece = WP;
		} else {
			to = (to & 7) + A1;
			piece = BP;
		}
	} else {
		piece = pos->square[to];
	}
	/* Check if we have to designate the rank or file */
	switch (piece) {
	case WQ:
	case BQ:
		/* Check like rooks and bishops */
	case WR:
	case BR:
		/* Check for other rooks/queens */
		i = 0;
		while (1) {
			tempdesfile = tempdesrank = 0;
			if (i == 0) {
				checksquare = 20 + (to % 10);
				checkto =  90 + (to % 10);
				if (from / 10 > to / 10 )
					checkto = to - 10;
				else if (from / 10 < to /10)
					checksquare = to + 10;
				inc = 10;
			} else if (i == 1) {
				checksquare = 10 * (to / 10) + 1;
				checkto =  10 * (to / 10) + 8;
				if (from % 10 > to % 10)
					checkto = to - 1;
				else if (from % 10 < to % 10)
					checksquare = to + 1;
				inc = 1;
			} else {
				break;
			}
			while (checksquare <= checkto) {
				if (pos->square[checksquare] == piece && checksquare != to) {
					if (same_rank(from, checksquare))
						tempdesfile = 1;
					else if (same_file(from, checksquare))
						desrank = 1;
					else
						tempdesfile = 1;
				} else if (pos->square[checksquare] != EMPTY && checksquare < to) {
					tempdesfile = tempdesrank = 0;	/* A piece is in the way */
				} else if (pos->square[checksquare] != EMPTY && checksquare > to) {
					break;			/* A piece is in the way */
				}
				checksquare += inc;
			}
			i++;
			if (tempdesfile == 1) desfile = 1;
			if (tempdesrank == 1) desrank = 1;
		}
		if (piece == WR || piece == BR) break;
	case WB:
	case BB:
		/* Check for other bishops/queens */
		i = 0;
		while (1) {
			tempdesfile = tempdesrank = 0;
			if (i == 0) {
				checksquare = to - (((to % 10) - 1) * 11);
				checkto =  to + ((9 - (to / 10)) * 11);
				if (from % 10 > to % 10  && from / 10 > to / 10)
					checkto = to - 11;
				else if (from % 10 < to % 10 && from / 10 < to / 10)
					checksquare = to + 11;
				inc = 11;
			} else if (i == 1) {
				checksquare = to - ((8 - (to % 10)) * 9);
				checkto =  to + ((9 - (to / 10)) * 9);
				if (from % 10 > to % 10 && from / 10 < to / 10)
					checksquare = to + 9;
				else if (from % 10 < to % 10  && from / 10 > to / 10)
					checkto = to - 9;
				inc = 9;
			} else {
				break;
			}
			while (checksquare <= checkto) {
				if (pos->square[checksquare] == piece && checksquare != to) {
					if (same_rank(from, checksquare))
						tempdesfile = 1;
					else if (same_file(from, checksquare))
						desrank = 1;
					else
						tempdesfile = 1;
				} else if (pos->square[checksquare] != EMPTY && checksquare < to) {
					tempdesfile = tempdesrank = 0;	/* A piece is in the way */
				} else if (pos->square[checksquare] != EMPTY && checksquare > to) {
					break;			/* A piece is in the way */
				}
				checksquare += inc;
			}
			i++;
			if (tempdesfile == 1) desfile = 1;
			if (tempdesrank == 1) desrank = 1;
		}
		break;
	case WN:
	case BN:
		/* Check for other knights */
		for (i=0;i<8;i++) {
			if (pos->square[to+jump[i]] == piece && to+jump[i] >= 0) {
				if (same_rank(from, to+jump[i]))
					desfile = 1;
				else if (same_file(from, to+jump[i]))
					desrank = 1;
				else
					desfile = 1;
			}
		}
		break;
	}
	/* Handle Castling */
	if ((piece == WK || piece == BK) && abs(from-to) == 2) {
		if (to % 10 == 3)
			strcpy(temp,"O-O-O");
		if (to % 10 == 7)
			strcpy(temp,"O-O");
	} else {
		/* The piece letter */
	        norm = norm_piece(piece);
		if (norm > 0)
		        *temp++ = piece_to_ascii_t[norm];
		/* The rank/file designators */
		if (desfile)
			file_to_ascii(&temp, from);
		if (desrank)
			rank_to_ascii(&temp, from);
		/* If there was a capture */
		if (position_last_piece_captured (pos) != EMPTY) {
			if (piece == WP || piece == BP)
				file_to_ascii (&temp, from);
			*temp++ = 'x';
		}
		/* Destination square */
	        square_to_ascii (&temp, to);
		/* If there was promotion */
		if (promote) {
			*temp++ = '=';
			norm = norm_piece(promote);
			*temp++ = piece_to_ascii_t[norm];
		}
		*temp = '\0';
	}
	temp = san;
	san = g_strdup (temp);
	g_free (temp);
	return san;
}