// Paul Johnson August 2, 2000

//pjrepeater6.m.  Decentralized scheduling as in pjrepeater4.m runs 
//a bit too slowly, so searching for a speedup, I have used a different 
//scheduling strategy.  Suppose a "day" is 10 steps, and on the first 
//step of each day, each agent is told to "step", which means to 
//schedule its actions for the rest of that day. Create 1 "master 
//schedule" that agents all use.  If all actions arriving in the 
//schedule at each time must be randomized, then the concurrent group 
//option can be used to mix that up.  That slows things down a bit.  
//If you don't do that, there is still a high degree of randomization 
//because the agents are told to step in a randomized order at the 
//beginning of each day.  

// Paul Johnson July 13, 2000
// pjrepeater5.m. In the interest of personal enlightenment and
// education, consider introducing a single "masterSchedule" that all
// agents can put their actions on.  The customization of the schedule
// object occurs in the bugSwarm level.  Note we have a usave of
// "ConcurrentGroup" rather than the customization steps used in the
// previous example.

// We (Marcus Daniels and I) are not recommending the approach here,
// just illustrating it.  pjrepeater4.m showed the best way to design
// these decentralized schedules of swarms in swarms.

//Marcus said, and I agree, "Here's how to configure the shared
//masterSchedule. In some cases it may be easier to look at the
//emergent structure of dynamic schedules if there is a shared,
//e.g. xfprint (masterSchedule).

//But, generally, I think it is better to have schedules in the agents
//themselves.  That way it is more "agent-based" and modular.

// decentralized scheduling using customized schedule type to
// randomize events that arrive on the scheduling mechanism for the
// same time.

#import <simtools.h>
#import <simtoolsgui/GUISwarm.h>
#import <objectbase/Swarm.h>
#import <random.h>

@interface Bug: Swarm
{
  int idnum;
  id schedule;
}

- scheduleSelfAt: (timeval_t)x;
- setSchedule: sch;
- setID: (int)x;
- print;
@end

@implementation Bug

- scheduleSelfAt: (timeval_t)x
{
  [schedule at: x createActionTo: self message: M(print)];
  return self;
}

- setSchedule: sch
{
  schedule= sch;
  return self;
}

- setID: (int) x
{
  idnum=x;
  return self;
}

- print
{
  fprintf (stderr,"I'm %s, Number %d and the current time is %ld\n",
	   [self getName], idnum,
          getCurrentTime ());
  return self;
}
@end

@interface BugA: Bug
- step;
@end

@implementation BugA
- step
{
 
#ifdef MARCUS
  { 
    timeval_t rand=  getCurrentTime()+2;
    fprintf(stderr,"BugA Num %d: there should be action at %ld \n", idnum, rand );
    [self scheduleSelfAt:  rand ];

    //here is a phony action that makes the randomized model swarm "stepSchedule" work:
    [schedule at: getCurrentTime()+10 createActionTo: self message: M(isClass)];
  }

#else
  {
    timeval_t rand= [uniformIntRand getIntegerWithMin: 0 withMax:10];
    fprintf(stderr,"BugA Num %d: rand=%ld, there should be action at %ld \n", idnum, rand, getCurrentTime() +rand );
    [self scheduleSelfAt:  getCurrentTime()+rand ];
        
    //here is the phony action that makes the schedule work:
    [schedule at: getCurrentTime()+10 createActionTo: self message: M(isClass)];
  } 
#endif
  return self;
}


@interface BugB: Bug
- step;
@end

@implementation BugB
- step
{
 
#ifdef MARCUS
 {
   timeval_t rand= 5+getCurrentTime(); 
   fprintf(stderr,"BugB Num %d: there should be action at %ld \n", idnum, rand);
  [self scheduleSelfAt: rand ];
 }

#else
 {
   timeval_t rand= [uniformIntRand getIntegerWithMin: 0 withMax: 9];
   fprintf(stderr,"BugB Num %d: rand=%ld,there should be action at %ld \n", idnum, rand, getCurrentTime() +rand );
   [self scheduleSelfAt: getCurrentTime()+rand ];
 }
#endif
  return self;
}


@interface BugSwarm: Swarm
{
  id masterSchedule;
  id bugList;
}

- buildObjects;

- setTheList: list;

- buildActions;
- (id <Activity>)activateIn: (id <Swarm>)swarmContext;

@end

