#include <stdio.h>
#include <strings.h>
#include <string.h>

// include all the engine headers
#include "ramjet.h"
#include "bitboard.h"
#include "quadboard.h"
#include "state.h"
#include "move.h"
#include "eval.h"
#include "hash.h"
#include "position.h"
#include "sort.h"
#include "game.h"
#include "pv.h"
#include "options.h"
#include "stats.h"
#include "params.h"
#include "search.h"

// max length of a UCI command
#define UCI_MAX 8192

// UCI strings
#define ENGINE_UCI_STR "id name RamJet 0.14"
#define AUTHOR_UCI_STR "id author Edoardo Manino"

// wait for commands from the GUI
void uci_loop(FILE* stream);

// just a tiny main to get things started
int main()
{
    FILE* stream = stdin;

    uci_loop(stream);

    return 0;
}

// wait for commands from the GUI
void uci_loop(FILE* stream)
{
    char line[UCI_MAX];
    char *uci, *token;
    SEARCH src;

    // initialisations
    int init = 0;
    opt_init(&src.opt, &src.ht);
    par_clear(&src.par);

    // no buffering
    setbuf(stream, NULL);
    setbuf(stdout, NULL);

    // wait for input
    while(fgets(line, UCI_MAX, stream) != NULL) {

        // logging
        if(src.opt.log != NULL) fprintf(src.opt.log, "GUI: %s", line);

        // parse the command
        for(uci = &line[0], token = grab_token(&uci); token[0] != '\0'; token = grab_token(&uci)) {

            // always accept a quit, uci or isready command
            if(!strcmp(token, "quit")) {
                src.par.sm = SM_HALT;
                opt_free(&src.opt, &src.ht);
                return;
            } else if(!strcmp(token, "uci")) {
                print_line(src.opt.log, ENGINE_UCI_STR);
                print_line(src.opt.log, AUTHOR_UCI_STR);
                print_line(src.opt.log, PONDER_UCI_STR);
                print_line(src.opt.log, LOG_UCI_STR);
                print_line(src.opt.log, C960_UCI_STR);
                print_line(src.opt.log, OM_UCI_STR);
                print_line(src.opt.log, HT_UCI_STR);
                print_line(src.opt.log, "uciok");
                break;
            } else if(!strcmp(token, "isready")) {
                if(!init) {
                    rj_init();
                    bb_init();
                    st_init();
                    eval_init();
                    init = 1;
                }
                print_line(src.opt.log, "readyok");
                break;

            // accept a setoption, position or go command only when the engine is idle
            } else if(src.par.sm == SM_HALT) {
                if(!strcmp(token, "setoption")) {
                    opt_scan_uci(uci, &src.opt, &src.ht);
                    break;
                } else if(!strcmp(token, "position")) {
                    gm_scan_uci(uci, &src.gm, src.opt.c960);
                    break;
                } else if(!strcmp(token, "go")) {
                    POSITION* pos = gm_get_ply(&src.gm, 0);
                    par_scan_uci(uci, &src.par, ST_GET_PL(pos->st));
                    if(stream == stdin)
                        new_thread(src_root, (void*) &src);
                    else
                        src_root((void*) &src); // serial execution in debug mode
                    break;
                }

            // accept a ponderhit or stop command only whem the engine is running
            } else {
                if(!strcmp(token, "ponderhit")) {
                    src.par.sm = SM_SMART;
                    break;
                } else if(!strcmp(token, "stop")) {
                    src.par.sm = SM_HALT;
                }
            }
        }
    }

    // error
    print_line(src.opt.log, "info string error: can't read the stdin");
}
