/* This file contains all of the initialization routines.

   06/19/94 AW  Created.
   06/19/94 AW  Reads values from parameters file.  
   06/20/94 AW  Initializes random seed and output files.
   06/22/94 AW  Initializes environment and population.
*/
#include <stdio.h>
#include <stdlib.h>
#include "types.h"

/* ----- prototype files ------------------------------------------------ */
#include "init.h"	/* prototypes file */
#include "output.h"
#include "trans_metab.h"
#include "metab_cycle.h"
#include "species.h"

/* ----- constants ------------------------------------------------------ */
#define GUTSIZE 5  /*** This should be initialized in context later
                       and make it proportional to critter size ***/

/* ----- ifdef constants for debugging ---------------------------------- */
#define CONTEXT 1	/* for initializing the context */
#define LOAD	1  	/* for loading the population */
#undef CONTEXT

/***** initialize_context *****/
/* This allocates memory for the context structure and initializes */
/* its fields.

   Called by: main()
   Calls:
*/

Context *initialize_context(char *params_file)
{
Context *the_context;
FILE *fp;		/* file pointer to read params file */
FILE *fp2;		/* file pointer to run_num & random files */
char fieldname[100];	/* reads in name of structure field */
char runfile[100];	/* name of file where run_num is stored */
char filename[100];
long temp;

#ifdef DEBUG
   printf(" ---in init context---\n");
#endif
 
  /* allocate space for context structure */
   the_context = (Context *) malloc(sizeof(Context));

  /* set pointer to params file */
   fp = fopen(params_file, "r");
   if (fp == NULL)
      {
      printf(" Error(init_context): cannot open file: %s\n", params_file);
      return(NULL);
      }

  /* Generate the next run_num.  Each experiment is assigned a unique
     run_num.  This number is simply one more than the value in the
     run_num_file (run.num) specified in the parameters file (params.in).
     Even when we are rerunning an experiment, a new run_num will be
     generated -- the random seed is what is re-used.
  */
   fscanf(fp, "%s %s", runfile, fieldname);
#ifdef CONTEXT
   printf(" runfile = %s\n",  runfile);
#endif
   fp2 = fopen(runfile, "r");
   fscanf(fp2, "%d", &the_context->run_num);
   the_context->run_num++;
   fclose(fp2);
   fp2 = fopen(runfile, "w");
   fprintf(fp2, "%d\n", the_context->run_num);
   fclose(fp2);
#ifdef CONTEXT
   printf(" run_num = %d\n", the_context->run_num);
#endif

  /***** Read in the rest of the parameter values *****/
   fscanf(fp, "%d %s", &the_context->rerun, fieldname);
   fscanf(fp, "%s %s", &the_context->output_path, fieldname);
   fscanf(fp, "%d %s", &the_context->max_steps, fieldname);
   fscanf(fp, "%lf %s", &the_context->mutation_prob, fieldname);

#ifdef CONTEXT
   printf(" Parameter values read in:\n");
   printf("    output_path = %s\n", the_context->output_path);
   printf("    max_steps = %d\n", the_context->max_steps);
   printf("    mutation_prob = %lf\n", the_context->mutation_prob);
#endif

  /* close params file pointer */
   fclose(fp);

#ifdef DEBUG
   printf(" ---end init context---\n");
#endif
   return(the_context);
}
 
