// Copyright 1996 by Jon Dart.  All Rights Reserved.

// Console version of Arasan, works with WinBoard

#include "stdafx.h"
#include "board.h"
#include "movegen.h"
#include "search.h"
#include "options.h"
#include "movearr.h"
#include "notation.h"
#include "timectrl.h"
#include "rmove.h"
#include "scoring.h"
#include "attacks.h"
#include "chessio.h"
#include "log.h"
extern "C"
{
#include <string.h>
#include <ctype.h>
};
#include "book.h"
#include <fstream.h>
#include <iostream.h>
#include <strstrea.h>
#include <ctype.h>
#include <time.h>
#include <sys/timeb.h>

#define LOG_FILE "games.pgn"

Options *global_options = NULL;
Move_Array *game_moves = NULL;
Book *opening_book = NULL;
char *programPath;
static BOOL verbose = FALSE;
Log *theLog = NULL;
static ReversibleMove last_move;
static char last_move_image[80];
static char log_file_name[255];
static int32 time_limit = -1;
static int32 time_left = 0;
static int32 opp_time = 0;
static int book_moves = 0;
static int moves = 40;
static int minutes = 5;
static int incr = 0;
static BOOL first_move = TRUE;
static BOOL computer_plays_white = FALSE;
static BOOL first = FALSE;
static Time_Info ti;

static void move_image(const Board &board, Move m, char *buf)
{
    // Produce a text string representing the move, in a form
    // Xboard understands.
    ExtendedMove emove(board,m);
    if (emove.Special() == ExtendedMove::KCastle)
    {
        strcpy(buf,"o-o");
    }
    else if (emove.Special() == ExtendedMove::QCastle)
    {
        strcpy(buf,"o-o-o");
    }
    else
    {
        *buf++ = m.StartSquare().FileImage();
        *buf++ = m.StartSquare().RankImage();
        *buf++ = m.DestSquare().FileImage();
        *buf++ = m.DestSquare().RankImage();
        if (emove.Special() == ExtendedMove::Promotion)
        {
            *buf++ = Piece::Image(m.PromoteTo());
        }
        *buf = '\0';
    }                  
}

static void save_game()
{
   if (game_moves->num_moves() == 0)
       return;
   ofstream ofs(log_file_name,ios::out | ios::app);
   if (ofs.good())
   {
        ChessIO::store_pgn(ofs, *theLog,
                      computer_plays_white ? White : Black,
                      NULL);
        ofs.close();
   }          
   else
   {
        cerr << "error opening logfile" << endl;
        MessageBox(NULL,"error opening log","",MB_OK);
   }
}

static Move search(Board &board, Search::Statistics &stats)
{
    Search searcher;
    
    stats.clear();
    Move move = opening_book->book_move(board,Balanced);
    if (move.IsNull())
    {
        // no book move
        Time_Info ti;
        Search_Limit limit;
        if (time_left > 0)
        {
           int moves_in_game = game_moves->num_moves()/2; // full moves, not half-moves
           int moves_to_time_control = moves-(moves_in_game % moves);
           cout << "moves left " << moves_to_time_control << endl;
           if (time_left > 500)
               time_left-= 500;
           limit.seconds = 1 + (time_left/100)/moves_to_time_control;
           cout << "time target = " << limit.seconds << endl;
           ti = Time_Info(Time_Target,limit);
        }
        move = searcher.find_best_move(board, ti,
                stats, Move::NullMove(), FALSE, verbose );
    }
    else
    {
        book_moves++;
    }
    ExtendedMove emove(board,move);
    last_move = ReversibleMove(board,emove);
    if (!emove.IsNull())
    {
        Notation::Image(board,last_move,last_move_image);
        theLog->add_move(board,last_move,last_move_image,&stats,TRUE);
        char movebuf[20];
        move_image(board,emove,movebuf);
        cout << 1+game_moves->num_moves()/2 << ". ... ";
        cout << movebuf << endl;
        board.MakeMove(ReversibleMove(board,emove));
        game_moves->add_move(board,emove);
    }
    if (stats.state == Search::Draw ||
          stats.state == Search::Stalemate)
    {
        cout << "Draw" << endl;
        theLog->setResult("1/2-1/2");
    }
    else if (stats.value >= 9999)
    {
        if (computer_plays_white)
            theLog->setResult("1-0");
        else
            theLog->setResult("0-1");
        cout << "computer mates!" << endl;
    }
    else if (stats.state == Search::Checkmate)
    {
        if (computer_plays_white)
            theLog->setResult("0-1");
        else
            theLog->setResult("1-0");
    }
    else if (stats.state == Search::Resigns)
    {
        cout << "resign" << endl;
        if (computer_plays_white)
            theLog->setResult("0-1");
        else
            theLog->setResult("1-0");
    }
    return emove;
}    

static BOOL is_move(char *buf)
{
   if (isalpha(*buf) && isdigit(buf[1]) &&
       isalpha(buf[2]) && isdigit(buf[3]))
       return TRUE;
   else if (strcmp(buf,"o-o")==0 ||
      strcmp(buf,"o-o-o")==0)
       return TRUE;
   else
       return FALSE;
}

