/* This file contains code for parasitizing and unparasitizing critters.

   06/20/94 ML & CR   Created.
*/

#include <stdio.h>
#include <stdlib.h>
#include "types.h"
#include "random.h"
#include "interactions.h"
#include "species.h"

#define TINT 1       /* local debugging */
#undef TINT

#define LENGTH_BONUS 0.5
#define TOPSCORE 8
#define PROB_LOCAL 1.0   /* probability of attempting first to parasitize */
                         /* one of self's parasites */



/* For hide and seek chromosomes, each gene is a bit.
   Two genes make up a match symbol; the first one is whether
   or not that symbol cares.  The second gene is for the symbols
   that do care. */
int compute_match_score(Critter *seekor,Critter *hidor)
{
  int score,tempseeklen,temphidelen,i;

  score = 0;
  tempseeklen = (seekor->seek_len / 2) * 2;  /* need multiples of 2 */
  temphidelen = (hidor->hide_len / 2) * 2; 

  for (i=0; (i<tempseeklen) && (i<temphidelen); i+=2) 
    {
      if(((seekor->seek)[i]==1) || ((hidor->hide)[i]==1)) /* don't care */
	{
	  score += 0;  /* placeholder :) */
	}
      else if((seekor->seek)[i+1] != (hidor->hide)[i+1])  /* mismatch */
	{
	  score -=1;
	}
      else /* match */
	{
	  score += 1;
	}
    }

  score += (tempseeklen - temphidelen)*LENGTH_BONUS;  /* bonus */

  return(score);
  
}


double compute_association_prob(int score)
{
  if(score<0)
    {
      return(0.0);
    }
  else if(score>TOPSCORE)
    {
      return(1.0);
    }
  else
    {
      return(((double) score)/TOPSCORE);
    }
}

    


void dissociate(Population *pop)
{
  Critter *curcritter,*host;
  int critternum,score;
  double dis_prob;

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

  curcritter = pop->critters;
  for(critternum=0; critternum < pop->current_size; critternum++)
    {
      if(curcritter->mouth != NULL)
	{
	  host = curcritter->mouth;
	  score = compute_match_score(curcritter,host);
	  dis_prob = 1.0 - compute_association_prob(score);
	  if(funiform(1.0) < dis_prob)    /* dissociate */
	    {
#ifdef TINT
	      printf("%d de-parasitizes %d\n",curcritter->id_num,host->id_num);
#endif
	      curcritter->mouth = NULL;
	      host->num_curr_para--;
	      decrement_interactions(host->species, curcritter->species);
	    }
	}
      curcritter = curcritter->p_next;
    }

#ifdef DEBUG
  printf("    ---end dissociate---\n");
#endif
}


void associate(Population *pop)
{
  Critter *curcritter,*curhost,*tempcritter;
  int critternum,score,tryhost,hostcount,numcurpara,found,i;
  double ass_prob;
  
  
#ifdef DEBUG
  printf("    ---in associate---\n");
#endif
  
  if(pop->current_size>1) { /* no parasitism with only 1 individual! */
    curcritter = pop->critters;
    for(critternum=0; critternum < pop->current_size; critternum++) {
      
#ifdef DEBUG
      if(curcritter==NULL) {
	printf("ERROR: NULL pointer to critter # %d\n",critternum);
      }
#endif
      
      if(curcritter->mouth == NULL)
	{
	  if((funiform(1.0)<PROB_LOCAL)&&
	     (curcritter->num_curr_para>0))
	    /* then try one of curcritter's parasites */
	    {                           /* if there are any */
	      tryhost = uniform(curcritter->num_curr_para); 
	      /* indexed BY PARASITE # */
	      numcurpara = 0;
	      tempcritter = pop->critters;
	      found = 0; /* found the desired one yet? */
	      for(i=0;((i<pop->current_size)&&(!found));i++)
		{
		  if(tempcritter->mouth == curcritter) /* this one's a para */
		    {
		      if(tryhost==numcurpara)
			{
			  found = 1;
			  curhost = tempcritter;
#ifdef TINT
			  printf("%d may parasitize it's own parasite: %d\n",
				 curcritter->id_num,curhost->id_num);
#endif
			}
		      numcurpara++;
		    }
		  tempcritter = tempcritter->p_next;
		}
	      if(found==0)
		printf("ERROR: PARASITE NOT FOUND\n");
	    }
	  else  /* try things not parasitizing curcritter */
	    {
	      tryhost = uniform(pop->current_size-1);  
	      curhost = pop->critters;
	      for(hostcount = 0; hostcount < tryhost; hostcount++)
		{
		  if (curhost == curcritter)  /* skip over current critter */
		    curhost = curhost->p_next;
		  curhost = curhost->p_next;
		}
	      if (curhost == curcritter)  /* skip over current critter */
		curhost = curhost->p_next;
	    }
	  
	  score = compute_match_score(curcritter,curhost);
	  ass_prob = compute_association_prob(score);
	  if(funiform(1.0) < ass_prob)      /* associate */
	    {
	      curcritter->mouth = curhost;
	      curcritter->num_times_para++;
	      curhost->num_times_host++;
	      curhost->num_curr_para++;
	      increment_interactions(curhost->species, curcritter->species);
#ifdef TINT
	      printf("%d parasitizes %d(check: %d)\n",curcritter->id_num,
		     curhost->id_num,(curcritter->mouth)->id_num);
#endif
	    }
	}
      curcritter = curcritter->p_next;
    }
  }
  
  
#ifdef DEBUG
  printf("    ---end associate---\n");
#endif
}
	  
      

void handle_associations(Population *the_population)
{
#ifdef DEBUG
  printf(" ---in handle_associations---\n");
#endif

  dissociate(the_population);
  associate(the_population);

#ifdef DEBUG
  printf(" ---end handle_associations---\n");
#endif

}
