
/* --------------------------------------------------------------------
   File: libgourm.cc
   Author: Doug Beeferman
   Date: 2/23/97
   Description: exports "print_recipe" function for generating recipes
   -------------------------------------------------------------------- */


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define MAX_FOODS    512
#define MAX_CHARS    1024
#define ALLOC_BLOCK_SIZE 50000

static int cFoods=0;

#define NAMES 0
#define UNITS 1
#define ADJECTIVES 2
#define ACTIONS 3

#define ADVERBS 0
#define STARTING_CONTAINERS 1
#define LASTLINES 2
#define TITLES 3

static char *rgszFoodInfo[MAX_FOODS][4];
static int rgcFoodInfoCount[MAX_FOODS][4];

static char *rgszGlobalInfo[10];
static int rgcGlobalInfoCount[10];

static int rgfFoodsUsed[MAX_FOODS];
static char szBuffer[MAX_CHARS];

static char rgszVars[128][100];
static char rgszIng[MAX_FOODS][100];

static int count_fields(char *szStr);
static char *return_field(char *szStr, int iIndex);
static int bernoulli_trial(int iSuccessPercent, int iMaxVal);
static char *normalize_word(char *s, int fPlural, int fAtomic);
static void read_cookbook(char *szFilename);
static void translate_string(char *s);




void print_recipe( /* char szString */ )
{
  int iSeed1=0,iSeed2=0, i=1, iLastLine, iTitle, iTitleY=0;
  int cIngredients, iTemp, cServed, cFoodsUsed=0;
  char *pch = NULL; /* szString */;
  char szIngDesc[MAX_CHARS];
  char szAdj[MAX_CHARS], szUnit[MAX_CHARS], szIng[MAX_CHARS];

  /* Load cookbook */
  read_cookbook("cookbook");

#if 0
  /* Use string to initialize random number generator. */
  while (*pch++) 
    {
      iSeed1 += i * (unsigned char) *pch;
      iSeed2 += (unsigned char) *pch * (unsigned char) *pch;
      i++;
    }
  srandom(iSeed1);
#endif

  srandom(time(NULL));

  /* 1. Choose number of ingredients to use */
  cIngredients = 2 + bernoulli_trial(80, 20);

  /* 2. Choose ingredients to use */
  for (i=0; i<cFoods; i++) 
    {
      if (i<cIngredients) rgfFoodsUsed[i] = 1;
      else rgfFoodsUsed[i] = 0;
    }
  for (i=0; i<cFoods; i++)
    {
      int iIndex = random()%cFoods;
      iTemp = rgfFoodsUsed[i];
      rgfFoodsUsed[i] = rgfFoodsUsed[iIndex];
      rgfFoodsUsed[iIndex] = iTemp;
      if (rgfFoodsUsed[i]) iTitleY = i;
    }
  

  /* 3. Choose number to serve */
  cServed = bernoulli_trial(80, 20) + 1;

  /* 4. Choose, generate title */
  iTitle = random() % rgcGlobalInfoCount[TITLES];
  for (i=cFoods-1; i>=0; i--)
    if ((rgfFoodsUsed[i] == 1)&&(random()%2)) iTitleY = i;
  strcpy(rgszVars['Y'], normalize_word(return_field(rgszFoodInfo
						    [iTitleY][NAMES], 
						    0),
				       0, 0));
  sprintf(rgszVars['D'], "%d", 25 + (random()%100)*10);
  sprintf(rgszVars['T'], "%d", 5 + (random()%115));
  strcpy(szIngDesc, return_field(rgszGlobalInfo[TITLES], iTitle));
  translate_string(szIngDesc);
  szIngDesc[0] = toupper(szIngDesc[0]);
  fprintf(stdout, "\n\n\t\t%s\n", szIngDesc);


  /* 5. For each selected ingredient, generate ingredient list entry */
  fprintf(stdout, "\nIngredients:\n\n");
  for (i=0; i<cFoods; i++)
    if (rgfFoodsUsed[i] == 1)
      {
	/* Ingredient */
	int iIngred = random() % (rgcFoodInfoCount[i][NAMES]);
	/* Quantity */
	int iQuant = bernoulli_trial(50,20) + 1;
	/* Unit */
	int iUnit = random() % (rgcFoodInfoCount[i][UNITS]);
	/* Adjective */
	int iAdj = random() % (rgcFoodInfoCount[i][ADJECTIVES]);

	int fAtomic = (!strcmp(return_field(rgszFoodInfo[i][UNITS], iUnit),
			       "<atomic>"));

	/* Normalize words */
	strcpy(szIng, normalize_word(return_field(rgszFoodInfo[i][NAMES],
						  iIngred),
				     ((!fAtomic) || (iQuant>1)),
				     fAtomic));
	strcpy(rgszIng[i], szIng);
	strcpy(szUnit, normalize_word(return_field(rgszFoodInfo[i][UNITS],
						   iUnit), 
				      (iQuant>1),
				      fAtomic));
	if (!strcmp(return_field(rgszFoodInfo[i][ADJECTIVES], iAdj),
		    "<none>"))
	  strcpy(szAdj, "");
	else
	  {
	    strcpy(szAdj, return_field(rgszFoodInfo[i][ADJECTIVES], iAdj));
	    strcat(szAdj, " ");
	  }

	if (fAtomic) 
	  sprintf(szIngDesc, "%d %s%s", iQuant, szAdj, szIng);
	else
	  sprintf(szIngDesc, "%d %s of %s%s", iQuant, szUnit, szAdj, szIng);
	translate_string(szIngDesc);
 
	fprintf(stdout, "\t* %s\n", szIngDesc);
	cFoodsUsed++;
      }

  srandom(iSeed2);

  fprintf(stdout, "\nPreparation:\n\n");
  /* 6. For each selected ingredient, generate action */
  cFoodsUsed = 0;
  strcpy(rgszVars['C'], return_field(rgszGlobalInfo[STARTING_CONTAINERS],
				     random()%
				     rgcGlobalInfoCount[STARTING_CONTAINERS]));
  strcpy(rgszVars['Y'], rgszVars['C']);
  
  for (i=0; i<cFoods; i++)
    if (rgfFoodsUsed[i] == 1)
      {

	int iAction = random() % (rgcFoodInfoCount[i][ACTIONS]);
	strcpy(szIngDesc, return_field(rgszFoodInfo[i][ACTIONS], iAction));


	/* Set variables */
	strcpy(rgszVars['X'], rgszIng[i]);
	sprintf(rgszVars['D'], "%d", 25 + (random()%100)*10);
	sprintf(rgszVars['T'], "%d", 5 + (random()%115));

	translate_string(szIngDesc);

	if (random()%2) strcpy(rgszVars['Y'], rgszIng[i]);
	
	fprintf(stdout, "\t%d. %s\n", cFoodsUsed+1, szIngDesc);
	cFoodsUsed++;
      }

  /* 7. Generate last line and "serves". */
  iLastLine = random() % rgcGlobalInfoCount[LASTLINES];
  strcpy(szIngDesc, return_field(rgszGlobalInfo[LASTLINES], iLastLine));
  translate_string(szIngDesc);

  fprintf(stdout, "\t%d. %s", cFoodsUsed+1, szIngDesc);
  fprintf(stdout, "\n\nServes %d.\n\n", cServed);
}