static void undo( Board &board)
{
         board.UndoMove((*theLog)[theLog->current()-1].move());
         theLog->back_up();
         game_moves->remove_move();
         if (theLog->current())
         {
             last_move = (*theLog)[theLog->current()-1].move();
             strcpy(last_move_image,(*theLog)[theLog->current()-1].image());
         }
         else
         {
             last_move.MakeNull();
             *last_move_image = '\0';
         }
}

int main(int argc, char **argv)
{
   Board board;
   Search_Limit limit;
   Search_Type type = Fixed_Ply;
   limit.max_ply = 2;
   int arg = 1;
   int do_perf = 0;

   global_options = new Options();
   game_moves = new Move_Array();
   programPath = strdup(argv[0]); // needed by opening book
   theLog = new Log();
   theLog->clear();
   theLog->write_header();
   opening_book = new Book(); 
   *last_move_image = '\0';
   
   strcpy(log_file_name,programPath);
   char *p = strrchr(log_file_name,'\\');
   strcpy(p+1,LOG_FILE);

   // Tell Winboard we're here
   cout << "Chess" << endl;

   // Read stdin until we get a quit command
   char buf[256];
   while (cin.getline(buf,255))
   {
     
      if (strcmp(buf,"quit")==0 || strcmp(buf,"end")==0)
      {
         break;
      }
      else if (strcmp(buf,"new")==0)
      {
         save_game();  
         board.Reset();
         theLog->reset();
         theLog->write_header();
         last_move.MakeNull();
         *last_move_image = '\0';
         delete game_moves;
         game_moves = new Move_Array();
      }
      else if (strcmp(buf,"hint")==0)
      {
      }
      else if (strcmp(buf,"depth")==0)
      {
      }
      else if (strncmp(buf,"level",5)==0)
      {
         sscanf(buf+6,"%ld %ld %ld",&moves,&minutes,&incr);
      }
      else if (strncmp(buf,"time",4)==0)
      {
          // my time left
          sscanf(buf+4,"%ld",&time_left);
//if (time_limit == -1)
//          {
//             time_limit = time_left;
//          }
          cout << "time left = " << time_left << endl;
          if (time_left <= 0)
              break;
      }
      else if (strncmp(buf,"otime",4)==0)
      {
          sscanf(buf+4,"%ld",&opp_time);
          if (opp_time <= 0)
              break;
      }
      else if (strcmp(buf,"post")==0)
      { 
         verbose = TRUE;
      }
      else if (strcmp(buf,"nopost")==0)
      {
         verbose = FALSE;
      }
      else if (strcmp(buf,"savegame")==0)
      {
      }
      else if (strcmp(buf,"remove")==0)
      {
          undo(board);
          undo(board);
      }
      else if (strcmp(buf,"undo")==0)
      {
          undo(board);
      }
      else if (strcmp(buf,"resign")==0)
      {
      }
      else if (strcmp(buf,"edit")==0)
      {
      }
      else if (strcmp(buf,"go")==0)
      {
         Search::Statistics stats;
         first = TRUE;
         if (computer_plays_white)
         {
            cout << "1. go" << endl;
            Move reply = search(board,stats);
         }
      }
      else if (strcmp(buf,"bk")==0)
      {
      }
      else if (strcmp(buf,"easy")==0)
      {
      }
      else if (strcmp(buf,"white")==0)
      {
         computer_plays_white = TRUE;
      }
      else if (strcmp(buf,"black")==0)
      {
         computer_plays_white = FALSE;
      }
      else if (strncmp(buf,"bogus",5)==0)
      {
      }
      else
      {
          // see if it could be a move
          if (isdigit(*buf))
          {
             char *p = buf;
             while (*p && !isalpha(*p)) ++p;
             if (*p)
             {
                char *q = buf;
                while (*p) *q++=*p++;
             }
          }
          if (is_move(buf))
          {
          // assume move
          cout << game_moves->num_moves() << ". " << buf << endl;

          ReversibleMove rmove;
          if (strcmp(buf,"o-o") == 0 ||
              strcmp(buf,"o-o-o") == 0)
          {
              char *p =  buf;
              while (*p) { *p = toupper(*p); p++; }
              ExtendedMove emove(board,Move::Value(buf,board.Side()));
              rmove = ReversibleMove(board,emove);
          }
          else
          {
              Square start = Square::Value(buf);
              Square dest = Square::Value(buf+2);
              if (buf[4])
              {
                  Piece::PieceType promotion = 
                      Piece::Value(toupper(buf[4]));
                  rmove = ReversibleMove(board,start,dest,
                    promotion);
              }
              else
                  rmove = ReversibleMove(board,start,dest);
          }
          if (!rmove.IsNull())
          {
          last_move = rmove;
          Notation::Image(board,rmove,last_move_image);
          theLog->add_move(board,last_move,last_move_image,NULL,TRUE);

          board.MakeMove(rmove);
          game_moves->add_move(board,rmove);
          Search::Statistics stats;
          Move reply = search(board,stats);
          }
          }
      }
   }

   if (opp_time <= 0)
       theLog->setResult("1-0");
   else if (time_left <= 0)
       theLog->setResult("0-1");
   save_game();
   delete opening_book;
   delete theLog;
   delete game_moves;
   delete global_options;
   
   cout << "quit" << endl;
   Sleep(2000);
   return 0;
}
