#include <stdio.h>
#include "tierra.h"
#include "extern.h"

int available[1024] ;

HostTNdata TNdata[1024] ;
HostGBdata GBdata[1024] ;

I32s   lNumSizes ;
double Totaldt ;

HostSizeList  hsl[10000] ;

int message_flag ;

int pass_back_num_cells = 999999999 ;

main(argc, argv)
     int argc;
     char *argv[];
{

  int i, source_node, incoming_size, size_already_exists ;

  char incoming_error_msg[1024] ;
  char error_msg[1024] ;

  char        incoming_extract_buf[10000] ;
  ExtractMsg  *incoming_extract_msg = (ExtractMsg *)incoming_extract_buf ;

  CMMD_enable();

  if (argc < 2)
    {
      printf("\n Arguments: Number of genebank processors [standard tierra input params]\n\n");  
      exit(0) ;
    }
  else 
    {
      NumGBPN = atoi(argv[1]) ;
      if ((NumGBPN < 0) || (CMMD_partition_size() % NumGBPN))
	{
	  printf("\n ERROR: Number of genebank processors (%d) must be a power of two.\n\n",NumGBPN) ;
	  exit(0) ;
	}
    }	

  message_flag = 1 ;

  FEStartup();         

  GetSoup(argc-1,argv+1);   

#if FRONTEND == STDIO
  printf("\n") ;
#endif /* FRONTEND == STDIO */

  for (i=0;i<CMMD_partition_size();i++)
    available[i] = 0 ;

  lNumSizes = 0 ;
  Generations = 0.0 ;

  while(1) 
    {
      if (CMMD_msg_pending(CMMD_ANY_NODE,TN_data_tag))
	{
	  source_node = CMMD_msg_sender() ;
	  CMMD_receive_block(CMMD_ANY_NODE,
			     TN_data_tag,
			     &TNdata[source_node],
			     sizeof(HostTNdata)) ;
	  available[source_node] = 1 ;
	  update_stats(source_node) ;
	}

      if (CMMD_msg_pending(CMMD_ANY_NODE,GB_data_tag))
	{
	  source_node = CMMD_msg_sender() ;
	  CMMD_send_and_receive(CMMD_ANY_NODE,
				GB_data_tag,
				&GBdata[source_node],
				sizeof(HostGBdata),
				source_node,
				GB_data_tag,
				&pass_back_num_cells,
				sizeof(pass_back_num_cells)) ;
	  available[source_node] = 1 ;
	  update_stats(source_node) ;
	}
      if (CMMD_msg_pending(CMMD_ANY_NODE,add_size_tag))
	{
	  CMMD_receive_block(CMMD_ANY_NODE,
			     add_size_tag,
			     &incoming_size,
			     sizeof(incoming_size)) ;

	  size_already_exists = 0 ;
	  for (i=0;i<lNumSizes;i++)
	    if (hsl[i].size == incoming_size)
	      {
		hsl[i].count++ ;
		size_already_exists = 1 ;
		break ; 
	      }

	  if (!size_already_exists)
	    {
	      hsl[lNumSizes].size = incoming_size ;
	      hsl[lNumSizes].count = 1 ;
	      lNumSizes++ ;
	    }
	}

      if (CMMD_msg_pending(CMMD_ANY_NODE,delete_size_tag))
	{
	  CMMD_receive_block(CMMD_ANY_NODE,
			     delete_size_tag,
			     &incoming_size,
			     sizeof(incoming_size)) ;

	  for (i=0;i<lNumSizes;i++)
	    if (hsl[i].size == incoming_size)
	      {
		hsl[i].count-- ;
		if (!hsl[i].count)
		  {
		    lNumSizes-- ;
		    hsl[i].size = hsl[lNumSizes].size ;
		    hsl[i].count = hsl[lNumSizes].count ;
		    break ;
		  }
	      } 
	} 

      if (CMMD_msg_pending(CMMD_ANY_NODE,ERROR_MSG_exit_tag))
	{
	  CMMD_receive_block(CMMD_ANY_NODE,
			     ERROR_MSG_exit_tag,
			     incoming_error_msg,
			     sizeof(incoming_error_msg)) ;
	  FEPrintf(ERR_X,ERR_Y,0,incoming_error_msg) ;
	  exit (0) ;
	}

      if (CMMD_msg_pending(CMMD_ANY_NODE,ERROR_MSG_noexit_tag))
	{
	  CMMD_receive_block(CMMD_ANY_NODE,
			     ERROR_MSG_noexit_tag,
			     incoming_error_msg,
			     sizeof(incoming_error_msg)) ;
	  sprintf(error_msg,"ERROR on PN %d - ",CMMD_msg_sender()) ;
	  strcat(error_msg,incoming_error_msg) ;
#if FRONTEND == STDIO
	  strcat(error_msg,"\n\n") ;
#else
	  strcat(error_msg,"          ") ;
#endif /* FRONTEND == STDIO */

	  FEPrintf(ERR_X,ERR_Y,0,error_msg) ;
	}

      if (CMMD_msg_pending(CMMD_ANY_NODE,EXTRACT_tag))
	{
	  CMMD_receive_block(CMMD_ANY_NODE,
			     EXTRACT_tag,
			     incoming_extract_buf,
			     10000) ;
	
	  incoming_extract_msg->g.genome = (FpInst) (incoming_extract_buf + sizeof(ExtractMsg)) ;
	  incoming_extract_msg->g.gbits = (FpGenB) (incoming_extract_buf + sizeof(ExtractMsg) + 
						    incoming_extract_msg->size * sizeof(Instruction)) ;
	  extract_on_frontend(incoming_extract_msg) ;
	}

      CMMD_poll_for_services() ;

    }
	  
}
  
