/****************************************************************************************************************
 *
 *  Copyright (c) 1992 by Antoine Dumesnil de Maricourt. All rights reserved.
 *
 *  This program is distributed in the hope that it will be useful.
 *  Use and copying of this software and preparation of derivative works
 *  based upon this software are permitted, so long as the following
 *  conditions are met:
 *       o credit to the authors is acknowledged following current
 *         academic behaviour
 *       o no fees or compensation are charged for use, copies, or
 *         access to this software
 *       o this copyright notice is included intact.
 *  This software is made available AS IS, and no warranty is made about 
 *  the software or its performance. 
 * 
 *  Bug descriptions, use reports, comments or suggestions are welcome.
 *  Send them to    dumesnil@etca.fr   or to:
 *       
 *       Antoine de Maricourt
 *       ETCA CREA-SP
 *       16 bis, avenue Prieur de la Cote d'Or
 *       94114 Arcueil Cedex
 *       France
 */

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>

/*
 *   RESIGN 
 *   TERMINAISONS ...
 */

#include <X11/Intrinsic.h>

#include "xgoban.h"

static FILE *to_black;
static FILE *from_black;
static FILE *to_white;
static FILE *from_white;

static int   white_pid = -1;
static int   black_pid = -1;

/*******************************************************************************************************
 */

void signalMove (node)
SG_NodePtr node;
{
  int    x       = node->x;
  int    y       = node->y;
  Player player  = (node->player == SG_BlackStone) ? black_player : white_player;
  FILE  *to_file = (node->player == SG_BlackStone) ? to_black     : to_white;

  switch (player) {
  case Wally :
    if (x == 0 && y == 0)
      (void) fprintf (to_file, "pass\n");
    else
      (void) fprintf (to_file, "%c%d\n", 'a' + ((x > 8) ? x : x - 1), 20 - y);

    (void) fflush  (to_file);
    break;

  case Gnugo :
    if (x == 0 && y == 0)
      (void) fprintf (to_file, "pass\n");
    else
      (void) fprintf (to_file, "%c%d\n", 'A' + ((x > 8) ? x : x - 1), y);

    (void) fflush  (to_file);
    break;

  case Human :
    if (w_second_toplevel != NULL && beep >= -100)
      if (second_player == node->player)
	XBell (XtDisplay (w_second_goban), beep);
      else
	XBell (XtDisplay (w_goban)       , beep);
    break;
  }
}

static void receive_data (file)
FILE *file;
{
  char       buffer[256];
  char      *pat;
  int        x = -1;
  int        y = -1;
  SG_NodePtr node;

  if (current_mode == PlayMode) {
    if (fgets (buffer, 256, file) == NULL)
      return;

    switch (current_node->player == SG_BlackStone ? black_player : white_player) {
    case Wally :
      if ((pat = strstr (buffer, "Wally moves to")) != NULL) {
	sscanf (pat + 16, "%d", &y);
	x = *(pat + 15) - 'a' + 1; 
	x = x > 8 ? x - 1 : x;
	y = 20 - y;
      }
      else if ((pat = strstr (buffer, "Wally passes")) != NULL) {
	x = 0;
	y = 0;
      }
      break;
      
    case Gnugo :
      if ((pat = strstr (buffer, "my move: ")) != NULL) {
	x = *(pat + 9) - 'A' + 1; 
	x = x > 8 ? x - 1 : x;
	sscanf (pat + 10, "%d", &y);
      }
      break;
    }
	
    if (x != -1 && y != -1) {

      HideTmpProperty (current_node);
	
      node         =  SG_MakeNode (current_node, SG_Move);
      node->x      = x;
      node->y      = y;
      node->color  = current_node->player;
      
      Play (node);

      modified     = True;
      current_node = node;

      ShowProperty   (current_node);
      RedisplayBoard ();

      if (x == 0 && y == 0)
	switch (node->player) {
	case SG_BlackStone : PopupDialog (w_goban, NULL, BlackPassDialog); break;
	case SG_WhiteStone : PopupDialog (w_goban, NULL, WhitePassDialog); break;
	}
	
      signalMove (node);
      
      if (beep >= -100)
	XBell (XtDisplay (w_goban), beep);
    }
  }
}

