/*serialization4.m. 

Begin from serialization3.m. We want Agents to know of their environment.

Data has new Ivars that refer to external structures:

id theGrid;
id controllerList;
Controller * controller; //explicit type avoids some compiler warnings

We don't want those archived. When we later reload the serialized
data, we will explicitly set the values of those variables with set
methods. When the Controller is recreated and the list is read from
collection.scm, then the Controller has to have the list set for it.
Then, when Data objects are created, they can get most IVARS from the
file, but not all. Data objects are told the Controller's identity
with [setController:] and then after that they can ask the controller
for references to theGrid and controllerList. I've done that in a
method "lispInReset" and that method presupposes the controller is
known to the Data object so the Data object can ask the controller for
the grid ([controller getGrid]) and its list ([controller getList]).

In Data's lispOutDeep: method, I stop Swarm from trying to archive objects
and references to thes things I call external structures by changing:

 [super lispOutVars: stream deep: YES];

to

 [super lispOutVars: stream deep: NO];

By setting deep:NO, I still get automatic saves of primitives, doubles,
ints, and statically allocated arrays of doubles, ints, and such. But
it ignores all pointers, as in: 

int * x; 

and when it finds a pointer to an Object like

id myList;

the archiver saves myList as a "nil".  After you run this, in
collection.scm you will see myList is set twice, once as nil and once
with values, because of the way lispOutDeep: is written for Data.

And then I add individual commands, one for each object I want archived.

    run 
    ./serialize4 -m create 
    and look over collection.scm.
    then run
    ./serialize4


Add the cpp flag DYN_MEM to use dynamically allocated arrays
inside the Data class.

Note I've moved the interfaces of Data and Controller to the top of
file so the declarations are available when needed.


By Paul Johnson <pauljohn@ku.edu>
Adapted from a post by
Marcus Daniels Mar 29, 2000 swarm-support email
and including "lispOutDeep" method by Marcus, Feb. 6, 2002

*/

#import <simtools.h> // initSwarm
#import <defobj.h> // Archivers
#import <defobj/Create.h> // CreateDrop
#import <defobj/defalloc.h> // getZone
#import <space.h> // Swarm Grid2d


#define CATEGORY_COUNT 12
#define SCALE_COUNT 7

@interface Integer: CreateDrop
{
  int value;
}
- setInt: (int)theValue;
- (int)getInt;
@end




@interface Controller: CreateDrop
{
  id <Grid2d> aGrid;
  id <List> collection;
}

- buildObjects;

- setList: aList;
- getList;
- getGrid;

- sayHi;

@end




@interface Data: CreateDrop
{
  const char *date;
  double someDouble;
  int someInt;
  id myList;

  //IVARS that refer to external structures, but are not copies
  id <List> controllerList;
  id <Grid2d> theGrid;
  id controller;

#ifndef DYN_MEM
  int category[CATEGORY_COUNT];
  double scales[SCALE_COUNT];
#else
  int * category;
  double * scales;

#endif
}

- createEnd;
- buildObjects;
- setDate: (const char *)date;
- (void)describe: stream;
- (double)getValue;
- setVal: (double)value;
- (void)setGrid: it;

- setController: it;
- setControllerList: it;
- describeControllerList;
- (void)lispOutDeep: stream;
- lispInReset;

@end




@implementation Integer

- setInt: (int)theValue
{
  value = theValue;
  printf("INTEGER CREATED: %d\n", theValue);
  return self;
}

- (int)getInt
{
  return value;
}
@end

#define INTEGER(value) \
  [[[Integer createBegin: scratchZone] setInt: value] createEnd]


@implementation Data


- createEnd
{
  return [super createEnd];
}


//can't dump these into createEnd, because createEnd gets run
//automatically when lisp loads these objects again.
- buildObjects
{
  int i, randNumber1;

  randNumber1 = [uniformIntRand getIntegerWithMin: 0L withMax: 5L];
  
#ifdef DYN_MEM
  printf("DYN_MEM is set\n");
  scales = [globalZone  allocBlock: (SCALE_COUNT* sizeof(double)) ];
  category = [globalZone allocBlock: (CATEGORY_COUNT* sizeof(int))];
#endif
  myList = [List create: [self getZone]];

  for ( i = 0; i < randNumber1; i++)
    {
      int randNumber2 = [uniformIntRand getIntegerWithMin: 10L withMax: 20L];
      [myList addLast: INTEGER(randNumber2)];
    }
  someInt = 55;
  return self;
}


- setDate: (const char *)theDate
{
  date = STRDUP (theDate);
  return self;
}

- setVal: (double)theValue
{
  int i;
  someDouble = theValue;
  for (i = 0; i < CATEGORY_COUNT; i++)
    {
      category[i] = i;
    }
  
  for (i = 0; i < SCALE_COUNT; i++)
    {
      scales[i] = [uniformDblRand getDoubleWithMin:0 withMax:1L];
    }
  return self;
}


- (void)describe: stream
{
  int i;
  id anObject, index;
  [stream catC: "Hello, I'm a Data object. Here's my story \n"];
  [stream catC: "Date: "];
  [stream catC: date];
  [stream catC: " someDouble: "];
  [stream catDouble: someDouble];
  [stream catC: "\nHere is myList. It is a bunch of  objects storing integers. I have "];
  [stream catInt: [myList getCount]];
  [stream catC: " items in myList. Their values are:" ];
  index = [myList begin: getZone(self)];
  while (( anObject = [index next]) )
    {
      [stream catC: " "];
      [stream catInt: [anObject getInt]] ; 
    }
  [stream catC: " \n"];
  [stream catC: " The integer array called category: "];
  for (i = 0; i < CATEGORY_COUNT; i++)
    {
      [stream catInt: category[i]];
      [stream catC: " "];
    }
  [stream catC: "\n And the double valued array called scale: "];
  for (i = 0; i < SCALE_COUNT; i++)
    {
      [stream catDouble: scales[i]];
      [stream catC: " "];
    }
  [stream catC: "\n"];

  if (controllerList)
    {
      [stream catC: "I also know the controller list. It has "];
      [stream catInt: [controllerList getCount]];
      [stream catC: " elements \n"];
    }
}