update_stats(source_node) 
int source_node ;
{
  int i, num_available_gb, num_available_tn, tNumCells ;
  double AvgBD ;

  NumCells  = 0 ;
  InstExeM = 0 ;
  InstExeMmax = 0 ;
  InstExeMmin = 999999999 ;
  Totaldt = 0.0 ;
  AvgBD = 0.0 ;
  TimePop = 0.0 ;
  TimeBirth = 0.0 ;
  TimeDeath = 0.0 ;
  RateMut = 0 ;
  RateMovMut = 0 ;
  RateFlaw = 0 ;

  AverageSize = 0 ;
  tNumCells = 0 ;
  MaxPop = 0 ;
  MaxMem = 0 ;
  NumGenotypes = 0 ;
  NumSizes = 0 ;
  NumGenRQ = 0 ;
  MaxGenPop.size = 0 ;
  MaxGenMem.size = 0 ;


  MinSize = 999999999 ;
  MaxSize = 0 ;

  num_available_tn = 0 ;
  num_available_gb = 0 ;

  for(i=0;i<CMMD_partition_size();i++)
    {
      if ((node_type(i,NumGBPN) == TIERRA_NODE) && (available[i]))
	{
	  /* THIS DATA COMES FROM A TIERRA NODE */

	  InstExeM += TNdata[i].InstExeM ;

	  if (TNdata[i].InstExeM > InstExeMmax)
	    InstExeMmax = TNdata[i].InstExeM ;

	  if (TNdata[i].InstExeM < InstExeMmin)
	    InstExeMmin = TNdata[i].InstExeM ;

	  NumCells += TNdata[i].NumCells ;

	  if (i == source_node)
	    AvgBD = (double) ((TNdata[i].TimeBirth + TNdata[i].TimeDeath) * TNdata[i].dt / 2.0) ;

	  TimeBirth += TNdata[i].TimeBirth ; 
	  TimeDeath += TNdata[i].TimeDeath ;

	  Totaldt += TNdata[i].dt ;
	  TimePop += TNdata[i].TimePop ;

	  RateMut += TNdata[i].RateMut ;
	  RateMovMut += TNdata[i].RateMovMut ;
	  RateFlaw += TNdata[i].RateFlaw ;

	  num_available_tn++ ;
	}
      else
	if ((node_type(i,NumGBPN) == GB_NODE) && (available[i]))
	  {

	    /* THIS DATA COMES FROM A GENEBANK NODE */

	    AverageSize += GBdata[i].AvgSize * GBdata[i].tNumCells ;
	    tNumCells += GBdata[i].tNumCells ;

	    if (GBdata[i].MaxPop > MaxPop)
	      {
		MaxPop = GBdata[i].MaxPop  ;
		MaxGenPop.size = GBdata[i].MaxGenPop.size ;
		MaxGenPop.label[0] = GBdata[i].MaxGenPop.label0 ;
		MaxGenPop.label[1] = GBdata[i].MaxGenPop.label1 ;
		MaxGenPop.label[2] = GBdata[i].MaxGenPop.label2 ;
		MaxGenPop.label[3] = GBdata[i].MaxGenPop.label3 ;
	      }

	    if (GBdata[i].MaxMem > MaxMem)
	      {
		MaxMem = GBdata[i].MaxMem  ;
		MaxGenMem.size = GBdata[i].MaxGenMem.size ;
		MaxGenMem.label[0] = GBdata[i].MaxGenMem.label0 ;
		MaxGenMem.label[1] = GBdata[i].MaxGenMem.label1 ;
		MaxGenMem.label[2] = GBdata[i].MaxGenMem.label2 ;
		MaxGenMem.label[3] = GBdata[i].MaxGenMem.label3 ;
	      }

	    NumGenotypes += GBdata[i].NumGenotypes ;

	    NumGenRQ += GBdata[i].NumGenRQ ;
	    
	    num_available_gb++ ;
	  }
    }

  if (num_available_tn)
    {
      InstExeI = InstExeM ;
      InstExeM /= num_available_tn ;
      Generations += AvgBD / (TimePop * (CMMD_partition_size() - NumGBPN) / num_available_tn) ;
      TimePop = TimePop / Totaldt * num_available_tn ; 
      RateMut /= num_available_tn ;
      RateMovMut /= num_available_tn ;
      RateFlaw /= num_available_tn ;
    }

  if (num_available_gb)
    {
      AverageSize /= tNumCells ;
    }

  NumSizes = lNumSizes ;

  for(i=0;i<lNumSizes;i++)
    {
      if (hsl[i].size < MinSize)
	MinSize = hsl[i].size ;
      if (hsl[i].size > MaxSize)
	MaxSize = hsl[i].size ;
    }

  if (InstExeMmin == 999999999)
    InstExeMmin = 0 ;

  if (num_available_tn == (CMMD_partition_size() - NumGBPN))
    pass_back_num_cells = NumCells ;
  else
    InstExeMmin = 0 ;
    

  NumTN = num_available_tn ;

  FEPlan() ;

#if FRONTEND == STDIO
  printf("\n") ;
#endif 

}


extract_on_frontend(extract_msg)
ExtractMsg  *extract_msg ;
{
    FILE *fp;
    head_t head;
    indx_t *indx ;

    strcpy(ExtrG,extract_msg->ExtrG);

    NumGenDG++;

#if FRONTEND != STDIO    
    FEStats() ;
#else
    printf("Extract: %s\n\n",ExtrG) ;
#endif

    /* open an archive, if it does not exist, create it */

    if (!(fp = open_ar(extract_msg->Buff, extract_msg->size, GFormat, -1)))
    {   FEError(-200,EXIT,NOWRITE,
            "Tierra extract() Unable to open extract file %s",extract_msg->Buff);
    }

    head = read_head(fp);

    indx = read_indx(fp, &head);

    add_gen(fp, &head, &indx, &(extract_msg->g));

    if (indx)
    {   thfree(indx);
        indx = NULL;
    }

    fclose(fp);

}

