// Copyright 1994, 1995 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "log.h"
#include "notation.h"
#include "globals.h"
#include <stddef.h>
#include <string.h>
#ifdef _WINDOWS
#include <windows.h>
#endif
#include <io.h>
#include <stdio.h>

#define LOG_FILE_NAME "ARASAN.LOG"
#define MAX_ANAL 5

Log_Entry::Log_Entry(const ReversibleMove &move,
  const char *move_image)
: my_move(move),
  my_image(move_image)
{
}

Log_Entry::Log_Entry()
: my_move(ReversibleMove()),my_image()
{
}

Log_Entry::~Log_Entry()
{
}

static char *time_image(const time_t time)
{
   static char buf[80];
   int hrs = (int)(time / 3600);
   int mins = (int)((time - (hrs*3600))/60);
   int secs = (int)(time - (hrs*3600) - (mins*60));
   sprintf(buf,"%02d:%02d:%02d",hrs,mins,secs);
   return buf;
}

Log::Log()
{
   contents.SetSize(Initial_Log_Size);
   my_current = 0;
   *buf = '\0';
   static char log_path[MAX_PATH];
   strcpy(log_path,programPath);
   {
      // find the last part of the program pathname
      char *p = strrchr(log_path,'\\');
      if (p)
      {
         // replace it with the log file name
         strcpy(p+1,LOG_FILE_NAME);
         log_file.open(log_path,ios::out|ios::trunc);
      }
   }
#ifdef _WINDOWS
   if (!log_file.is_open() || !log_file.good())
   {
      MessageBox(NULL,"Can't open log file.  Game moves will not be saved.","",MB_OK);
   }
#endif
}
             
Log::~Log()
{
   if (log_file.good())
   {
      log_file << buf << endl;
      log_file.close();
   }
   // free the contents
   for (int i=0; i < contents.GetSize(); i++)
   {
       delete (Log_Entry*)contents[i];
   }
}

void Log::add_move( Board &board, const ReversibleMove &emove,
 const char *move_image, const Search::Statistics *stats,
 const BOOL toFile )
{
    // adding a move clears the "result" field
    setResult("");

    Log_Entry *entry = new Log_Entry(emove, move_image );
    // moves are always added at the "current" position.
    contents.SetAtGrow(my_current,entry);
    my_current++;
    // Adding a move at a given point removes any moves
    // after it in the array.
    contents.SetSize(my_current);
    char num[10];
    *buf = '\0';
    sprintf(num,"%d. ",(num_moves()-1)/2 + 1);
    strcat(buf,num);
    strcat(buf,move_image);
    int len = strlen(buf);
    char *p = buf + len;
    for (int i = 0; i < 15-len; i++)
       *p++ = ' ';
    *p = '\0';
    if (stats)
    {
        char statbuf[80];
        if (stats->num_moves == 0)
           strcpy(statbuf," (book)");
        else 
        {
           sprintf(statbuf,"\t%s    %d\t%ld\t%ld\t%5d",
              time_image(stats->elapsed_time),
              stats->plies_completed,
              stats->num_moves,
              stats->num_nodes,
              stats->value);
           if (!stats->best_line[0].IsNull())
           {
              char pred[50];
              *pred = '\0';
              Board tmp_board = board;
              tmp_board.MakeMove(emove);
              for (int i=1; i<=MAX_ANAL; i++)
              {
                 Move m = stats->best_line[i];
                 if (m.IsNull())
                    break;
                 char pred_move_image[20];
                 *pred_move_image = '\0';
                 Notation::Image(tmp_board,m,pred_move_image);
                 strcat(pred,pred_move_image);
                 strcat(pred," ");
                 tmp_board.MakeMove(ExtendedMove(tmp_board,m));
              }
              strcat(statbuf,"\t");
              strcat(statbuf,pred);
           }
        }
        if (stats->value == 9999)
        {
           // side to move has delivered checkmate
           strcat(statbuf," mate");
           if (board.Side() == White)
              setResult("1-0");
           else
              setResult("0-1");
        }
        else if (stats->state == Search::Stalemate)
        {
           strcat(statbuf," stalemate");
           setResult("1/2-1/2");
        }
        else if (stats->state == Search::Resigns)
        {
           if (board.Side() == White)
              setResult("0-1");
           else
              setResult("1-0");
           strcat(statbuf," resigns");
        }
        else if (stats->state == Search::Draw)
        {
           strcat(statbuf," draw");
           setResult("1/2-1/2");
        }
        strcat(buf,statbuf);
    }
    if (!toFile || !log_file.is_open())
       return;
    log_file << buf << endl;
    *buf = '\0';
}

const ReversibleMove &Log::move( const unsigned n ) const
{
    Log_Entry *entr = (Log_Entry*)contents.GetAt(n);
    return entr->move();
}

const ReversibleMove &Log::last_move() const
{
    ASSERT (current());
    return move(current()-1);
}

void Log::setResult(const char *result)
{
   int n = contents.GetSize();
   if (n >0)
   {
      Log_Entry *last = (Log_Entry*)contents.GetAt(n-1);
      last->setResult(result);
   }      
}

const char *Log::getResult() const
{
   int n = contents.GetSize();
   if (n >0)
   {
      Log_Entry *last = (Log_Entry*)contents.GetAt(n-1);
      return last->result();
   }
   else
      return "";
}

void Log::remove_move()
{
    if (current() == 0)
       return;
    Log_Entry *entr = (Log_Entry*)contents.GetAt(current()-1);
    delete entr;
    contents.RemoveAt(current()-1);
    --my_current;
}

BOOL Log::back_up()
{
    if (my_current >0)
    {
       --my_current;
       return TRUE;
    }
    else
       return FALSE;
}

BOOL Log::go_forward()
{
    if (my_current < contents.GetSize())
    {
       ++my_current;
       return TRUE;
    }
    else
       return FALSE;
}

void Log::reset()
{
    my_current = 0;
}

void Log::clear()
{
     write_eol();
     for (int i = 0; i < contents.GetSize(); i++)
     {
        Log_Entry *rec = (Log_Entry*)contents[i];
        delete rec;
     }
     contents.RemoveAll();
     my_current = 0;
}

void Log::write_header()
{
    if (log_file.is_open())
    {
        flush();
        static char header1[] = "Arasan version ";
        static char header2[] = "   move          time     depth\tmoves\tnodes\tvalue\tpredicted";
        log_file << header1 << Arasan_Version << " game log" << endl;
        log_file << header2 << endl;
    }
}

void Log::write(const char *s)
{
     if (log_file.is_open())
     {
        flush();
        log_file << s;
     }
}

void Log::write_eol()
{
     if (log_file.is_open())
        log_file << endl;
}

void Log::flush()
{
     if (strlen(buf) > 0 && log_file.is_open())
     {  
        log_file << buf << endl;
     }
     *buf = '\0';
}
