
// posix.cpp

// includes

#include <cerrno>
#include <cstdio> // REMOVE ME?
#include <cstdlib>
#include <cstring>
#include <ctime>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>

#else // assume POSIX
#  include <sys/resource.h>
// #  include <sys/select.h>
#  include <sys/time.h>
#  include <sys/types.h>
#  include <unistd.h>
#endif

#include "posix.h"
#include "util.h"

// constants

#include "YBWCManager.h"

static const bool UseDebug = false;

// prototypes

#if !defined(_WIN32) && !defined(_WIN64)
static double duration (const struct timeval * tv);
#endif

// functions

// input_available()

bool input_available() 
{

#if defined(_WIN32) || defined(_WIN64)

  static bool init = false, is_pipe_or_socket;
  static HANDLE stdin_h;
  DWORD val, error;

  static fd_set stdin_readfds;
  static struct timeval stdin_tv;
  static int bStdinIsProbablySocket = 0;
  int    nStdinData;
  int    nSelectResult;

  // val = 0; // needed to make the compiler happy?

  // have a look at the "local" buffer first, *this time before init (no idea if it helps)*

  if(UseDebug && !init) 
  {
    printf("info string init=%d stdin->_cnt=%d\n", int(init), stdin->_cnt);
    fflush(stdout); // kh 22.09.06
  }

  if(stdin->_cnt > 0) 
    return true; // HACK: assumes FILE internals

  // input init (only done once)

  if(!init) 
  {
    init = true;

    stdin_h = GetStdHandle(STD_INPUT_HANDLE);

    if(UseDebug && (stdin_h == NULL || stdin_h == INVALID_HANDLE_VALUE)) 
    {
      error = GetLastError();
      printf("info string GetStdHandle() failed, error=%d\n", error);
      fflush(stdout); // kh 22.09.06
    }

    is_pipe_or_socket = !GetConsoleMode(stdin_h, &val); // kh 06.07.06 true if stdin supplied by launching parent is a pipe OR socket

    if(UseDebug) 
    {
      printf("info string init=%d is_pipe_or_socket=%d\n", int(init), int(is_pipe_or_socket));
      fflush(stdout); // kh 22.09.06
    }

    if(UseDebug && is_pipe_or_socket) // GetConsoleMode() failed, stdin supplied by launching parent is a pipe OR socket
    {
      error = GetLastError();
      printf("info string GetConsoleMode() failed, error=%d\n", error);
      fflush(stdout); // kh 22.09.06
    }

    if(is_pipe_or_socket) 
    {
      if(PeekNamedPipe(stdin_h, NULL, 0, NULL, &val, NULL)) // kh 06.07.06 fails if stdin supplied by launching parent is a socket
      {
        if(YBWCManager_Instance()->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_INFO_PRIO_1)
        {
          YBWCManager_Printf(YBWCManager_Instance(), "stdin supplied by launching parent is handled as a pipe\n");
        }

        bStdinIsProbablySocket = 0;
        if(UseDebug) 
        {
          printf("info string init=%d bStdinIsProbablySocket=%d\n", int(init), int(bStdinIsProbablySocket));
          fflush (stdout); // kh 22.09.06        
        }
      }
      else
      {
        if(YBWCManager_Instance()->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_INFO_PRIO_1)
        {
          YBWCManager_Printf(YBWCManager_Instance(), "stdin supplied by launching parent is handled as a socket\n");
        }

        bStdinIsProbablySocket = 1;
        if(UseDebug) 
        {
          printf("info string init=%d bStdinIsProbablySocket=%d\n", int(init), int(bStdinIsProbablySocket));
          fflush (stdout); // kh 22.09.06        
        }
      }
    }
    else
    {
      SetConsoleMode(stdin_h, val&~(ENABLE_MOUSE_INPUT|ENABLE_WINDOW_INPUT));
      FlushConsoleInputBuffer(stdin_h); // no idea if we can lose data doing this
    }
  }

  // different polling depending on input type
  // does this code work at all for pipes?

  if(is_pipe_or_socket) 
  {
    if(bStdinIsProbablySocket)
    {
      FD_ZERO(&stdin_readfds);
      FD_SET ((SOCKET)stdin_h, &stdin_readfds);

      stdin_tv.tv_sec  = 0;
      stdin_tv.tv_usec = 0;

      nSelectResult = select(FD_SETSIZE, &stdin_readfds, 0, 0, &stdin_tv);

      nStdinData = FD_ISSET(stdin_h, &stdin_readfds);

      return (nStdinData != 0);
    }
    else
    {
      if(!PeekNamedPipe(stdin_h, NULL, 0, NULL, &val, NULL)) 
      {
        if(UseDebug) // PeekNamedPipe() failed, everybody assumes EOF then
        {
          error = GetLastError();
          printf("info string PeekNamedPipe() failed, error=%d\n", error);
          fflush (stdout); // kh 22.09.06        
        }

// kh 06.07.06 original defensive (but there is not necessarily at least 1 character available...)
// may cause "out of sync" problems in the uci protocol
        return true; // HACK: assumes EOF on failure
      }

      if(UseDebug && val < 0) 
      {
        printf("info string PeekNamedPipe(): val=%d\n", val);
        fflush (stdout); // kh 22.09.06
      }

      return val > 0; // != 0???
    }
  } 
  else 
  {
    GetNumberOfConsoleInputEvents(stdin_h, &val);
    return val > 1; // no idea why 1
  }

  return false;

#else // assume POSIX

  int val;
  fd_set set[1];
  struct timeval time_val[1];

  FD_ZERO(set);
  FD_SET(STDIN_FILENO,set);

  time_val->tv_sec = 0;
  time_val->tv_usec = 0;

  val = select(STDIN_FILENO + 1, set, NULL, NULL, time_val);
  if (val == -1 && errno != EINTR) 
  {
    my_fatal("input_available(): select(): %s\n", strerror(errno));
  }

  return val > 0;

#endif
}

// now_real()

double now_real() 
{

#if defined(_WIN32) || defined(_WIN64)

   return double(GetTickCount()) / 1000.0;

#else // assume POSIX

  struct timeval tv[1];
  struct timezone tz[1];

  tz->tz_minuteswest = 0;
  tz->tz_dsttime = 0; // DST_NONE not declared in linux

  if(gettimeofday(tv,tz) == -1) // tz needed at all?
  {
     my_fatal("now_real(): gettimeofday(): %s\n", strerror(errno));
  }

  return duration(tv);

#endif
}

// now_cpu()

double now_cpu() 
{

#if defined(_WIN32) || defined(_WIN64)

  return double(clock()) / double(CLOCKS_PER_SEC); // OK if CLOCKS_PER_SEC is small enough

#else // assume POSIX

  struct rusage ru[1];

  if(getrusage(RUSAGE_SELF,ru) == -1) 
  {
    my_fatal("now_cpu(): getrusage(): %s\n", strerror(errno));
  }

  return duration(&ru->ru_utime);

#endif
}

// duration()

#if !defined(_WIN32) && !defined(_WIN64)

static double duration(const struct timeval * tv) 
{
  ASSERT(tv != NULL);

  return double(tv->tv_sec) + double(tv->tv_usec) * 1E-6;
}

#endif

// end of posix.cpp
