/*
    Copyright 2015 Harm Jetten

    This file is part of Moby Dam.

    Moby Dam 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.

    Moby Dam 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 Moby Dam.  If not, see <http://www.gnu.org/licenses/>.
*/

/* val.c: evaluate a board position */

#include "test.h"

bool debug_info;                 /* print extra debug info */
char db_dirs[PATH_MAX] = ".";    /* directory/ies of endgame database files */
char eval_dirs[PATH_MAX] = ".";  /* directory/ies of evaluation table files */

/* read bitboard components and construct bitboard */
/* bb -> board structure to fill */
/* arg -> bitboard components white,black,kings,side */
/* returns: TRUE if successful */
static bool setup_bitboard(bitboard *bb, char *arg)
{
    int i;

    i = sscanf(arg, "%" SCNi64 ",%" SCNi64 ",%" SCNi64 ",%u",
               &bb->white, &bb->black, &bb->kings, &bb->side);
    if (i != 4)
    {
        printf("error: wrong nr of bitboard values entered\n");
        return FALSE;
    }
    printf("0x%" PRIx64 ",0x%" PRIx64 ",0x%" PRIx64 ",%u\n",
           bb->white, bb->black, bb->kings, bb->side);
    if ((bb->white & bb->black) != 0)
    {
        printf("error: white/black occupation conflict\n");
        return FALSE;
    }
    if (((bb->white | bb->black) & bb->kings) != bb->kings)
    {
        printf("error: kings occupation conflict\n");
        return FALSE;
    }
    if (((bb->white | bb->black | bb->kings) & ~ALL50) != 0)
    {
        printf("error: occupation beyond squares 1-50\n");
        return FALSE;
    }
    if (((bb->white & ~bb->kings) & ROW1) != 0 ||
        ((bb->black & ~bb->kings) & ROW10) != 0)
    {
        printf("error: unpromoted man\n");
        print_board(bb);
        return FALSE;
    }
    return TRUE;
}

/* read "print_board()" output copy-pasted to stdin and construct bitboard */
/* bb -> board structure to fill */
/* returns: TRUE if successful */
static bool setup_printedboard(bitboard *bb)
{
    int sq, ret;
    bool ok;

    sq = 0;
    ok = TRUE;
    empty_board(bb);
    do
    {
        ret = fgetc(stdin);
        switch (ret)
        {
        case 'w':
            ok = place_piece(bb, ++sq, MW);
            break;
        case 'W':
            ok = place_piece(bb, ++sq, KW);
            break;
        case 'b':
            ok = place_piece(bb, ++sq, MB);
            break;
        case 'B':
            ok = place_piece(bb, ++sq, KB);
            break;
        case '.':
            sq++;
            break;
        case '(':
            if (sq == 50)
            {
                ret = fgetc(stdin);
                switch (ret)
                {
                case 'w':
                    bb->side = W;
                    return TRUE;
                case 'b':
                    bb->side = B;
                    return TRUE;
                default:
                    return FALSE;
                }
            }
            return FALSE;
        default:
            break;
        }
    } while (sq <= 50 && ok && ret != EOF);
    return FALSE;
}

/* set up test board, do eval and show result */
int main(int argc, char *argv[])
{
    char fen[210];
    s32 score, inv_score;
    int opt;
    bitboard brd;
    bool dbdir = FALSE;
    bool checksym = FALSE;

    init_board(&brd);

    while (TRUE)
    {
        opt = getopt(argc, argv, "de:sv:");
        if (opt == -1) 
        {
            break;
        }
        switch (opt) 
        {
        case 'd':
            debug_info = TRUE;
            break;
        case 'e':
            strncpy(db_dirs, optarg, sizeof db_dirs - 1);
            dbdir = TRUE;
            break;
        case 's':
            checksym = TRUE;
            break;
        case 'v':
            strncpy(eval_dirs, optarg, sizeof eval_dirs - 1);
            break;
        default:
            printf("Usage: %s [-d] [-e dbdir] [-v evaldir] [FEN or bitboard]\n", argv[0]);
            printf("  -d = print lots of extra debug info\n"
                   "  -e dbdir = directory holding endgame database files\n"
                   "       (or multiple colon-separated directories)\n"
                   "       (default: current directory)\n"
                   "  -s = check symmetry of evaluation function\n"
                   "  -v evaldir = directory holding evaluation table files\n"
                   "       (default: current directory)\n"
                   "  FEN = board position to evaluate\n"
                   "       (default: read graphical board from stdin)\n"
                   "  bitboard = components as values separated by commas\n"
                   "       (example: 0x2000000000018,0x4000001000,0x2004000001008,1)\n");
            exit(EXIT_FAILURE);
        }
    }

    if (!init_eval(eval_dirs))
    {
        exit(EXIT_FAILURE);
    }
    if (optind < argc)
    {
        if (argv[optind][0] >= 'B')
        {
            if (!setup_fen(&brd, argv[optind]))
            {
                exit(EXIT_FAILURE);
            }
        }
        else
        {
            if (!setup_bitboard(&brd, argv[optind]))
            {
                exit(EXIT_FAILURE);
            }
        }
    }
    else
    {
        if (!setup_printedboard(&brd))
        {
            print_board(&brd);
            exit(EXIT_FAILURE);
        }
    }

    if (dbdir)
    {
        init_enddb(db_dirs);
    }

    make_fen(fen, &brd);
    printf("%s\n", fen);

    print_board(&brd);
    score = eval_board(&brd);
    printf("eval score=%d\n", score);

    /* check symmetry of evaluation function */
    if (checksym)
    {
        invert_board(&brd);
        if (debug_info)
        {
            print_board(&brd);
        }
        inv_score = eval_board(&brd);
        if (score != inv_score)
        {
            printf("error: eval score=%d inv_score=%d\nhit enter\n", score, inv_score);
            getchar();
            exit(EXIT_FAILURE);
        }
    }

    if (dbdir && endgame_value(&brd, 0, &score))
    {
        printf("endgame_value score=%d\n", score);
        printf("egdb errors=%" PRIu64 "\n", end_err);
    }
    return EXIT_SUCCESS;
}
