/*serialization3.m. 

Begin from serialization1.m

Here's the idea. There is a

    List
	       
    It contains Data objects

    Each Data object contains a variety of variables and containers.
    Data objects can have Lists, Maps, as well as arrays of ints
    and doubles, and these may even be dynamically allocated.

    In this example, the lispOutDeep: code calls the function
    lisp_process_array() to save the dynamic arrays of doubles and ints.  The
    compiler complains that it does not know about this function, but
    the program runs anyway. lisp_process_array() is in swarm's
    src/defobjc/internal.h file, but when I build swarm, that file
    does not get copied into the install directory.  I don't know yet
    what is needed to deal with it. I will get to it.  The other
    serialization work is taken care of by [super lispOutVars: stream
    deep: YES]; 

    Then run ./serialize3 -m create 
    and look over collection.scm.

    Then run ./serialize3 to bring the list back to life.

    One Gotcha I discovered. Watch out to not include things in Data's
    createEnd method.  When the archiver creates objects, it allocates
    memory for variables and if you have code in createEnd, it will
    stomp on the variables that the archiver has already set. To avoid
    that, there is now a buildObjects in Data that is called when
    objects are first created, but it is not called when they are
    reborn by the serializer..

Requires Swarm newer than 2002-02-06

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


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

#define CATEGORY_COUNT 12
#define SCALE_COUNT 7

@interface Integer: CreateDrop
{
  int value;
}
- setInt: (int)theValue;
- (int)getInt;
@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] createEnd] setInt: value] 



@interface Data: CreateDrop
{
  const char *date;
  double someDouble;
  int someInt;
  id myList;
#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)lispOutDeep: stream;

@end

@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"];

}

- (double)getValue
{
  return someDouble;
}

- (void)lispOutDeep: stream
{
  int categoryCount = CATEGORY_COUNT;
  int scaleCount = SCALE_COUNT;

  [stream catStartMakeInstance: "Data"];

  //This method saves variables that are not explicitly saved by the array commands below. 
  //I'm generally confused what it does...
  [super lispOutVars: stream deep: YES];

#ifdef DYN_MEM
  [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];
}




@end

@interface Controller: CreateDrop
{
  id collection;
}

- createList;
@end

#define NAME "myCollection"

@implementation Controller


#define DATA(date,value) \
  [[[[[Data createBegin: getZone (self)] createEnd] buildObjects] setDate: date] setVal: value]
- createList
{
  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)];

  return collection;
}

@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 createList];
      [archiver putDeep: NAME object: list]; 
#ifndef USE_HDF5
      [archiver sync];  //required only for Lisp anymore?
#endif
    }
  else
    {
      id collection;
      collection = [archiver getObject: NAME];
      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 -DDYN_MEM -D_GNU_SOURCE -o serialize3 -Wall -g -Wno-import -I$SWARMHOME/include -I$SWARMHOME/include/swarm -L$SWARMHOME/lib -L$SWARMHOME/lib/swarm serialization3.m  -lswarm -lobjc "
End:
*/

