/*serialization.m. 


By Paul Johnson <pauljohn@ku.edu>
March 6, 2002

Adapted from a post by
Marcus Daniels Mar 29, 2000 swarm-support email
(which was archived as 
http://lark.cc.ku.edu/~pauljohn/SwarmFaq/WorkingExampleCode/objc/HDF5orLispListsInOut.m)
and including "lispOutDeep" method created by Marcus, Feb. 6, 2002

Requires Swarm newer than 2002-02-05

This is a serialization example that shows how to serialize
dynamically allocated arrays and reload them. This currently works
in Lisp to write and read.
  
The default build uses statically allocated memory.
Add the cpp flag -DDYN_MEM to use dynamically allocated arrays
inside the Data class.

Please note the fundamental importance of the lispOutDeep method
in the Data class. That method has explicit code that writes
the dynamically allocated arrays. That method "catches" 
all the other IVARs with 

 [super lispOutVars: stream deep: YES];

The super class's lispOutVars:deep: method is inherited from high up
in the SwarmObject hierarchy, and it writes out all primitives
(doubles, ints, and such) as well as triggering Swarm's archiving
routines for Swarm Lists and other things Swarm knows how to
archive. If there are statically allocated arrays, it will also
find/save them as well.

If the Data object has references to things you don't want saved 
along with the object, then don't do it that way.  There are 
commands that can be used to save particular variables.

This is able to write and read static arrays in lisp format.
With Marcus's help, I can write and read dynamically allocated
arrays in Lisp/scm format.
I am able to write static arrays in hfd5 format.
I get seg faults when I try to load the hdf5 format files.


To save data use:
   ./serialize1 -m create

   ./serialize1 
to read the saved data back in and generate some printout.

I cut out the MAP compiler flag and map related examples to 
reduce distraction.
*/

#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 Data: CreateDrop
{
  const char *date;
  double value;

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

#else
  int * category;
  double * scales;

#endif


}


#ifdef DYN_MEM

- createEnd;

#endif

- setDate: (const char *)date;
- (void)describe: stream;
- (double)getValue;
- setVal: (double)value;
- (void)lispOutDeep: stream;

#ifdef DYN_MEM
- hdf5OutDeep: (id <HDF5>)hdf5obj;
#endif

@end

@implementation Data

#ifdef DYN_MEM
- createEnd
{
  printf("DYN_MEM is set\n");
     scales = [globalZone  allocBlock: (SCALE_COUNT* sizeof(double)) ];
     category = [globalZone allocBlock: (CATEGORY_COUNT* sizeof(int))];
     return [super createEnd];
}
#endif

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

- setVal: (double)theValue
{
  int i;
  value = 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;
}


//This will print out some variables. It is triggered by xfprint() in main.
- (void)describe: stream
{
  int i;
  [stream catC: "This is a Data object. Here are some IVARS\n"];
  [stream catC: "Date: "];
  [stream catC: date];
  [stream catC: " Value: "];
  [stream catDouble: value];
  [stream catC: " Integer Array called category: "];
  for (i = 0; i < CATEGORY_COUNT; i++)
    {
      [stream catInt: category[i]];
      [stream catC: " "];
    }

  [stream catC: "\n Double Array called scale: "];
  for (i = 0; i < SCALE_COUNT; i++)
    {
      [stream catDouble: scales[i]];
      [stream catC: " "];
    }
  [stream catC: "\n"];

}

- (double)getValue
{
  return value;
}


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

  [stream catStartMakeInstance: "Data"];
  [super lispOutVars: stream deep: YES];//Important to note this!!

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


#ifdef DYN_MEM
- hdf5OutDeep: (id <HDF5>)hdf5obj
{
  int categoryCount = CATEGORY_COUNT;
  int scaleCount = SCALE_COUNT;

  [super hdf5OutDeep: hdf5obj];

  //  [hdf5obj shallowStoreObject: self];

  [hdf5obj storeAsDataset: "category" typeName: NULL type: fcall_type_sint rank: 1 dims: &categoryCount ptr: category];
  [hdf5obj storeAsDataset: "scales" typeName: NULL type: fcall_type_double rank: 1 dims: &scaleCount ptr: scales ];
  return self;
}
#endif



@end

@interface Controller: CreateDrop
{
  id collection;
}

- createList;
@end

#define NAME "myCollection"

@implementation Controller


#define DATA(date,value) \
  [[[[Data createBegin: getZone (self)] createEnd] 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 -DUSE_HDF5 -D_GNU_SOURCE -o serialize1  -g -Wno-import -I$SWARMHOME/include -I$SWARMHOME/include/swarm -L$SWARMHOME/lib -L$SWARMHOME/lib/swarm serialization1.m  -lswarm -lobjc "
End:
*/

