

/* Basic stuff for use with the alchemy code. Clearly some of this stuff
 * could go into server/alchemy, but I left it here just in case it proves
 * more generally useful. 
 *
 * Nov 1995 - file created by b.t. thomas@astro.psu.edu 
 */


/* Our definition of 'formula' is any product of an alchemical process.
 * Ingredients are just comma delimited list of archetype (or object) 
 * names. 
 */

/* Example 'formula' entry in libdir/formulae:
 * 	Object transparency
 *	chance 10
 *	ingred dust of beholdereye,gem
 *	arch potion_generic
 */

#include <global.h>
#include <object.h>
#include <ctype.h>

static recipelist *formulalist;

static recipelist *init_recipelist() {
  recipelist *tl = (recipelist *) malloc(sizeof(recipelist));
  if(tl==NULL)
    fatal(OUT_OF_MEMORY);
  tl->total_chance=0;
  tl->number=0;
  tl->items=NULL;
  tl->next=NULL;
  return tl;
}

recipe *get_empty_formula() {
  recipe *t = (recipe *) malloc(sizeof(recipe));
  if(t==NULL)
    fatal(OUT_OF_MEMORY);
  t->chance = 0;
  t->index = 0;
  t->transmute = 0;
  t->yield=0;
  t->title = NULL;
  t->arch_name = NULL;
  t->ingred = NULL;
  t->next=NULL;
  return t;
}
 
/* get_formulalist() - returns pointer to the formula list */

recipelist * get_formulalist ( int i ) {
  recipelist *fl=formulalist;
  int number=i;

  while(fl && number>1) { 
         if(!(fl=fl->next)) break;
         number--;
     }   
  return fl;
}

/*
 * init_formulae() - Builds up the lists of formula from the file in 
 * the libdir. -b.t. 
 */
 
void init_formulae() {
  static int has_been_done=0;
  FILE *fp;
  char filename[MAX_BUF], buf[MAX_BUF], *cp, *next;
  recipe *formula=NULL;
  recipelist *fl=init_recipelist();
  linked_char *tmp;
  int value, comp;

  if(!formulalist) formulalist = fl;
 
  if (has_been_done) return;
  else has_been_done = 1;
 
  sprintf(filename, "%s/formulae", settings.libdir);
  LOG(llevDebug, "Reading alchemical formulae from %s...",filename);
  if ((fp = open_and_uncompress(filename, 0, &comp)) == NULL) {
    LOG(llevError, "Can't open %s.\n", filename);
    return;
  }
 
  while (fgets(buf, MAX_BUF, fp)!=NULL) {
    if (*buf=='#') continue;
    if((cp=strchr(buf,'\n'))!=NULL)
      *cp='\0';
    cp=buf;
    while(*cp==' ') /* Skip blanks */
      cp++;
 
    if (!strncmp(cp, "Object", 6)) {
      formula=get_empty_formula();
      formula->title = add_string(strchr(cp,' ') + 1);
    }
    else if (sscanf(cp, "trans %d", &value)) {
        formula->transmute = (uint16) value;
    }
    else if (sscanf(cp, "yield %d", &value)) {
	formula->yield = (uint16) value;
    }
    else if (sscanf(cp, "chance %d", &value)) {
        formula->chance = (uint16) value;
    }
    else if (!strncmp(cp, "ingred",6)) {
      int numb_ingred = 1;
      cp = strchr(cp,' ') + 1;
      do {
        if ((next=strchr(cp,','))!=NULL)
          {*(next++) = '\0'; numb_ingred++;}
        tmp = (linked_char*) malloc(sizeof(linked_char));
        tmp->name = add_string(cp);
        tmp->next = formula->ingred;
        formula->ingred = tmp;
	/* each ingredient's ASCII value is coadded. Later on this 
 	 * value will be used allow us to search the formula lists 
	 * quickly for the right recipe. 
	 */ 
	formula->index += strtoint(cp);
      } while ((cp=next)!=NULL);
      /* now find the correct (# of ingred ordered) formulalist */
      fl=formulalist;
      while(numb_ingred!=1) {
	if(!fl->next)
	  fl->next = init_recipelist();
	fl = fl->next;
        numb_ingred--;
      }
      fl->total_chance += formula->chance;
      fl->number++;
      formula->next = fl->items; 
      fl->items = formula;
    } else if (!strncmp(cp, "arch",4)) { 
        formula->arch_name = add_string(strchr(cp,' ')+1);
        (void) check_recipe(formula);
    } else
        LOG(llevError,"Unknown input in file %s: %s\n", filename, buf);
  }
  LOG(llevDebug,"done.\n");
  close_and_delete(fp, comp);
  /* Lastly, lets check for problems in formula we got */
  check_formulae();
}

