/*  animals - guessing game	Authors: Terrence W. Holm & Edwin L. Froese */

#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <minix/minlib.h>
#include <stdio.h>

#define  ANIMALS	"/usr/lib/animals"
#define  DEFAULT_ANIMAL	"beaver"
#define  MAX_NODES	999	/* Enough for 500 animals  */
#define  MAX_LINE	90

_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(void Read_Animals, (char *animal_file));
_PROTOTYPE(void Write_Animals, (char *animal_file));
_PROTOTYPE(int Ask, (char *question));
_PROTOTYPE(char *Get_Animal, (void));
_PROTOTYPE(char *Get_Question, (void));
_PROTOTYPE(char *A_or_An, (char *word));
_PROTOTYPE(char *Alloc, (int size));
_PROTOTYPE(void Abort, (int dummy));
_PROTOTYPE(void Error, (char *message));

struct node {
  int question;
  char *text;
  int yes;
  int no;
} animals[MAX_NODES];

int count = 0;
struct termios old_tty_mode;


int main(argc, argv)
int argc;
char *argv[];
{
  char *animal_file = ANIMALS;

  if (argc > 2) {
	fprintf(stderr, "Usage:  %s  [ data_base ]\n", argv[0]);
	exit(1);
  }
  if (argc == 2) animal_file = argv[1];

  tcgetattr(0, &old_tty_mode);

  signal(SIGINT, Abort);
  signal(SIGQUIT, Abort);

  if (access(animal_file, R_OK) == 0)
	Read_Animals(animal_file);
  else {
	animals[0].question = 0;
	animals[0].text = DEFAULT_ANIMAL;
	count = 1;
  }

  while (Ask("\nAre you thinking of an animal?")) {
	int i = 0;

	while (1) {
		if (animals[i].question) {
			if (Ask(animals[i].text))
				i = animals[i].yes;
			else
				i = animals[i].no;
		} else {
			printf("Were you thinking of %s %s",
			 A_or_An(animals[i].text), animals[i].text);

			if (Ask("?")) printf("I knew it!\n");

			else {
				/* Insert a new question and animal name  */

				if (count + 2 > MAX_NODES)
					Error("Too many animal names");

				animals[count].question = 0;
				animals[count].text = animals[i].text;
				++count;

				animals[count].question = 0;
				printf("What animal were you thinking of? ");
				animals[count].text = Get_Animal();
				++count;

				animals[i].question = 1;
				printf("What question would distinguish %s %s from\n%s %s? ",
				       A_or_An(animals[count - 2].text), animals[count - 2].text,
				       A_or_An(animals[count - 1].text), animals[count - 1].text);

				animals[i].text = Get_Question();

				printf("For %s %s, the answer would be",
				       A_or_An(animals[count - 1].text), animals[count - 1].text);

				if (Ask("?")) {
					animals[i].yes = count - 1;
					animals[i].no = count - 2;
				} else {
					animals[i].yes = count - 2;
					animals[i].no = count - 1;
				}
			}

			break;
		}
	}			/* End while ( 1 )  */

  }


  tcsetattr(0, TCSANOW, &old_tty_mode);

  printf("\nThank you for playing \"animals\".\n");
  printf("The animal data base is now being updated.\n");

  Write_Animals(animal_file);

  sleep(1);
  printf("\nBye.\n");

  return(0);
}


/*  Reading and writing the animal data base */


void Read_Animals(animal_file)
char *animal_file;

{
  FILE *f;
  char buffer[MAX_LINE];

  if ((f = fopen(animal_file, "r")) == NULL)
	Error("Can not open animal data base");

  while (fgets(buffer, MAX_LINE, f) != NULL) {
	int string_length;
	char *string;

	buffer[strlen(buffer) - 1] = '\0';

	swab(buffer, buffer, strlen(buffer));

	if (buffer[0] == 'q') {
		char *end = strchr(buffer, '?');
		string_length = end - buffer;
		animals[count].question = 1;
		sscanf(end+1, "%d:%d",&animals[count].yes,&animals[count].no);
	} else {
		animals[count].question = 0;
		string_length = strlen(buffer) - 1;
	}

	string = Alloc(string_length + 1);

	string[0] = '\0';
	strncat(string, buffer + 1, (size_t)string_length);

	animals[count].text = string;

	count++;
  }

  fclose(f);
}


void Write_Animals(animal_file)
char *animal_file;

{
  FILE *f;
  int i;
  char buffer[MAX_LINE];

  if ((f = fopen(animal_file, "w")) == NULL)
	Error("Can not write animal data base");

  for (i = 0; i < count; ++i) {
	if (animals[i].question) sprintf(buffer, "q%s%d:%d", animals[i].text,
			animals[i].yes, animals[i].no);
	else
		sprintf(buffer, "a%s", animals[i].text);

	/* Make the data base a bit difficult to read  */
	swab(buffer, buffer, strlen(buffer));
	fprintf(f, "%s\n", buffer);
  }

  fclose(f);
  chmod(animal_file, 0666);
}


/*  Reading data from the user */
int Ask(question)
char *question;
{
  struct termios new_tty_mode;
  int response;

  new_tty_mode = old_tty_mode;
  new_tty_mode.c_lflag &= ~ICANON;
  tcsetattr(0, TCSANOW, &new_tty_mode);

  printf("%s ", question);

  while ((response = getchar()) != 'y' && response != 'n')
	printf("\n%s [yn]?", question);
  putchar('\n');
  tcsetattr(0, TCSANOW, &old_tty_mode);
  if (response == 'y')
	return(1);
  else
	return(0);
}


char *Get_Animal()
{
  char s[MAX_LINE];
  char *text;
  int text_length;

  fgets(s, MAX_LINE, stdin);
  text_length = strlen(s);
  text = Alloc(text_length);
  text[0] = '\0';
  strncat(text, s, (size_t)(text_length - 1));
  return(text);
}


char *Get_Question()
{
  char s[MAX_LINE];
  char *end;
  char *text;

  fgets(s, MAX_LINE, stdin);

  /* Capitalize the first letter  */
  if (islower(s[0])) s[0] = toupper(s[0]);

  /* Make sure the question ends with a '?'  */
  if ((end = strchr(s, '?')) == NULL)
	s[strlen(s) - 1] = '?';
  else
	end[1] = '\0';

  text = Alloc(strlen(s) + 1);
  strcpy(text, s);
  return(text);
}


/*  Utility routines */
char *A_or_An(word)
char *word;
{
  if (strchr("aeiouAEIOU", word[0]) == NULL)
	return("a");
  else
	return("an");
}


char *Alloc(size)
int size;
{
  char *memory;

  if ((memory = (char *)malloc((size_t)size)) == NULL)
	Error("No room in memory for all the animals");

  return(memory);
}


void Abort(dummy)
int dummy; /* to keep the compiler happy */
{
  tcsetattr(0, TCSANOW, &old_tty_mode);

  printf("\nThank you for playing \"animals\".\n");
  printf("Since you aborted, the animal data base will not be updated.\n");
  sleep(1);
  printf("\nBye.\n");
  exit(1);
}


void Error(message)
char *message;
{
  tcsetattr(0, TCSANOW, &old_tty_mode);
  fprintf(stderr, "Error: %s\n", message);
  exit(1);
}