static char *normalize_word(char *s, int fPlural, int fAtomic)
{
  char szTemp[MAX_CHARS], *szSing, *szPlur;
  int iLen;
  strcpy(szTemp, s);
  iLen = strlen(szTemp);

  if (szTemp[iLen-1]=='*') 
    {
      s[iLen-1]='\0';
      if (!fAtomic) fPlural = 0;
    }

  szSing = strtok(szTemp, "+");
  szPlur = strtok(NULL, "+");

  if (fPlural == 0) return szSing;
  else if (szPlur == NULL)
    {
      strcat(szSing, "s");
      return szSing;
    }
  else return szPlur;
}


static void read_cookbook(char *szFilename)
{
  FILE *fs;
  char szLine[MAX_CHARS], *tok, *pLine;
  int iState=0;

  fs = fopen(szFilename, "r");
  assert(fs);

  cFoods = 0;
  while (fgets(szLine, MAX_CHARS, fs) != NULL)
    {
      int i=0;
      pLine = szLine;
      if ((szLine[0] == '\n') || (szLine[0] == '#')) continue;
      if (szLine[0] == '!') 
	{
	  tok = strtok(szLine, " \n\t");
	  pLine += strlen(tok) + 1;

	  if (!strcmp(tok, "!ADVERBS"))
	    {
	      rgszGlobalInfo[ADVERBS] = strdup(pLine);
	      rgcGlobalInfoCount[ADVERBS] = count_fields(pLine);
	    }
	  else if (!strcmp(tok, "!STARTING_CONTAINERS"))
	    {
	      rgszGlobalInfo[STARTING_CONTAINERS] = strdup(pLine);
	      rgcGlobalInfoCount[STARTING_CONTAINERS] = count_fields(pLine);
	    }
	  else if (!strcmp(tok, "!LASTLINES"))
	    {
	      rgszGlobalInfo[LASTLINES] = strdup(pLine);
	      rgcGlobalInfoCount[LASTLINES] = count_fields(pLine);
	    }
	  else if (!strcmp(tok, "!TITLES"))
	    {
	      rgszGlobalInfo[TITLES] = strdup(pLine);
	      rgcGlobalInfoCount[TITLES] = count_fields(pLine);
	    }
	  continue;
	}

      rgszFoodInfo[cFoods][iState] = strdup(szLine);
      rgcFoodInfoCount[cFoods][iState] = count_fields(szLine);
      if (iState < 3) iState++; else {cFoods++; iState = 0;}
    }
}


static void translate_string(char *s)
{
  char szTemp[MAX_CHARS], *pSrc=szTemp, *pDest=s;
  int i;
  strcpy(szTemp, s);

  while(*pSrc)
    {
      if (*pSrc == '_') *pDest = ' ';
      else if (*pSrc == '$')
	{
	  pSrc++;
	  *pDest = '\0';
	  translate_string(rgszVars[*pSrc]);
	  strcat(pDest, rgszVars[*pSrc]);
	  pDest += strlen(rgszVars[*pSrc])-1;
	}
      else *pDest = *pSrc;
      pSrc++;
      pDest++;
    }
  *pDest = '\0';
}

static int count_fields(char *szStr)
{
  char *pch = szStr;
  int i=0;
  while (strtok(pch, " \n\t")) 
    {
      pch = NULL;
      i++;
    }
  return i;
}

static char *return_field(char *szStr, int iIndex)
{
  char szCopy[MAX_CHARS];
  char *pch = szCopy, *pstr;
  int i=0;
  strcpy(szCopy, szStr);

  while ((pstr = strtok(pch, " \n\t")) != NULL)
    {
      if (i==iIndex) return pstr;
      pch = NULL;
      i++;
    }
  return NULL;
}

static int bernoulli_trial(int iSuccessPercent, int iMaxVal)
{
  int i=0;

  while (i<iMaxVal)
    {
      if ((random()%100) < iSuccessPercent) i++;
      else return i;
    }
  return iMaxVal;
}