/* check_formulae()- since we are doing a squential search on the
 * formulae lists now, we have to be carefull that we dont have 2
 * formula with the exact same index value. Under the new nbatches
 * code, it is possible to have multiples of ingredients in a cauldron
 * which could result in an index formula mismatch. We *don't* check for
 * that possibility here. -b.t.
 */
void check_formulae( void ) {
  recipelist *fl;
  recipe *check, *formula;
  int numb = 1;

  LOG(llevDebug,"Checking formulae lists...\n");

  for(fl=formulalist; fl!=NULL; fl = fl->next) { 
    for (formula=fl->items; formula!=NULL; formula=formula->next) 
      for (check=formula->next; check!=NULL; check=check->next) 
        if(check->index==formula->index) { 
          LOG(llevError," ERROR: On %d ingred list: ", numb);
          LOG(llevError, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n",
            formula->arch_name,formula->title,check->arch_name,check->title,formula->index);
        }
    numb++;
  }

  LOG(llevDebug,"done.\n");

}

/* Borrowed (again) from the artifacts code for this  */

void dump_alchemy( void ) {
  recipelist *fl=formulalist;
  recipe *formula=NULL;
  linked_char *next;
  int num_ingred=1;

  fprintf(logfile, "\n");
  while(fl) { 
    fprintf(logfile, "\n Formulae with %d ingredient%s  %d Formulae with total_chance=%d\n", 
	num_ingred, num_ingred>1?"s.":".",fl->number,fl->total_chance);
    for (formula=fl->items; formula!=NULL; formula=formula->next) {
      artifact *art=NULL;
      archetype *at=NULL;
      char buf[MAX_BUF], *string;
      
      string=strtok(formula->arch_name,",");
      while(string) { 
	if((at=find_archetype(string))!=NULL) { 
          art = locate_recipe_artifact(formula);
          if (!art && strcmp(formula->title,"NONE")) 
		LOG(llevError,"Formula %s has no artifact\n",formula->title);
	  else {
	     if(strcmp(formula->title,"NONE"))
                sprintf(buf,"%s of %s",string,formula->title);
	     else
                sprintf(buf,"%s",string);
             fprintf(logfile,"%-30s(%d) bookchance %3d  ",buf,formula->index,
		formula->chance);
             fprintf(logfile,"\n");
             if (formula->ingred !=NULL) {
		int nval=0,tval=0;
                fprintf(logfile,"\tIngred: ");
                for (next=formula->ingred; next!=NULL; next=next->next) { 
		  if(nval!=0) fprintf(logfile,","); 
                  fprintf(logfile,"%s(%d)",next->name,(nval=strtoint(next->name)));
		  tval += nval;
		}
                fprintf(logfile,"\n");
		if(tval!=formula->index) fprintf(logfile, "WARNING:ingredient list and formula values not equal.\n");
             }  
	  }
	} else 
	   LOG(llevError,"Can't find archetype:%s for formula %s\n", string,
		formula->title); 
	string = strtok(NULL,",");
      }
    }  
  fprintf(logfile,"\n");
  fl = fl->next;
  num_ingred++;
  }
}

