/* This file contains code for aging and taxing critters.
   It also handles killing and removing critters from the population.

   06/21/94 ML  Created.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "types.h"
#include "interactions.h"
#include "species.h"
#include "death.h"



/* ---------- parameters ----------------------------------------------*/
/* These parameters are fairly arbitrary, but should probably depend
   on the rate of food injection into the environment.                 */
/* Setting them to 2.0, 0.01, and 10 will tax a very simple critter
   (total genome length of 10) one unit of energy every 10 time steps. */
#define TAX_POWER 2.0
#define TAX_FACTOR 0.01
#define TAX_PERIOD 10

/* maximum age that individuals will be allowed to live */
#define MAXAGE 100


/*---------- code for aging and taxing critters -----------------------*/


int tax_amount(Critter *critter)
{
  int genome_length;
  double tax;

  genome_length = critter->hide_len + critter->seek_len + critter->meta_len;
  
  tax = pow((double) genome_length, TAX_POWER);

  tax = (tax * TAX_FACTOR) + 0.5;

  return ((int) tax);
}


void update_population(Population *population)
{
  Critter *cur_critter;
  
#ifdef DEBUG
  printf(" ---in update_population---\n");
#endif


  for (cur_critter = population->critters; 
       cur_critter != NULL;
       cur_critter = cur_critter->p_next) {
    cur_critter->age++;
    if (cur_critter->mouth != NULL)
      cur_critter->para_age++;
    
    /* tax energy, if tax time */
    if ((cur_critter->age % TAX_PERIOD) == 0)       
      cur_critter->energy -= tax_amount(cur_critter);
  }
  
#ifdef DEBUG
  printf(" ---end update_population---\n");
#endif
}



/*---------- code for killing and removing critters ------------------*/

/* frees all the memory that has been allocated for the critter */
void free_critter(Critter *critter)
{
  Reaction *cur_reaction, *next_reaction;

  free(critter->hide);
  free(critter->seek);
  free(critter->metabolism);

  /* free_fast_metabolism */
  cur_reaction = critter->fast_metabolism;
  while (cur_reaction != NULL) {
    next_reaction = cur_reaction->next_reaction;
    free(cur_reaction);
    cur_reaction = next_reaction;
  }

  free(critter);
}

/* removes all parasites from dying host, corrects living host count */
void remove_parasites(Population *population, Critter *critter)
{
  Critter *cur_critter;
  int num_paras;
  
  if(critter->mouth != NULL) {
    critter->mouth->num_curr_para--;   /* correct host's count */
    decrement_interactions(critter->mouth->species, critter->species);
  }
  
  for (cur_critter = population->critters;
       cur_critter != NULL;
       cur_critter = cur_critter->p_next) 
    if (cur_critter->mouth == critter) {
      cur_critter->mouth = NULL;
      decrement_interactions(critter->species, cur_critter->species);
    }
}

/* remove critter from linked list of critters */
void remove_from_list(Population *population, Critter *critter)
{
  if (critter->p_prev != NULL) {
    if (critter->p_next != NULL) {  
      /* previous and next critters exist */
      critter->p_prev->p_next = critter->p_next;
      critter->p_next->p_prev = critter->p_prev;
    }
    else {  
      /* only previous critter exists; no next critter */
      critter->p_prev->p_next = NULL;
    }
  }
  else { 
    /* only next critter exist; no previous critter */
    if (critter->p_next != NULL) {
      critter->p_next->p_prev = NULL;
      population->critters = critter->p_next;
    }
    else { 
      /* neither previous or next critters exist */
      population->critters = NULL;
    }
  }
}

/* throw critters contents into the environment */
void recycle_critter(Population *population, Critter *critter)
{
  int metab;
  Reaction *react_ptr;

  for (metab=0; metab<NUM_METABOLITES; metab++)
    population->environment[metab] += critter->gut[metab];
  (population->environment[1]) += critter->meta_len + critter->hide_len +
    critter->seek_len;
}

/* remove critters which are too old, 
   or which have energy <= 0                        */
void weed_out_the_dead(Population *population)
{
  Critter *cur_critter, *next_critter;

#ifdef DEBUG
  printf(" ---in weed_out_the_dead---\n");
#endif
  
  cur_critter = population->critters;
  while (cur_critter != NULL) {
    next_critter = cur_critter->p_next;
    if ((cur_critter->age > MAXAGE) || (cur_critter->energy <= 0)) {

#ifdef DEBUG
      printf("critter id num %d is dying\n",cur_critter->id_num);
#endif
      
      recycle_critter(population, cur_critter);

      remove_parasites(population, cur_critter);

      remove_from_list(population, cur_critter);
      
      population->metabolizer = population->critters;
      population->current_size--;
      /* this next line MUST be done after remove_parasites */
      decrement_species_count(cur_critter->species);
      free_critter(cur_critter);
      
    }
    cur_critter = next_critter;
  }
  
#ifdef DEBUG
  printf(" ---end weed_out_the_dead---\n");
#endif
}