- (double)getValue
{
  return someDouble;
}

//Tell Data agents about the grid they are in
- (void)setGrid: it
{
  theGrid = it;
}


- setController: it
{
  controller = it;
  return self;
}

//Agents might need this in order to sample from their environment
- setControllerList: it
{
  controllerList = it;
  return self;
}

- describeControllerList
{
  printf("\nHi, In the \"describeControllerList method\", I'm a Data Object named %s, and I am aware of this collectorList. \n", date);
  printf ("The collectorList's xfprint() says is:\n");
  xfprint (controllerList);
  printf ("End of xfprint(controllerList) in describeControllerList");
  return self;
}

- (void)lispOutDeep: stream
{

  [stream catStartMakeInstance: "Data"];

  //If Data has values set for "theGrid" or "controllerList"
  //I get a seg fault. If I save the IVARs one-by-one, it is OK.
   [super lispOutVars: stream deep: NO];
 
   [stream catSeparator];
   [stream catKeyword: "myList"];
   [stream catSeparator];
   [myList lispOutDeep: stream];

   //??Is previous 4-line thing same as:
   //[stream catStartMakeInstance: "myList"];
   //[myList lispOutDeep: stream];
   //[stream catEndMakeInstance];


// If i wanted to specifically save a string, a double, and Int, I'd
// do this instead of lispOutVars:.  lispOutVars: with deep:NO causes a nil to be
// written for objects, although it appears harmless
//    [stream catSeparator];
//    [stream catKeyword: "date"];
//    [stream catSeparator];
//    [stream catString: date];
  
//    [stream catSeparator];
//    [stream catKeyword: "someDouble"];
//    [stream catSeparator];
//    [stream catDouble: someDouble];
  
//    [stream catSeparator];
//    [stream catKeyword: "someInt"];
//    [stream catSeparator];
//    [stream catInt: someInt];

#ifdef DYN_MEM
 {
  int categoryCount = CATEGORY_COUNT;
  int scaleCount = SCALE_COUNT;

  [stream catSeparator];
  [stream catKeyword: "category"];
  [stream catSeparator];
  lisp_process_array (1, &categoryCount, fcall_type_sint,
                      category, NULL, stream, NO);
  
  [stream catSeparator];
  [stream catKeyword: "scales"];
  [stream catSeparator];
  lisp_process_array (1, &scaleCount, fcall_type_double,
                      scales, NULL, stream, NO);

 }
#endif  

  [stream catEndMakeInstance];
}

//After a Data object is "reborn" by lisp, there are
//some variables that refer to external structures.
//Data now asks the controller for those structures.
- lispInReset 
{
  controllerList = [controller getList];
  theGrid = [controller getGrid];
  return self;
}

@end







#define NAME "myCollection"

@implementation Controller


#define DATA(date,value) \
  [[[[[Data createBegin: getZone (self)] createEnd] buildObjects] setDate: date] setVal: value]


- buildObjects
{
  aGrid = [Grid2d create: getZone(self) setSizeX: 100 Y: 100];
 
  collection = [List create: getZone (self)];
  [collection addLast: DATA ("1999-01-01", 1.0)];
  [collection addLast: DATA ("1999-01-02", 2.0)];
  [collection addLast: DATA ("1999-12-31", 3.0)];

  [aGrid putObject: [collection atOffset: 0] atX: 10 Y: 20];
  [aGrid putObject: [collection atOffset: 1] atX: 20 Y: 30];
  [aGrid putObject: [collection atOffset: 2] atX: 50 Y: 40];

  //Tell the Data objects where they are:
  [collection forEach: M(setGrid:):aGrid];
  [collection forEach: M(setControllerList:):collection];

  return collection;
}

- lispInReset
{
  aGrid = [Grid2d create: getZone(self) setSizeX: 100 Y: 100];
  return self; 
}

- setList: aList
{
  collection = aList;
  return self;
}

- getList
{ 
  return collection;
}

- getGrid;
{
  return aGrid;
}

- sayHi
{
  printf("I'm a Controller. How are you? \n\n");
  return self;
}

@end

int
main (int argc, const char **argv) 
{
  id archiver;
  id controller;
  
  initSwarmBatch (argc, argv); 
 
#ifdef USE_HDF5
  archiver = [HDF5Archiver create: globalZone setPath: "collection.hdf"];
#else
  archiver = [[[LispArchiver createBegin: globalZone]
		setPath: "collection.scm"]
	       createEnd];
#endif

  controller = [Controller create: globalZone];
  

  
  if (strcmp ([arguments getAppModeString], "create") == 0)
    {
      id list = [controller buildObjects];
      [archiver putDeep: NAME object: list]; 
#ifndef USE_HDF5
      [archiver sync];  //required only for Lisp anymore?
#endif
    }
  else
    {
      id collection;
      collection = [archiver getObject: NAME];

      //After lisp in magic creates the list and the objects in it, we need
      //to set some variables in both collector and for each item in the collection.
      //Instead of calling buildObjects for Collector, the Controller
      //needs only reset some things:   
      [controller setList: collection];
      [controller lispInReset];

      [collection forEach: M(setController:):controller];
      [collection forEach: M(lispInReset)];
      xfprint (collection);
    }
  
  
  [archiver drop]; //must drop to make it write in hdf5 mode. Replaces effect of sync?
  return 0;
}

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