void dump_prod_val_vs_cost( void ) {



}

recipe *get_first_recipe() {
  recipelist *fl=formulalist;
  recipe *formula=fl->items;

  return formula;
}

char * ingred_name (char *name) {
  char *cp=name;
 
  if(atoi(cp)) cp = strchr(cp,' ') + 1;
  return cp;
}

/* strtoint() - we use this to convert buf into an integer
 * equal to the coadded sum of the (lowercase) character 
 * ASCII values in buf (times prepended integers).
 */

int strtoint (char *buf) {
  char *cp = ingred_name(buf);
  int val=0, len=strlen(cp), mult=numb_ingred(buf);

  while (len) { 
    val += tolower(*cp);
    cp++; len--;
  }
  return val*mult;
}

/* check_recipe() - makes sure we actually have the requested artifact 
 * and archetype. */

int check_recipe(recipe *rp) {
  archetype *at;

    if((at=find_archetype(rp->arch_name))!=NULL) { 
          artifact *art=locate_recipe_artifact(rp);
          if (!art && strcmp(rp->title,"NONE")) {
		LOG(llevError,"\n WARNING: Formula %s of %s has no artifact.\n",
                rp->arch_name,rp->title); 
		return 0;
	  }
    } else { 
           LOG(llevError,"\n WARNING: Can't find archetype:%s for formula:%s\n", 
                rp->arch_name,rp->title); 
	   return 0;
    }
   
    return 1;
}

artifact * locate_recipe_artifact(recipe *rp) {
   object *item=get_archetype(rp->arch_name);
   artifactlist *at=NULL;
   artifact *art=NULL;

   if(!item) return (artifact *) NULL; 

   if((at=find_artifactlist(item->type)))
      for(art=at->items;art;art=art->next)
          if(!strcmp(art->item->name,rp->title)) break;
 
   free_object(item);

   return art;
}

int numb_ingred (char *buf) {
  int numb;

  if((numb=atoi(buf))) return numb;
  else return 1;
}

recipelist * get_random_recipelist ( void ) {
  recipelist *fl=NULL;
  int number=0,roll=0;

     /* first, determine # of recipelist we have */
     for(fl=get_formulalist(1);fl;fl=fl->next) number++;
 
     /* now, randomly choose one */
     if(number>0) roll=RANDOM()%number;
 
     fl=get_formulalist(1);
     while(roll && fl) {
        if(fl->next) fl = fl->next;
        else break;
        roll--;
     }   
     if(!fl) /* failed! */
        LOG(llevError,"get_random_recipelist(): no recipelists found!\n");
     else if(fl->total_chance==0) fl=get_random_recipelist();

     return fl;
}

recipe * get_random_recipe ( recipelist *rpl ) {
  recipelist *fl=rpl;
  recipe *rp=NULL;
  int r=0;

  /* looks like we have to choose a random one */ 
  if(fl==NULL) if((fl=get_random_recipelist())==NULL) return rp;

  r=RANDOM()%fl->total_chance;
  for (rp=fl->items;rp;rp=rp->next) {
      r -= rp->chance;
      if (r<0) break;
  }
  return rp;
}

void free_all_recipes()
{
    recipelist *fl=formulalist,*flnext;
    recipe *formula=NULL,*next;
    linked_char *lchar, *charnext;

    LOG(llevDebug,"Freeing all the recipes\n");
    for (fl=formulalist; fl!=NULL; fl=flnext) {
	flnext=fl->next;

	for (formula=fl->items; formula!=NULL; formula=next) {
	    next=formula->next;
      
	    if (formula->arch_name) free_string(formula->arch_name);
	    if (formula->title) free_string(formula->title);
	    for (lchar=formula->ingred; lchar; lchar=charnext) {
		charnext=lchar->next;
		free_string(lchar->name);
		free(lchar);
	    }
	    free(formula);
	}
	free(fl);
    }
}