/***** init_random *****/
/* parameters:	the_context
		output_file
   called by:	main
   actions:	 Generate the random seed depending on the rerun
     parameter value.  If the value is -1, then generate
     a new random seed for this run.  If the value is >= 0, then
     this value specifies the run number of the experiment to rerun.
*/
int init_random(Context *the_context, Output_file *output_file)
   {
   FILE *fp2;
   char filename[100];
   long temp;

#ifdef DEBUG
   printf("  ---in init_random---\n");
#endif

  /***** Generate random seed and print to file *****/
  /* Generate the random seed depending on the rerun
     parameter value.  If the value is -1, then generate
     a new random seed for this run.  If the value is >= 0, then
     this value specifies the run number of the experiment to rerun.
  */
   if (the_context->rerun < 0)
      {
      the_context->seed = seed_random(the_context->rerun);
#ifdef PR_RANDOM
     /* print to .random file */
      fprintf(output_file[RANDOM_FILE].fp, " %ld", the_context->seed);
      fclose(output_file[RANDOM_FILE].fp);
#endif  /* PR_RANDOM */
      }  /* if generate new random seed */
   else
      {
     /* get old random seed by reading in from a file or something */
      sprintf(filename, "%srun.%d.random", the_context->output_path,
              the_context->rerun);
      fp2 = fopen(filename, "r");
      if (fp2 == NULL)
         {
         printf(" Error(ga_start): cannot open file: %s\n", filename);
         return ERROR;
         }  /* if */
      fscanf(fp2, "%ld", &temp);
      the_context->seed = seed_random(temp);
      fclose(fp2);
#ifdef PR_RANDOM
     /* print to .random file */
      fprintf(output_file[RANDOM_FILE].fp, " %ld", the_context->seed);
      fclose(output_file[RANDOM_FILE].fp);
#endif  /* PR_RANDOM */
      }  /* get old random seed -- rerun an old run */
#ifdef CONTEXT
   printf(" Random seed = %ld\n", the_context->seed);
#endif

#ifdef DEBUG
   printf("  ---end init_random---\n");
#endif
   }  /* init_random */

/***** init_population *****/
/* parameters:  
   called by:
   actions:     Reads in population information from a file.
		Returns the population structure or NULL if error.
*/
Population * initialize_population()
   {
   Population *the_population;
   int error;

#ifdef DEBUG
   printf(" ---in init population---\n");
#endif

   the_population = (Population *) malloc(sizeof(Population));

   error = load_population(the_population);
   if (error == ERROR)  return(NULL);
   init_species_list(the_population);

  /*** seed the population ***/
  /*** initialize the environment ***/

#ifdef DEBUG
   printf(" ---end init population---\n");
#endif
   return(the_population);
}

/***** initialize_stats *****/
/* parameters:
   called by: 	main()
   actions:	mallocs the stats array and initializes 
		the values where needed.
*/
Stats *initialize_stats()
   {
   Stats *stats;
#ifdef DEBUG
  printf(" ---in init stats---\n");
#endif

   stats = (Stats *)malloc(sizeof(Stats));

   stats->num_critters = 0;
   stats->num_hosts = 0;
   stats->num_parasites = 0;
   stats->num_species = 0;
   stats->avg_num_para_per_host = 0.0;
   stats->avg_num_offspring = 0.0;
   stats->avg_age = 0.0;
   stats->avg_energy = 0.0;
   stats->avg_size = 0.0;

#ifdef DEBUG
  printf(" ---end init stats---\n");
#endif
   return(stats);
   }  /* init_stats */

