/* brain-objs.hh - Class headers for competences, behavior sequences and
   such like.

   $Log: brain-objs.hh,v $
   Revision 1.1  1999/12/27 23:48:46  joanna
   Initial revision

   Revision 1.6  1998/02/22 21:32:02  jo
   post-"edmund mouse" check in -- 0 some values, add TIME comments for
   getting time from somewhere different (the simulation, not the clock)
   fix a memory leak

   Revision 1.5  1997/10/13 11:53:23  jo
   works with laps, does rat thing

   Revision 1.3  1997/09/10 01:36:43  joanna
   works with robs stuff -- fixed loads more chaining problems,
   new rob libraries.  Interactive rob ringrose, not piped yet.

   Revision 1.1  1997/08/10 18:34:11  joanna
   Initial revision

*/

#include <sys/time.h>  // needed for gettimeofday(), struct timeval.
/*     v--- they are on linux!
// these is not declared in current time.h, for some reason
extern "C" {
  int gettimeofday(struct timeval *tp, struct timezone *tzp); 
  void usleep(unsigned useconds);
}
*/
#include <stdlib.h>


enum Braintype {BRAIN, SENSE, ACT, TRIG, LAP, COMP, DRIVE};

class Brain {  // the base class for the rest of the brain classes
  friend class braintrie;
public: 
  static const Braintype bt = BRAIN; 
  virtual Braintype get_bt(){return (bt);}
  Brain(){};
  inline virtual ~Brain(); // all polymorphing classes need virtual destructors
  Brain(char * myname, Brain * mystuff): name(myname), obj(mystuff){}; 
  Brain(char * myname, int (*mystuff)()): name(myname), value(mystuff){}; 
  Brain(char * myname): name(myname){}; // just for testing
  // void operator=(Brain &source);  // assignment op (don't need for abst.)
  // all pc's need virt. printers
  virtual void print (ostream *os) {*os << "Brain(" << name << ")";}
  
  // stuff used by all or some of the subclasses...
  virtual lapres execute() {cerr << "tried to execute base class Brain:" 
			      << name; return (FAILURE);}
  virtual Brain * instantiate () {
    cerr << "tried to instantiate base class (or primitive or trigger) " 
      << name; return (FAILURE);}
  virtual lapres trigger () {
    cerr << "tried to trigger base class (or primitive or trigger) " 
      << name; return (FAILURE);}
  virtual Brain * active_comp () {return (0);} // overridden only in Comp

  char *get_name() {return (name);}
protected:
  char *name;
  Brain *obj;      // what any of the collections have
  int (*value)(); // what a Sense or Act has  
};  // class Brain

inline Brain::~Brain(){}  // virtual, 'eh?
ostream &operator<<(ostream &os, Brain &r);


// do I _have_ to do this?  ick!
#include "braintrie.hh"


/*  There are two types of brains, primitives and senses.  Unfortunately,
    you don't always know whether you have an act or a competence, so
    you can't really have second level of base classes...
*/

//  ************* PRIMITIVES **************  //

/*   Senses 

# one of the senses is essentially a named perceptual skill.  
# It should take the form of a function that returns a value.
## it's created using "add_sense"
# a "sense" on the other hand is an element of a FAP.  It is also
# a predicate; when it's excuted, it returns a truth value.
# It consists of a reference to one of the senses, and a "value".
## it's created using "new"

*/

class Sense : public Brain {
public:
  // would like inline Sense(char *myname, lapres (*test)(int)); but can't
  // generate the filters on the fly in C so...
  inline Sense(char *myname, char * testst);
  inline Sense(){}; // argh!  needed for using add_sense, but kills type check.
  virtual Braintype get_bt(){return (bt);}
  static bool add_sense(char * name, int (*value)()) {
    return (senses.add(name, new Brain(name, value)));}
  // return Sense's eval function's result when applied on sense's routine
  lapres execute() {
#ifndef QUIETLY
    cerr << " s:" <<name;
#endif
     return (test(sense()));}
protected:
  // name, obj from Brain
  static const Braintype bt = SENSE;   // one for each class (logically!)
  inline lapres test(int val); // what a Sense has...  
  char tester; int thresh;          // and these! since C can't (eval..)
  void encode_test(char *testst);
  int encode_test_lookup(char *testst);
  int (*sense)();              // what the senses have
  static braintrie senses;
};

