/* **************************************************************************
 * Tricky-Eights Dispatch Program -- te_dispatch.c
 * 
 *      This program executes the a tricky-eights server and four
 * client programs, and routes the standard input/output between the
 * programs.
 *
 *      This code is unix specific -- too bad.
 *
 * Written by:  PAJ -- University of Utah   January 14, 1998.
 * Modified by: no one yet!  :)
 *
 * Compiled as a console application using visual C++ Version 4.0
 ************************************************************************* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <windows.h>
#include <process.h>
#include <io.h>
#include <fcntl.h>


/* **************************************************************************
 * Global variables
 ************************************************************************* */

char players[] = "nesw";
char path [5][100];
char seed [100];
int pid [5] = {0, 0, 0, 0, 0};
int alive[5] = {1, 1, 1, 1, 1};
int pipe_fd [2];
int dispatch_write_end [5];
int dispatch_read_end [5];
int client_write_end [5];
int client_read_end [5];
int killed = 0;


/* **************************************************************************
 * Helper functions
 ************************************************************************* */

int player_to_int (char p)
{
  int c;

  /* Find the integer mapping to the character. */

  for (c = 0; c < 4; c++)
    if (players[c] == p)
      return c + 1;

  /* Not found, indicate error by returning -1. */

  return -1;
}


/* **************************************************************************
 * Main function
 ************************************************************************* */

int main (int argc, char ** argv)
{
  int c, d, child, done, target, allow;
  struct stat info;
  char inpipe[10], outpipe[10];
  char fromserver[500], toserver[500], errormessage[500];
  int fromsize, tosize;

  /* This executable can get run in one of two modes.  In the first, */
  /*   this program is being run from the command line to be a dispatcher. */
  /*   In the second, it is being run a second (or third or...) time in */
  /*   order to start the child processes. */

  /* If this is a spawned copy of this program, then set up the pipes */
  /*   and execute the appropriate child program. */

  /* Parameters to the spawned process:  programname "spawned" pipe filename */

  if (argc > 5)
  {
    if (strcmp (argv[1], "spawned") == 0)
    {
      dup2 (atoi (argv[2]), 0); /* Replace stdin with pipe */
      dup2 (atoi (argv[3]), 1); /* Replace stdout with pipe */
      execl (argv[4], argv[4], argv[5], NULL);  /* Execute client program. */
      exit (-1);
    }
  }

  /* Make sure there are enough parameters, copy them out. */
  
  if (argc < 6)
  {
    fprintf (stderr, "Usage:  te_dispatch server client client client client [seed]\n");
    exit (-1);
  }

  for (c = 1; c <= 5; c++)
    strcpy (path[c-1], argv[c]);
  
  if (argc >= 7)
    strcpy (seed, argv[6]);
  
  /* Make I/O pipes. */

  for (c = 0; c < 5; c++)
  {
    if (_pipe(pipe_fd, 8192, _O_BINARY) != 0)
    {
      fprintf (stderr, "Unable to create pipe pair #a%i.\n", c+1);
      exit (-1);
    }

    dispatch_read_end[c] = pipe_fd[0];
    client_write_end[c] = pipe_fd[1];
  }

  for (c = 0; c < 5; c++)
  {
    if (_pipe(pipe_fd, 8192, _O_BINARY) != 0)
    {
      fprintf (stderr, "Unable to create pipe pair #b%i.\n", c+1);
      exit (-1);
    }

    client_read_end[c] = pipe_fd[0];
    dispatch_write_end[c] = pipe_fd[1];
  }

  
  /* Fork off the processes, set up i/o pipes, exec the children. */

  /* Different from the unix version, the Win95 version spawns off */
  /*   the processes, and they set up their own pipes to stdin/out */
  /*   and do their own exec calls. */

  for (c = 0; c < 5; c++)
  {
    itoa (client_read_end[c], inpipe, 10);
    itoa (client_write_end[c], outpipe, 10);

    child = spawnl (_P_NOWAIT, argv[0], argv[0], "spawned", inpipe, 
                    outpipe, path[c], seed, NULL);

    if (child != -1)
      pid[c] = child;
  }

  /* Yield a few times to the other processes. */

  Sleep (0);
  Sleep (0);
  Sleep (0);
  Sleep (0);
  Sleep (0);
  Sleep (2000);
  
  /* Loop until the server quits, dispatching text. */
  /*   If any client quits first, error until the server quits. */

  done = 0;
  fromsize = 0;
  tosize = 0;
  target = 1;
  allow = 0;
  
  while (!done)
  {
    /* Read messages. */

    fstat (dispatch_read_end[0], &info);
    if (info.st_size > 0)
      fromsize += read (dispatch_read_end[0], fromserver+fromsize,
			500-fromsize < info.st_size ? 500-fromsize : info.st_size);
			    
    fstat (dispatch_read_end[target], &info);
    if (info.st_size > 0)
      tosize += read (dispatch_read_end[target], toserver+tosize,
		       500-tosize < info.st_size ? 500-tosize : info.st_size);

    /* Filter out extra control characters. */

    for (c = d = 0; c < fromsize; c++)
    {
      fromserver[d] = fromserver[c];
      if (fromserver[d] != '\r')
        d++;
    }
    fromsize = d;

    for (c = d = 0; c < tosize; c++)
    {
      toserver[d] = toserver[c];
      if (toserver[d] != '\r')
        d++;
    }
    tosize = d;

    /* Check for redirect command from server. */

    if (fromserver[0] == '#' && player_to_int (fromserver[2]) > 0)
    {
      target = player_to_int (fromserver[2]);

      allow = 1;
    }

    /* Echo completed server lines. */
  
    for (c = 0; c < fromsize; c++)
      if (fromserver[c] == '\n')
	      break;

    if (c++ < fromsize)
    {
      if (fromserver[0] != '#')
      {
	      write (dispatch_write_end[target], fromserver, c);
	      write (2, fromserver, c);  /* To stderr for watching. */
      }
	
      for (d = 0; c < fromsize; c++, d++)
	      fromserver[d] = fromserver[c];

      fromsize = d;

      Sleep (0);
    }
    
    /* Echo completed client lines. */
    
    for (c = 0; c < tosize; c++)
      if (toserver[c] == '\n')
	      break;

    if (c++ < tosize)
    {
      if (allow)
      {
        write (dispatch_write_end[0], toserver, c);
        write (2, toserver, c);  /* To stderr for watching. */

	      allow--;
      }

      for (d = 0; c < tosize; c++, d++)
	      toserver[d] = toserver[c];

      tosize = d;
 
      Sleep (0);
    }

    /* Error dumps. */

    if (fromsize >= 500)
    {
      sprintf (errormessage, "!!! Server overflowed output buffer.\n");
      write (dispatch_write_end[0], errormessage, strlen(errormessage));
      fprintf (stderr, "Server overflowed output buffer.\n");
    }
  
    if (tosize >= 500)
    {
      sprintf (errormessage, "!!! Player %c, program %s overflowed output buffer.\n", players[target-1], path[target]);
      write (dispatch_write_end[0], errormessage, strlen(errormessage));
      fprintf (stderr, "!!! Player %c, program %s overflowed output buffer.\n", players[target-1], path[target]);
    }
    
    /* See if we are done. */

    /* This code deleted because it won't work in Windows. */
    /*   To kill the program, press control-c several times. */

  }

  /* Won't ever get here in Windows version. */

  fprintf (stderr, "Exiting.\n");

  /* All done -- end the program without error. */

  return 0;
}