@implementation BugSwarm
- buildObjects
{
  [super buildObjects];

  {
    id groupProto;
    
    groupProto = [ConcurrentGroup customizeBegin: self];
    [groupProto setDefaultOrder: Randomized];
    groupProto = [groupProto customizeEnd];
    
    masterSchedule = [Schedule createBegin: self];
    // [masterSchedule setConcurrentGroupType: groupProto];
    [masterSchedule setAutoDrop: YES];
    masterSchedule = [masterSchedule createEnd];
  }

  [bugList forEach: M(setSchedule:): masterSchedule];

  return self;
}

- setTheList: list
{
  bugList = list;
  return self;
}

- buildActions
{
  [super buildActions];
  return self;
}

- (id <Activity>)activateIn: (id <Swarm>)swarmContext
{
  [super activateIn: swarmContext];
  [masterSchedule activateIn: self];
  return [self getActivity];
}

@end

@interface ModelSwarm: Swarm
{
  id <Schedule> schedule;
  id stepSchedule;
  id bugSwarm;
  id bugList;
}
- announcement;
- buildObjects;
- buildActions;
- activateIn: swarmContext;

@end

@implementation ModelSwarm

- announcement
{
  fprintf (stderr,"I'm model swarm, current time is %ld\n", getCurrentTime());
  return self;
}

- buildObjects
{
 
  [super buildObjects];

  bugList = [ List create: self];
 
 [bugList addLast: [[BugA create: self] setID: 0]];
 [bugList addLast: [[BugA create: self] setID: 1]];
 [bugList addLast: [[BugA create: self] setID: 2]];
 [bugList addLast: [[BugA create: self] setID: 3]];
 [bugList addLast: [[BugA create: self] setID: 4]];
 [bugList addLast: [[BugB create: self] setID: 5]];
 [bugList addLast: [[BugB create: self] setID: 6]];
 [bugList addLast: [[BugA create: self] setID: 7]];
 [bugList addLast: [[BugB create: self] setID: 8]];
 [bugList addLast: [[BugA create: self] setID: 9]];

  [bugList forEach: M(buildObjects)];

  bugSwarm = [BugSwarm createBegin: self];
  bugSwarm = [ bugSwarm createEnd];
  [bugSwarm setTheList: bugList];
  [bugSwarm buildObjects];

  return self;
}

- buildActions
{
 
  id modelActions;
  [super buildActions];
  modelActions = [ActionGroup create: self];

  [bugSwarm buildActions];

  stepSchedule = [Schedule create: self setRepeatInterval: 10];
  // [modelActions createActionForEach: bugList message: M(step)];

     [
     [modelActions createActionForEach: bugList message: M(step)]
      setDefaultOrder: Randomized];

  [stepSchedule at: 0 createActionTo: self message: M(announcement)];
  [stepSchedule at: 0 createAction: modelActions];

  return self;

}

- activateIn: swarmContext
{
  [super activateIn: swarmContext];
  [stepSchedule activateIn: self];
  [bugSwarm activateIn: self];
  return [self getActivity];
}  
@end

@interface ObserverSwarm: GUISwarm
{
  id <Swarm> modelSwarm;
  id displaySchedule;

}
- buildObjects;
- buildActions;
- (id <Activity>)activateIn: (id <Swarm>)swarmContext;
@end

@implementation ObserverSwarm

- buildObjects
{
  [super buildObjects]; 
  
  modelSwarm = [ModelSwarm create: self];
  [controlPanel setStateStopped];
  [modelSwarm buildObjects];
  return self;
}  

- buildActions
{
  [super buildActions];
  [modelSwarm buildActions];

   displaySchedule = [Schedule create: self setRepeatInterval: 1];
   [displaySchedule at: 0 createActionTo: actionCache message:
M(doTkEvents)];

  return self;
}

- (id <Activity>)activateIn: (id <Swarm>)swarmContext
{
  [super activateIn: swarmContext];
  [modelSwarm activateIn: self];
  [displaySchedule activateIn: self];

  return [self getActivity];
}


int
main (int argc, const char **argv)
{
  id swarm;

  initSwarm (argc, argv);

  swarm = [ObserverSwarm create: globalZone];

  [swarm buildObjects];
  [swarm buildActions];
  [swarm activateIn: nil];
  [swarm go];
  
  return 0;
}

/* 
Local Variables: 
compile-command: "$SWARMHOME/bin/libtool-swarm --mode=link gcc -D_GNU_SOURCE \
  -DAPPNAME=pjrepeater6 -o pjrepeater6 -Wall -Werror -g -Wno-import -I$SWARMHOME/include \
  -I$SWARMHOME/include/swarm -L$SWARMHOME/lib/swarm pjrepeater6.m  -lswarm -lobjc" 
End: 

*/