void startGame (app_context)
XtAppContext app_context;
{
  int   from_pipe [2];
  int   to_pipe[2];
  char *argv[10];
  int   argc = 0;

  /*
   *   Start black player program
   */

  if (black_player != Human) {
    pipe (from_pipe);
    pipe (to_pipe  );

    if ((black_pid = fork ()) == 0) {
      fcntl(from_pipe[0], F_SETFL, O_NDELAY);

      close (0); dup (to_pipe  [0]);
      close (1); dup (from_pipe[1]);
      
      close (to_pipe  [0]);
      close (to_pipe  [1]);
      close (from_pipe[0]);
      close (from_pipe[1]);
      
      switch (black_player) {
      case Wally :
	argv[argc++] = "wally";
	argv[argc++] = "\0\0\0"; sprintf (argv[argc - 1], "%d", board_size);
	if (handicap == 0) argv[argc++] = "-even";
	argv[argc++] = NULL;
	
	if (execvp ("wally", argv) == -1)
	  { fprintf (stderr, "XGoban : can't exec wally\n"); stopGame (); exit (1); }
	break;

      case Gnugo :
	if (execlp ("gnugo", "gnugo", (char *) NULL) == -1)
	  { fprintf (stderr, "XGoban : can't exec gnugo\n"); stopGame (); exit (1); }
	break;
       
      case Human : break;
      }
    }

    if (black_pid == -1) 
      { fprintf (stderr, "XGoban : fork failed \n"); stopGame (); exit (1); }
    
    /*
     *    XGoban : install pipe with black player
     */

    else {
      close (to_pipe  [0]);
      close (from_pipe[1]);
      
      from_black = fdopen (from_pipe[0], "r");
      to_black   = fdopen (to_pipe  [1], "w");
    
      setbuf (from_black, (char *) NULL);
      setbuf (to_black  , (char *) NULL);
      
      XtAppAddInput (app_context, fileno (from_black), (XtPointer) XtInputReadMask, 
		     (XtInputCallbackProc) receive_data, (XtPointer) from_black);
      
      switch (black_player) {
      case Wally : break;
      case Gnugo :
	(void) fprintf (to_black, "\n%d\nw\n", handicap);
	(void) fflush  (to_black);
	break;
      case Human : break;
      }
    }
  }

  /*
   *   Start white player program
   */

  if (white_player != Human) {
    pipe (from_pipe);
    pipe (to_pipe  );

    if ((white_pid = fork ()) == 0) {
      fcntl(from_pipe[0], F_SETFL, O_NDELAY);

      close (0); dup (to_pipe  [0]);
      close (1); dup (from_pipe[1]);
      
      close (to_pipe  [0]);
      close (to_pipe  [1]);
      close (from_pipe[0]);
      close (from_pipe[1]);
      
      switch (white_player) {
      case Gnugo :
	if (execlp ("gnugo", "gnugo", (char *) NULL) == -1)
	  { fprintf (stderr, "XGoban : can't exec wally\n"); stopGame (); exit (1); }
	break;
      case Wally : break;
      case Human : break;
      }
    }

    if (white_pid == -1) 
      { fprintf (stderr, "XGoban : fork failed \n"); stopGame (); exit (1); }

    /*
     *    XGoban : install pipe with white player
     */

    close (to_pipe  [0]);
    close (from_pipe[1]);
      
    from_white = fdopen (from_pipe[0], "r");
    to_white   = fdopen (to_pipe  [1], "w");
    
    setbuf (from_white, (char *) NULL);
    setbuf (to_white  , (char *) NULL);
    
    XtAppAddInput (app_context, fileno (from_white), (XtPointer) XtInputReadMask,
		   (XtInputCallbackProc) receive_data, (XtPointer) from_white);
    
    switch (white_player) {
    case Wally : break;
    case Human : break;
    case Gnugo :
      (void) fprintf (to_white, "\n%d\nb\n", handicap);
      (void) fflush  (to_white);
      break;
    }
  }
}

void stopGame ()
{
  if (black_pid != -1) (void) kill (black_pid, SIGKILL);
  if (white_pid != -1) (void) kill (white_pid, SIGKILL);

  current_mode = EditMode;
}