/***** init_output_files *****/
/* parameters:	the_context
   called by:	initialize_context()
   actions:	stores all output file information into an array
		of structures that it returns.  Info includes
		file name, pointer to file, whether the file is
		active, if the header should be printed.
*/
Output_file *init_output_files(Context *the_context)
   {
   int i;
   Output_file *output_file;

#ifdef DEBUG
   printf(" ---in init_output_files---\n");
#endif

   output_file = (Output_file *)calloc(NUM_OUTPUT_FILES,
                                       sizeof(Output_file));
   for (i=0; i<NUM_OUTPUT_FILES; i++)
      output_file[i].active = FALSE;

#ifdef  PR_PARAMS
   sprintf(output_file[PARAMS_FILE].filename,
           "%srun.%d." PARAMS_EXT,
           the_context->output_path,
           the_context->run_num);
   output_file[PARAMS_FILE].header = PARAMS_HEADER;
   output_file[PARAMS_FILE].active = TRUE;
   output_file[PARAMS_FILE].fp = 
           fopen(output_file[PARAMS_FILE].filename, "w");
   if (output_file[PARAMS_FILE].fp == NULL)
      printf(" Error(init_output_files): cannot open file: %s\n",
             output_file[PARAMS_FILE].filename);
#endif

#ifdef  PR_RANDOM
   sprintf(output_file[RANDOM_FILE].filename,
           "%srun.%d." RANDOM_EXT,
           the_context->output_path,
           the_context->run_num);
   output_file[RANDOM_FILE].header = RANDOM_HEADER;
   output_file[RANDOM_FILE].active = TRUE;
   output_file[RANDOM_FILE].fp = 
           fopen(output_file[RANDOM_FILE].filename, "w");
   if (output_file[RANDOM_FILE].fp == NULL)
      printf(" Error(init_output_files): cannot open file: %s\n",
             output_file[RANDOM_FILE].filename);
#endif

#ifdef  PR_STATS
   sprintf(output_file[STATS_FILE].filename,
           "%srun.%d." STATS_EXT,
           the_context->output_path,
           the_context->run_num);
   output_file[STATS_FILE].header = STATS_HEADER;
   output_file[STATS_FILE].active = TRUE;
   output_file[STATS_FILE].fp =
           fopen(output_file[STATS_FILE].filename, "w");
   if (output_file[STATS_FILE].fp == NULL)
      printf(" Error(init_output_files): cannot open file: %s\n",
             output_file[STATS_FILE].filename);
#endif

#ifdef  PR_GENEOLOGY
   sprintf(output_file[GENE_FILE].filename,
           "%srun.%d." GENE_EXT,
           the_context->output_path,
           the_context->run_num);
   output_file[GENE_FILE].header = GENE_HEADER;
   output_file[GENE_FILE].active = TRUE;
   output_file[GENE_FILE].fp =
           fopen(output_file[GENE_FILE].filename, "w");
   if (output_file[GENE_FILE].fp == NULL)
      printf(" Error(init_output_files): cannot open file: %s\n",
             output_file[GENE_FILE].filename);
#endif

   write_header(the_context, output_file);

#ifdef DEBUG
   printf(" ---end init_output_files---\n");
#endif
   return(output_file);
   }  /* init_output_files */

/***** load_population *****/
/* parameters:	the_population
   called by:	
   actions:	Reads in the contents of the envrionment.
		Reads in population information from a file.
		Expects the_population to be allocated already.
		Returns ERROR if error.
*/
int load_population(Population *the_population)
   {
   int error;
   char filename[100];
   FILE *fp;
   int i, j;
   struct critter_struct *current;  /* pointers to linked list */
   struct critter_struct *previous;

#ifdef DEBUG
   printf("  ---in load_population---\n");
#endif

   printf(" Read population from which file? ");
   scanf("%s", filename);
   fp = fopen(filename, "r");
   if (fp == NULL)
      {
      printf(" Error(load_population): cannot open file: %s\n", filename);
      return (ERROR);
      }  /* if */

  /* the first step is to read in the environment contents */
   for (i=0; i<NUM_METABOLITES; i++)
      {
      fscanf(fp, "%d", &the_population->environment[i]);
      }

  /* next read in the size of the population */
   fscanf(fp, "%d", &the_population->current_size);
   the_context->critter_num = the_population->current_size-1;
#ifdef LOAD
   printf("   population size = %d\n", the_population->current_size);
#endif

  /* read data for each critter */
  /* load_critter allocates space for the next critter
     and reads in its info.  Returns a Critter structure.
     all that should need to be set in that structure 
     are the p_prev and p_next pointers. */

  /* first case is special */
   current = load_critter(the_population, fp);
   the_population->critters = current;
   current->p_prev = NULL;
   previous = current;

  /* set up rest of the population */
   for (i=1; i<the_population->current_size; i++)
      {
      current = load_critter(the_population, fp);
      previous->p_next = current;
      current->p_prev = previous;
      previous = current;
      }  /* for i */
   previous->p_next = NULL;

#ifdef LOAD
   printf(" ** Environment: ");
   for (j=0; j<NUM_METABOLITES; j++)
      printf(" %d", the_population->environment[j]);
   printf("\n");
   printf(" ** From linked list:\n");
   current = the_population->critters;
   while (current != NULL)
      {
     /* Critter ID info */
      printf(" critter id: %d, parent id: %d\n",
             current->id_num, current->parent_id);
     /* genetic material */
      printf("      hide_len: %d, hide: ",
             current->hide_len);
      for (j=0; j<current->hide_len; j++)
         printf("%d", current->hide[j]);
      printf("\n");
      printf("      seek_len: %d, seek: ",
             current->seek_len);
      for (j=0; j<current->seek_len; j++)
         printf("%d", current->seek[j]);
      printf("\n");
      printf("      meta_len: %d, metabolism: ",
             current->meta_len);
      for (j=0; j<current->meta_len; j++)
         printf("%d", current->metabolism[j]);
      printf("\n");
     /* metabolism stuff */
      printf("      In gut: ");
      for (j=0; j<NUM_METABOLITES; j++)
         printf(" %d", current->gut[j]);
      printf("\n");
      printf("      amt_food: %d\n", current->amt_food);
     /* Critter's current state */
      printf("      energy: %d\n", current->energy);
      printf("      age: %d\n", current->age);
      printf("      para_age: %d\n", current->para_age);
      printf("      num_offspring: %d\n", current->num_offspring);
      printf("      num_times_host: %d\n", current->num_times_host);
      printf("      num_times_para: %d\n", current->num_times_para);
      printf("      num_curr_para: %d\n", current->num_curr_para);
      current = current->p_next;
      }  /* while */
   printf(" ** End of linked list:\n");
#endif

#ifdef DEBUG
   printf("  ---end load_population---\n");
#endif
   return(OK);
   }  /* load_population */