inline Sense::Sense(char *myname, char *testst) {
  Sense *tempsense = (Sense *) senses.safefind(myname);
  if (tempsense == 0) {
    cerr << "Attempt to generate unknown sense " << myname << ".\n";
    exit (-1);
  } else {
    sense = tempsense->value; 
    name = new char[80]; strcpy(name, myname);
    encode_test(testst);};
} // Sense::Sense(myname, mytest)

inline lapres Sense::test(int val) {
  switch (tester) {
  case '=': 
    return ((val == thresh) ? SUCCESS : FAILURE);
  case '!': 
    return ((val != thresh) ? SUCCESS : FAILURE);
  case '<': 
    return ((val < thresh) ? SUCCESS : FAILURE);
  case '>': 
    return ((val > thresh) ? SUCCESS : FAILURE);
  default: 
    cerr << "bad test! (Sense::)\n"; ::exit(-3);
  } // switch tester
}

//  ****************************  //

/*   Acts 

# one of the actions is a behavior primitive of some kind.
# It should return $::SUCCESS if things are going fine, and $::FAIL 
# if it failed or aborted.
## it's created using "add_act"
# an "act" on the other hand is an element of a FAP.  Otherwise,
# it's pretty much just one of the actions.
## it's created using "new"

# Constructor Note!!  Acts and competences look the same in LAP files, so 
# you want to check the value of Act.act after doing new -- a 0 means nothing
# was found.

*/

class Act : public Brain {
public:
  inline Act(char *myname);
  inline Act(){}; // argh!  needed for using add_act, but kills type check
  virtual Braintype get_bt(){return (bt);}
  static bool add_act(char * name, lapres (*value)()) {
    return (actions.add(name, new Brain(name, value)));}
  // return Act's eval function's result when applied on sense's routine
  lapres execute() {
#ifndef QUIETLY
    cerr << " a:" <<name;
#endif
     return(act());}
protected:
  // name, obj from Brain
  static const Braintype bt = ACT; // one for each class (logically!)
  lapres (*act)();           // which act I am
  static braintrie actions;
};

// check the value of act on a new Act to see if this worked!
inline Act::Act(char *myname) {
  Act *tempact = (Act *) actions.safefind(myname);
  if (tempact == 0) {
    // NOT AN ERROR!  Might be a competence,  >>> check "act" <<<
    // oh wait -- now LAPs only contain acts and senses, nothing else has acts
    // act = 0;
    cerr << "Attempt to generate unknown act " << myname << ".\n";
    exit (-1);
  } else {
    act = tempact->value;
  }
  name = new char[80]; strcpy(name, myname);
} // Act::Act(myname, myaction)


//  ************* COLLECTIONS **************  //

/*   Triggers

# a trigger is like a lap, except it just fires when you execute it,
# without leaving any room for time slicing (so all the elements should
# be simple perceptual stuff!)

# triggers and LAPs could really be completely seperate classes, but 
# I subclass them just in case...

Triggers are always associated with another class, so don't store
themselves... they don't even need a name.

*/

class Trig : public Brain {
public:
  inline Trig(Brain ** mylap): lap(mylap) {};
  inline Trig(){}; // needed for using get_trig, but kills type check
  virtual Braintype get_bt(){return (bt);}
  // return Trig's eval function's result when applied on sense's routine
  inline lapres execute();
protected:
  // name, obj from Brain
  static const Braintype bt = TRIG; // one for each class (logically!)
  Brain **lap;                // array of primitives, s/b null terminated
}; // class Trig

