/*
 * Production.C - methods for L-System productions.
 *
 * Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
 *                     University of Berne, Switzerland
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#include <stdlib.h>
#include "Production.h"
#include "Error.h"

implementList(ProductionList, ProductionPtr);

//___________________________________________________________ Production

Production::Production(Predecessor* p, Expression* c, SuccessorList* s)
: _name(p->name), predecessor(p), condition(c), successors(s)
{
  arg_count = (p->formalParam) ? (int)p->formalParam->count()
                               : 0;
  stochastic = (successors->count() > 1);
}

Production::~Production()
{
  delete  predecessor;
  if (condition) delete condition;

  for (long i=0; i<successors->count(); i++)
    delete successors->item(i);
  delete successors;
}

// choose a successor out of the successor list and clone it
ModuleList* Production::cloneSuccessor()
{
  // if we have more than one successor, choose by random
  if (stochastic) {
    double p = drand48();
    for (register long i=0; i<successors->count(); i++)
      if (p <= successors->item(i)->probability())
	return successors->item(i)->clone();

    // No successor choosen. VERY, VERY BAD!
    Error(ERR_PANIC, "cloneSuccessor: no successor choosen");
    return NULL;
  }  
  else
    return successors->item(0)->clone();
}

// The probabilities of the successor must cumulate to 1. In addition
//  a preprocessing is done, e.g.:
//    A -> (0.5) B    =>  A -> (0.5) B
//      -> (0.5) C          -> (1.0) C
//  => no arithmetic operation has to be performed will choosing a
//     successor
int Production::cumulateProbability()
{
  double sum = 0.0;

  for (long i=0; i<successors->count(); i++) {
    sum += successors->item(i)->probability();
    successors->item(i)->probability() = sum;
  }
  return ((sum > 1.01) ? 1 : 0);
}

int Production::hashValue()
{
  extern unsigned int modHash(const char*, long);
  return modHash((const char*)_name, (long)arg_count);
}

ostream& operator<<(ostream& os, Production& p)
{
  long i;

  os << p.predecessor->name;
  if (p.predecessor->formalParam) {
    os << '(';
    for (i=0; i<p.predecessor->formalParam->count()-1; i++)
      os << *p.predecessor->formalParam->item(i) << ", ";
    os << *p.predecessor->formalParam->item(i) << ")";
  }
  if (p.condition) os << " : " << *p.condition << ' ';

  for (i=0; i<p.successors->count()-1; i++)
    os << *p.successors->item(i) << "\n\t";
  os << *p.successors->item(i) << '\n';
  
  return os;
}