/***** load_critter *****/
/* parameters:	the_population
		fp		pointer to input file
   called by:	load_population
   actions:	reads in info about one critter
		returns a critter structure or NULL if error.
*/
struct critter_struct *load_critter(Population *the_population, FILE *fp)
   {
   int i;
   int sum;	/*  to sum the amount of food in gut */

   struct critter_struct *critter;
#ifdef DEBUG
   printf("   ---in load_critter---\n");
#endif

   critter = (struct critter_struct *)malloc(sizeof(struct critter_struct));

  /* critter ID info */
   fscanf(fp, "%d", &critter->id_num);
   fscanf(fp, "%d", &critter->parent_id);
  /* genetic material */
   fscanf(fp, "%d", &critter->hide_len);
   critter->hide = (Chromosome)calloc(critter->hide_len, sizeof(Genetype));
   for (i=0; i<critter->hide_len; i++)
      fscanf(fp, "%d", &critter->hide[i]);
   fscanf(fp, "%d", &critter->seek_len);
   critter->seek = (Chromosome)calloc(critter->seek_len, sizeof(Genetype));
   for (i=0; i<critter->seek_len; i++)
      fscanf(fp, "%d", &critter->seek[i]);
   fscanf(fp, "%d", &critter->meta_len);
   critter->metabolism =(Chromosome)calloc(critter->meta_len,sizeof(Genetype));
   for (i=0; i<critter->meta_len; i++)
      fscanf(fp, "%d", &critter->metabolism[i]);

  /***New metabolism stuff - Carlo ***/
   genes_to_metab(critter); /*sets up the "fast_metabolism"*/
   reset_metabolism(critter); /*Prepares the critter to execute the */
			      /*first reaction in its metabolism.*/

  /* metabolism stuff */
   sum = 0;
   for (i=0; i<NUM_METABOLITES; i++)
      {
      fscanf(fp, "%d", &critter->gut[i]);
      sum += critter->gut[i];
      }
   critter->amt_food = sum;
   critter->mouth = NULL;
   critter->prev_metabolizer = NULL;
   critter->next_metabolizer = NULL;
  /* Critter's current state */
   fscanf(fp, "%d", &critter->energy);
   fscanf(fp, "%d", &critter->age);
   fscanf(fp, "%d", &critter->para_age);
   fscanf(fp, "%d", &critter->num_offspring);
   fscanf(fp, "%d", &critter->num_times_host);
   fscanf(fp, "%d", &critter->num_times_para);
   fscanf(fp, "%d", &critter->num_curr_para);

#ifdef DEBUG
   printf("   ---end load_critter---\n");
#endif
   return(critter);
   }  /* load_critter */