inline lapres Trig::execute() {
  lapres ret;
  // first, do my thing
  for (int iii = 0; lap[iii] != 0; iii++) {
    ret = lap[iii]->execute();
	if (ret == FAILURE) {
	    return (FAILURE);} 
	if (ret == CONTINUE) {
	  char msg[64];
	  sprintf (msg, "triggers shouldn't get CONTINUE 
                 (but %s in %s did!)\n", lap[iii]->get_name(), name);
	    cerr << msg;
	} // if got a CONTINUE, barf (but continue)
    } // foreach element of LAP
  return (SUCCESS);		// if got through (or weren't any)
} // Trig::execute

//  ****************************  //
  
/*   LAPs

LAPs are sort of like FAPs, or maybe reaction chains, or I don't know
-- in my papers, I call them "behavior sequences" but the name stands
for "Learnable Action Patterns" (I wanted "Flexible Action
Patterns"...  maybe I got "fLexible Action Patterns.") but they don't
themselves learn yet, aside from habituation.

*/

class Lap : public Trig {
public:
  inline Lap(){}; // needed for using get_lap, but kills type check
  virtual Braintype get_bt(){return (bt);}
  // Copy constructor is used in instantiate() -- it might be more
  // efficient to build our own (see commented one below) but in
  // that case it should probably be private:
  // inline Lap(Lap &other);
  static void add_lap(char *myname, Brain ** mylap, Trig *mytrig) {
      Laps.add(myname, new Lap (myname, mylap, mytrig));}
  static Lap * get_lap (char *key) { return ((Lap *) Laps.find(key));}
  static Lap * find_lap (char *key) { return ((Lap *) Laps.safefind(key));}
  // note these names are reversed from the PERL -- execute executes like
  // in the other classes, instantiate creates an instance.
  inline lapres execute();
  inline Brain * instantiate();
  lapres trigger () {return(trig->execute());}
protected:
  inline Lap(char *myname, Brain ** mylap, Trig * mytrig); // from read/file
  // name, obj from Brain
  // lap from Trig
  static const Braintype bt = LAP;    // one for each class (logically!)
  static braintrie Laps;
  Trig *trig;                   
  int lindex;
}; // class Lap

inline Lap::Lap (char *myname, Brain ** mylap, Trig * mytrig) {
  name = new char[strlen(myname)+1];
  strcpy (name, myname);
  trig = mytrig;
  lap = mylap;
  lindex = 0;
} // Lap::Lap (name, LAP, trig)

/* See comment in class -- default copy constructors chain off
   their element's copy constructors, defined ones chain off their
   default constructors, unless you use the notation below

inline Lap::Lap (Lap &other): name(other.name), lap(other.lap), 
                              trig(other.trig), lindex(0){
// empty!
} // Lap::Lap (LAP) copy!
*/

inline Brain * Lap::instantiate() {
  Lap * res = new Lap (*this);
  res->lindex = 0;
  return (res);
};

/* When a LAP executes, it just does one of its actions, update its
   process pointer (lindex), and returns the value.  This allows 
   reactivity and possibly even prioritizing during a LAP.
*/
inline lapres Lap::execute() {
  lapres ret;

  // see if done (or degenerate case -- nothing to do!!
  if (lap[lindex] == (Brain *) 0) {
    lindex = 0;
    return (SUCCESS);
  }

  // first, do my thing
  ret = lap[lindex]->execute();

  // now, process and transmit results 
  switch (ret) {
  case FAILURE: 
    lindex = 0; 
    return (FAILURE);
    break;
  case CONTINUE:  // only happens if executing another lap
    return (CONTINUE);
    break;
  case SUCCESS: 
    lindex++;
    return (CONTINUE);
    break;
  default: 
    cerr << "Unknown return value " << ret << "in LAP " << name << 
      "::execute at index " << lindex << ".\n";
    exit (-1);
  } // switch (ret)
} // Lap::execute

/*   
# another startup procedure, seperate to defeat chicken&eggs problem
# could ref actual competences instead of putting in just their names,
# but we don't allow comps right now so dies if not acts or senses
sub tie_comp () { # args ignored  ...
    foreach $fap (keys %LAPs) {
	# can't be more elegant here - have to reassign at the end!
	foreach $iii (0..$#{$LAPs{$fap}->{LAP}}) {
	    unless (ref($LAPs{$fap}->{LAP}->[$iii])) {
		# die "No such comp in Comps \"$LAPs{$fap}->{LAP}->[$iii]\" 
		# (called by LAP \"$fap\" index $iii)" # stuff for comps
		die "Unknown action  \"$LAPs{$fap}->{LAP}->[$iii]\" 
		  (called by LAP \"$fap\" index $iii)" # stuff for no comps
		    unless my $ref = Comp->getcomp($LAPs{$fap}->{LAP}->[$iii]);
		$LAPs{$fap}->{LAP}->[$iii] = $ref;
	    } # unless it's already a reference (sense or act)
	}}} # sub tie_comp

Actually, LAPs aren't supposed to call comps (though they might call
other LAPs) -- so I'm not rewriting this.
*/


//  ****************************  //

/*   Competences

# there's actually two ways a competence can run --- 
#     normal  --  any higher priority FAP that can trigger will, 
#                 usurping any lower FAP that was running.
#   exclusive --  once a FAP is triggered it inhibits everything else,
#                 runs til its terminated.

# right now, "Comp"s run exclusively -- also, they might get renamed
# "reaction chains" to be like the ethology lit. (a little -- they aren't
# really chains!)  "Drive"s are run non-exclusively (see below.)
# In fact, Drives are _more_ mock-parallel since they keep track of 
# the state of each of their FAP's current projects even if they are 
# "interupted."
*/


class Comp : public Brain {
public:
  inline Comp(){active=-2;} // needed for using get_comp, but kills type check
  virtual Braintype get_bt(){return (bt);}
  // See comments on copy constructors in Lap...
  // Notice also that the copy's pointers reference canonical arrays.
  // If we are going to learn Comps or Laps we'll have to change that.
  // inline Comp(Comp &other);
  inline virtual ~Comp(); // destroy what copy constructor creates...
  static void add_comp(char *myname, char *lapstring, Trig *mytrig) {
      Comps.add(myname, new Comp (myname, lapstring, mytrig));}
  static Comp * get_comp (char *key) { return ((Comp *) Comps.find(key));}
  static Comp * find_comp (char *key) { return ((Comp *)Comps.safefind(key));}
  // note these names are reversed from the PERL -- execute executes like
  // in the other classes, instantiate creates an instance.
  inline lapres execute();
  inline Brain * instantiate(); // optional argument for Drive
  lapres trigger () {return(trig->execute());}
  inline Brain * active_comp() {
    if (active != -1 && LAPs[active]->get_bt() == COMP)
				  return (LAPs[active]); else return(0);}
  // stuff for startup -- checks after reading in script files
  // -- er and, does some serious initializing too!  Anything constant
  // across a named competence.
  void check_comp();
  void do_check();
protected:
  inline Comp(char *myname, char *lapstring, Trig *mytrig);
  // name, obj from Brain
  static const Braintype bt = COMP;    // one for each class (logically!)
  static braintrie Comps;
  char *lapstring;               // just for start up, don't bother to copy
  Brain **LAPs;                 // array of primitives, s/b null terminated
  Trig *trig;
  // these should all each have the same index as their LAP
  int lapnum, *rewards, *loss_rate, *recover_rate;
  float *priority;
  int decay_ts, decay_ms;
  int *sorted;                  // the lap indecies in order of priority
  int active;                  // the index of the LAP currently being executed
                              //  or -1 if nothing active

  // stuff for sorting LAPs (for execute) -- results in sorted[]
  inline void sort_comp();                  
  inline void priority_sort();
  // comps often have 0 recovery rates to habituation...
  void reset() { if (loss_rate) // if loses, need fast/guaranteed recovery
    for (int iii = 0; iii < lapnum; iii++) priority[iii] = rewards[iii];}
  // stuff for scanning in
  inline int count_laps();
  Brain * find_lap_comp(char *mystery); // find a component lap or comp
}; // class Comp


// this is just to register the competences, "check_comp" the init routine
// builds the actual competences out of the strings, once all the comps
// have objects
inline Comp::Comp (char *myname, char *mylapst, Trig * mytrig) {
  loss_rate = 0; recover_rate = 0; priority = 0;
  // * I should make this swappable...  TIMEFIX (comment out)
  struct timeval tv;
  gettimeofday(&tv, 0);
  decay_ts = tv.tv_sec;
  decay_ms = tv.tv_usec;
  // * end TIMEFIX comment
  lapstring = new char[strlen(mylapst)+1];
  strcpy (lapstring, mylapst);
  trig = mytrig;
  name = new char [strlen(mylapst)+1];
  strcpy (name, myname);
} // Comp::Comp(char *, char *, Trig)

/*  See comments in Lap...
// the assumption is that this is only used for instantiate: 
// make new versions of all the state that needs to be seperate.
inline Comp::Comp (Comp &other) : LAPs(other.LAPs), 
      trig(other.trig), lapnum(other.lapnum), rewards(other.rewards), 
      loss_rate(other.loss_rate), recover_rate (other.recover_rate){
  name = other.name;

  // share habituation across a named competence!
  priority = other.priority;

  active = -1;                  // flag for no active index
  LAPs = new Brain * [lapnum];  // need a LAPs full of instances
  sorted = new int [lapnum];
  for (int ixi = 0; ixi < lapnum; ixi++) {
    sorted[ixi] = ixi;
    LAPs[ixi] = other.LAPs[ixi]->instantiate();
  }
} // Comp::Comp(Comp) copy!
*/

/* currently, this keeps priority the same across all comps of the
   same name.  what you really want is to not make new competences
   (probably -- think about it.)
*/
// if change something here, change in Drive too, because C++ isn't 
// really OO (snarl.)
inline Brain * Comp::instantiate() {
  Comp * res = new Comp (*this);

  /* Nope -- share habituation across named competence,
     so that if it comes back on the stack it won't
     keep running the first thing!
  if (res->loss_rate != 0) {	
    res->priority = new float[lapnum];
  } // if habituating, build place to store such info
  */

  res->active = -1;                  // flag for no active index
  res->LAPs = new Brain * [lapnum];  // need a LAPs full of instances
  res->sorted = new int [lapnum];
  for (int ixi = 0; ixi < lapnum; ixi++) {
    res->sorted[ixi] = ixi;
    // instantiate LAP's immediately, leave higher stuff til it usurps
    if (LAPs[ixi]->get_bt() == LAP)
      res->LAPs[ixi] = LAPs[ixi]->instantiate();
    else
      res->LAPs[ixi] = LAPs[ixi];
    /* see Nope above
    if (res->loss_rate != 0) 
      res->priority[ixi] = res->rewards[ixi];
    */
  } // for ixi
  return (res);
}  // Comp::instantiate

inline Comp::~Comp () { // destroy what copy constructor creates
  if (active != -2) {delete LAPs; delete sorted;}  
  // delete priority; //no longer instance dependent!
}

inline int Comp::count_laps() {
  int counter = 0;
  for (int ixi = 0; lapstring[ixi]; ixi++)
    if (lapstring[ixi] == '(') 
      counter++;
  return (counter);
} // Comp::count_laps

/*
# this is only complicated if there's habituation -- in which case,
# this is what does the recovery!  Returns a reference to an array of
  lap indicies or something, maybe pointers to the laps sorted in order

  Recovery -- the recovery rate is the number of times per second you
  want the beast to regain a point -- so something that takes a hit of
  30 points and has a recover rate of 1 would run aproximately twice a
  minutep, something that takes a 20 point hit and has a recovery rate
  of 200 would run at about 10 Hz.  Of course, things are less
  predictable than that due to unpredictability of time slice size and
  other processes' priorities.  You need to have something harmless
  with no habituation hanging around if you really want the Hz effect
  to get to work.
*/

inline void Comp::sort_comp () {
  /*
  // TIMEFIX static struct timeval tv;
  static int secs; // TIMEFIX, mics;
  */
  static struct timeval tv;
  static int secs, mics;

  if (recover_rate) {  // gotta deal with habituation
    // * TIMEFIX (comment out)
    gettimeofday(&tv, 0);
    secs = tv.tv_sec - decay_ts;
    mics = tv.tv_usec - decay_ms;
    decay_ts = tv.tv_sec;
    decay_ms = tv.tv_usec;
    for (int lap = 0; lap < lapnum; lap++) {
      priority[lap] += 
	((secs + (mics / 1000000.0)) * recover_rate[lap]);
      priority[lap] = (priority[lap] > rewards[lap]) ? 
	rewards[lap] : priority[lap];
    } // for each lap
    /*  begin NON-TIMEFIX comment
    end TIMEFIX comment * / 
    secs = GlobalTickClock - decay_ts;
    decay_ts = GlobalTickClock;
    for (int lap = 0; lap < lapnum; lap++) {
      priority[lap] += (secs * recover_rate[lap]);
      priority[lap] = (priority[lap] > rewards[lap]) ? 
	rewards[lap] : priority[lap];
    } // for each lap
    end NON-TIMEFIX comment */
    priority_sort();
  } // if a recovery rate
  // otherwise, LAPs should already be sorted by reward!
} // sort_comp

//does quick sort of sorted[] (lap indecies) based on priority[]
inline void Comp::priority_sort() {
  register int i;
  register long *next, *prev;
  long tmp, *base = (long int *) sorted;
  for (next = base, i = lapnum - 1; i > 0; i--)
    {
      prev = next++;
      if ( (priority[*prev] - priority[*next]) < 0)
	{
	  tmp = *next;
	  do  *(prev + 1) = *prev;
	  while (--prev >= base ? (priority[*prev] - priority[tmp]) < 0 : 0);
	  *(prev + 1) = tmp;
	}
    }
  return;
} // Comp::priority_sort

/*
# a standard comp is either:
# -- already executing a FAP, in which case it continues
# -- picking something to do.  If it picks another comp, THAT one becomes
#    the new most active one, this one just returns it.
*/
inline lapres Comp::execute () {
  lapres res; int fap;

  // in my bio model, sensing failures always short-term inhibit their
  // own FAP getting fired.  In this, you just go down in order of priority
  // until you find a triggerable FAP.

  // notice that, in a LAP, we only age on completion
  // (aging at beginning would have a different effect on persistance...)
  if (active != -1) {
    res = LAPs[active]->execute();
    if (res == SUCCESS || res == FAILURE) { // fap finished
      fap = active;
      if (loss_rate)
	priority[active] -= loss_rate[active]; // habituate if does
      active = -1;                            // reset active
      if (res == SUCCESS && !(strcmp (name, LAPs[fap]->get_name()))) {
	reset();           // reset priorities for next time because...
	return (SUCCESS);  // if names match, succeeded in goal!
      }
      else 
	// comp continues, but takes a habituation hit for completing a lap
	return (HABCONT); 
    } // if finishing a FAP, clean up!
    return (CONTINUE);
  } // if already working on a FAP (stored in active)

  // else pick what to do next...

  sort_comp();

  for (int fapx = 0; fapx < lapnum; fapx++) {
    if (LAPs[sorted[fapx]]->trigger() == SUCCESS) {
      if (priority && priority[sorted[fapx]] <= 0)
	break; // things without priority don't get to go!
#ifndef QUIETLY
      cerr << " c:" << LAPs[sorted[fapx]]->get_name(); // debug
#endif
      switch (LAPs[sorted[fapx]]->get_bt()) {
      case COMP: 
	// want the caller to use the new comp, not this one. --
	// signal through active value and returning a terminal value
	active = sorted[fapx];  // signal through active value
	// have to habituate this thing _now_, since we're giving up control
	if (loss_rate)
	  priority[active] -= loss_rate[active]; 
	return (SUCCESS);       // and by returning a terminal value
	break;
      case LAP:
	// new active thing to do (may be goal...)
	active = sorted[fapx];
	return (CONTINUE);
	break;
      default: 
	cerr << "bad return object" << LAPs[sorted[fapx]] << " in Comp "
	  << name << "\n";
	exit (-1);
	break;
      } // switch on type of brain element
    } // if trigger fired succesfully
  } // for each FAP in order of priority
  reset();              // reset priorities because...
  return (FAILURE);	// if we got this far, nothing could run!
} // Comp::execute


/*

reference in case I messed up quick sort
		
//does quick sort of weights
void Beat_block::sort_blocks(int *order ,int length) {
      register int i;
      register long *next, *prev;
      long tmp, *base = (long int *) order;
      for (next = base, i = length - 1; i > 0; i--)
        {
          prev = next++;
          if ( (weights[*prev] - weights[*next]) < 0)
            {
              tmp = *next;
              do  *(prev + 1) = *prev;
              while (--prev >= base ? (weights[*prev] - weights[tmp]) < 0 : 0);
              *(prev + 1) = tmp;
            }
        }
      return;
}

*/

/* the old habituation computation (with sigmoid)

      if ($self->{tired}->{$lap}) {
		    $self->{tired}->{$lap} -= 
			$tm * $self->{recover_rate}->{$lap};
		    $self->{tired}->{$lap} = 0 
			if ($self->{tired}->{$lap} < 0);
		}}}
	# recalculate reward
	foreach $lap (keys %{$self->{rewards}}) {
		    $self->{rewards}->{$lap} = 
			1 / exp (.01 * $self->{tired}->{$lap}) * 
			    $self->{class}->{rewards}->{$lap};
		}
			
*/


//  ****************************  //

/*   Drives

Only a couple significant differences from Comps -- see comments
above.

*/


class Drive : public Comp {
public:
  inline Drive(){active=(Brain **)-2;}; 
  // See comments on copy constructors in Comp
  // inline Drive(Drive &other);
  inline virtual ~Drive(); // destroy what copy constructor creates...
  virtual Braintype get_bt(){return (bt);}
  static void add_drive(char *myname, char *lapstring, Trig *mytrig) {
      Comps.add(myname, new Drive (myname, lapstring, mytrig));}
  static Drive * get_drive (char *key) { return ((Drive *) Comps.find(key));}
  static Drive * find_drive (char *key){return((Drive *)Comps.safefind(key));}
  inline lapres execute();
  inline Brain * instantiate();
protected:
  inline Drive(char *myname, char *lapstring, Trig *mytrig)
    : Comp(myname, lapstring, mytrig){};
  // name, obj from Brain
  // loads of stuff from comp... LAPs, trig...
  static const Braintype bt = DRIVE;    // one for each class (logically!)
  Brain **active;              // whatever's currently being run for each drive
}; // class Drive

/*
// the assumption is that this is only used for instantiate: 
// make new versions of all the state that needs to be seperate.
inline Drive::Drive (Drive &other) {
  active = new Brain * [lapnum];  // hold what's currently getting run for each
} // Drive::Drive(Drive) copy!  (mostly done in superclass)
*/


// if change something here, change it above in Comp too!
inline Brain * Drive::instantiate() {
  Drive * res = new Drive (*this);
  
  /* Nope -- share habituation across named competence,
     so that if it comes back on the stack it won't
     keep running the first thing!
  if (res->loss_rate != 0) {	
    res->priority = new float[lapnum];
  } // if habituating, build place to store such info
  */

  res->LAPs = new Brain * [lapnum];  // need a LAPs full of instances
  res->sorted = new int [lapnum];
  res->active = new Brain * [lapnum];  
  for (int ixi = 0; ixi < lapnum; ixi++) {
    res->sorted[ixi] = ixi;
    // instantiate LAP's immediately, leave higher stuff til it usurps
    if (LAPs[ixi]->get_bt() == LAP)
      res->LAPs[ixi] = LAPs[ixi]->instantiate();
    else
      res->LAPs[ixi] = LAPs[ixi];

    res->active[ixi] = 0;
    /* see Nope above
    if (res->loss_rate != 0) 
      res->priority[ixi] = res->rewards[ixi];
    */
  }
  // except our active's different; hold what's currently getting run for each
  return (res);
}  // Drive::instantiate

inline Drive::~Drive () { // destroy what copy constructor creates
  if (active > 0) delete active;
}

inline lapres Drive::execute () {
  lapres res; int fap; Brain *temp;

  // in my bio model, sensing failures always short-term inhibit their
  // own FAP getting fired.  In this, you just go down in order of priority
  // until you find a triggerable / executable  FAP.

  sort_comp();

  for (int fapx = 0; fapx < lapnum; fapx++) {
    if (LAPs[sorted[fapx]]->trigger() == SUCCESS) {
      if (priority && priority[sorted[fapx]] <= 0)
	break;  // things without priority don't get to go!
      fap = sorted[fapx];
#ifndef QUIETLY
      cerr << " d:" << LAPs[fap]->get_name(); // debug
#endif
      // LAPs hold the _start_ condition for a drive, not necessarily
      // what it's currently up to.
      if (active[fap] == 0) {
	active[fap] = LAPs[fap]->instantiate();
      } // if something's not started or just finished, restart
#ifndef QUIETLY
      else cerr << "(" << active[fap]->get_name() << ")";
#endif

      res = active[fap]->execute();

      // print "$fap \n";  // DEBUG TIMING / fap execution frequency
      // time delay -- must be > 0, (would be better in 1/10 secs...)

      switch (res) {
      case SUCCESS:
	// May be actually a new comp -- look for it!
	if (active[fap]->active_comp()) { // a sign!
	  temp = active[fap];  // keep a pointer through next assign...
	  active[fap] = (active[fap]->active_comp())->instantiate();
	  delete temp;         // ...nail a pesky memory leak!
	  return (CONTINUE);
	}
	// may be we've finished... (unlikely in a drive, but hey)
	if (!strcmp(name, active[fap]->get_name())) {
	  return (SUCCESS);
	}
	// otherwise, fall through to failure, the other terminal
      case FAILURE:
	delete active[fap];
	active[fap] = 0;
	// fall through to HABCONT -- any LAP finish gets habituation
      case HABCONT:
	if (loss_rate)   // habituate ones that habituate, on any termination
	  priority[fap] -= loss_rate[fap];
	// fall through to Continue -- it's all the same if we haven't won!
      case CONTINUE: 
	return (CONTINUE);
	break;
      default: 
	cerr << "bad return from object" << active[fap] << 
	  " in Drive " << name;
	exit (-1);
	break;
      } // switch res
    } // if triggered
  } // for each fap
  return (FAILURE);  // nothing triggered!  We lose!
} // Drive::execute
	
