/*
 
    Texel - A UCI chess engine.
 
    Copyright (C) 2012  Peter Ă–sterlund, peterosterlund2@gmail.com
 
 
 
    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 <http://www.gnu.org/licenses/>.
 
*/
 
 
 
/*
 
 * uciprotocol.cpp
 
 *
 
 *  Created on: Mar 4, 2012
 
 *      Author: petero
 
 */
 
 
 
#include "uciprotocol.hpp"
 
#include "searchparams.hpp"
 
#include "computerPlayer.hpp"
 
#include "textio.hpp"
 
 
 
#include <iostream>
 
 
 
 
 
void
 
UCIProtocol::main(bool autoStart) {
 
    UCIProtocol uciProt;
 
    uciProt.mainLoop(std::cin, std::cout, autoStart);
 
}
 
 
 
UCIProtocol::UCIProtocol()
 
    : pos(TextIO::readFEN(TextIO::startPosFEN)),
 
      quit(false)
 
{
 
}
 
 
 
void
 
UCIProtocol::mainLoop(std::istream& is, std::ostream& os, bool autoStart) {
 
    if (autoStart)
 
        handleCommand("uci", os);
 
    std::string line;
 
    while (true) {
 
        getline(is, line);
 
        if (!is.good()) {
 
            if (engine)
 
                engine->stopSearch();
 
            break;
 
        }
 
        handleCommand(line, os);
 
        if (quit)
 
            break;
 
    }
 
}
 
 
 
void
 
UCIProtocol::handleCommand(const std::string& cmdLine, std::ostream& os) {
 
    std::vector<std::string> tokens;
 
    tokenize(cmdLine, tokens);
 
    const int nTok = (int)tokens.size();
 
    if (nTok == 0)
 
        return;
 
    try {
 
        std::string cmd = tokens[0];
 
        if (cmd == "uci") {
 
            os << "id name " << ComputerPlayer::engineName << std::endl;
 
            os << "id author Peter Osterlund" << std::endl;
 
            EngineControl::printOptions(os);
 
            os << "uciok" << std::endl;
 
        } else if (cmd == "isready") {
 
            initEngine(os);
 
            os << "readyok" << std::endl;
 
        } else if (cmd == "setoption") {
 
            initEngine(os);
 
            std::string optionName;
 
            std::string optionValue;
 
            if (nTok < 2)
 
                return;
 
            if (tokens[1] == "name") {
 
                int idx = 2;
 
                while ((idx < nTok) && (tokens[idx] != "value")) {
 
                    optionName += toLowerCase(tokens[idx++]);
 
                    optionName += ' ';
 
                }
 
                if ((idx < nTok) && (tokens[idx++] == "value")) {
 
                    while ((idx < nTok)) {
 
                        optionValue += toLowerCase(tokens[idx++]);
 
                        optionValue += ' ';
 
                    }
 
                }
 
                engine->setOption(trim(optionName), trim(optionValue), true);
 
            }
 
        } else if (cmd == "ucinewgame") {
 
            if (engine)
 
                engine->newGame();
 
        } else if (cmd ==  "position") {
 
            std::string fen;
 
            int idx = 1;
 
            if (nTok < 2)
 
                return;
 
            if (tokens[idx] == "startpos") {
 
                idx++;
 
                fen = TextIO::startPosFEN;
 
            } else if (tokens[idx] == "fen") {
 
                idx++;
 
                std::string sb;
 
                while ((idx < nTok) && (tokens[idx] != "moves")) {
 
                    sb += tokens[idx++];
 
                    sb += ' ';
 
                }
 
                fen = trim(sb);
 
            }
 
            if (fen.length() > 0) {
 
                pos = TextIO::readFEN(fen);
 
                moves.clear();
 
                if ((idx < nTok) && (tokens[idx++] == "moves")) {
 
                    for (int i = idx; i < nTok; i++) {
 
                        Move m = TextIO::uciStringToMove(tokens[i]);
 
                        if (m.isEmpty())
 
                            break;
 
                        moves.push_back(m);
 
                    }
 
                }
 
            }
 
        } else if (cmd == "go") {
 
            initEngine(os);
 
            int idx = 1;
 
            SearchParams sPar;
 
            bool ponder = false;
 
            while (idx < nTok) {
 
                std::string subCmd = tokens[idx++];
 
                if (subCmd == "searchmoves") {
 
                    while (idx < nTok) {
 
                        Move m = TextIO::uciStringToMove(tokens[idx]);
 
                        if (m.isEmpty())
 
                            break;
 
                        sPar.searchMoves.push_back(m);
 
                        idx++;
 
                    }
 
                } else if (subCmd == "ponder") {
 
                    ponder = true;
 
                } else if (subCmd == "wtime") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.wTime);
 
                } else if (subCmd == "btime") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.bTime);
 
                } else if (subCmd == "winc") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.wInc);
 
                } else if (subCmd == "binc") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.bInc);
 
                } else if (subCmd == "movestogo") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.movesToGo);
 
                } else if (subCmd == "depth") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.depth);
 
                } else if (subCmd == "nodes") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.nodes);
 
                } else if (subCmd == "mate") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.mate);
 
                } else if (subCmd == "movetime") {
 
                    if (idx < nTok)
 
                        str2Num(tokens[idx++], sPar.moveTime);
 
                } else if (subCmd == "infinite") {
 
                    sPar.infinite = true;
 
                }
 
            }
 
            if (ponder) {
 
                engine->startPonder(pos, moves, sPar);
 
            } else {
 
                engine->startSearch(pos, moves, sPar);
 
            }
 
        } else if (cmd == "stop") {
 
            if (engine)
 
                engine->stopSearch();
 
        } else if (cmd == "ponderhit") {
 
            engine->ponderHit();
 
        } else if (cmd == "quit") {
 
            if (engine)
 
                engine->stopSearch();
 
            quit = true;
 
        }
 
    } catch (const ChessParseError&) {
 
    }
 
}
 
 
 
void
 
UCIProtocol::initEngine(std::ostream& os) {
 
    if (!engine)
 
        engine = std::make_shared<EngineControl>(os);
 
}
 
 
 
/** Convert a string to tokens by splitting at whitespace characters. */
 
void
 
UCIProtocol::tokenize(const std::string& cmdLine, std::vector<std::string>& tokens) {
 
    tokens.clear();
 
    std::string tmp = trim(cmdLine);
 
    int start = 0;
 
    bool inWord = true;
 
    for (int i = 0; i < (int)tmp.size(); i++) {
 
        if (inWord) {
 
            if (isspace(tmp[i])) {
 
                tokens.push_back(tmp.substr(start, i - start));
 
                inWord = false;
 
            }
 
        } else {
 
            if (!isspace(tmp[i])) {
 
                start = i;
 
                inWord = true;
 
            }
 
        }
 
    }
 
    if (inWord)
 
        tokens.push_back(tmp.substr(start, tmp.size() - start));
 
}