/* C structure support class.  A CStruct contains two instance
 variables, a dictionary of field descriptions and binary data.
 CStructs types must be declared using UserType class. See UserType
 class for more information. CStructs are useful whenever you need
 to convert between Actor objects and binary data, such as in
 communication with an operating system, disk files or a
 window manager. */!!

inherit(Object, #CStruct,
#(fields  /* field info objects */
data    /* binary struct */), 2, nil)!!

now(class(CStruct))!!

/* Return a new CStruct whose fields are described by
  the given user type. Type is a symbol, which must be
  a previously defined UserType. */
Def build(self, type)
{ ^build(new(self), type);
}!!

now(CStruct)!!

/* Return a handle to the data struct. */
Def asHandle(self)
{ ^asHandle(data);
}!!

/* Evaluates the block over each of the receiver's fields.
  A field is passed to the block as an Association, the
  Association represents a field's name-value pair. */
Def assocsDo(self, aBlock)
{ ^do(fields,
   {using(f)
     eval(aBlock, init(new(Association), name(f), self[name(f)]));
   });
}!!

/* Return the value of the field with the given
  name. */
Def at(self, name | field)
{ errorIfNil(field := fieldAt(self, name),
  #fieldNameError);
  ^fetch(field, data, 0);
}!!

/* Initialize the receiver using the given type. The type
  must be a valid UserType symbol. */
Def build(self, type | t )
{ t := findType(CType, type);
  data := new(Struct, length(t));
  fields := fields(t);
}!!

/* Return the data portion of the CStruct. */
Def data(self)
{ ^data;
}!!

/* Evaluates a block over each value of the receiver's
  fields. */
Def do(self, aBlock)
{ ^do(fields,
   {using(f) eval(aBlock, self[name(f)]);
   });
}!!

/* Return the field description information
  of the field with the given name. */
Def fieldAt(self, aName)
{ ^fields[aName];
}!!

/* Return the CStructs field descriptions. */
Def fields(self)
{ ^fields;
}!!

/* Free the handle to the data struct. */
Def freeHandle(self | hnd)
{ ^freeHandle(data);
}!!

/* Return the data portion of the CStruct.  Note:
  this object should be made static if it is passed to
  Windows and could move before the Call is executed. */
Def getData(self | aCStruct)
{ aCStruct := copy(self);
  setData(aCStruct, getData(data));
  ^aCStruct;
}!!

/* Return true to inspector's indexed query. */
Def isIdx(self)
{
}!!

/* Evaluates a block over each name of the receiver's
  fields. */
Def keysDo(self, aBlock)
{ ^do(fields,
   {using(f) eval(aBlock, name(f));
   });
}!!

/* Return a long pointer to the data struct. */
Def lP(self)
{ ^lP(data);
}!!

/* Print the receiver CStruct onto the specified stream. */
Def printOn(self, aStream | pos)
{ pos := position(aStream);
  printOn(class(self), aStream);
  printOn('(', aStream);
  do(fields,
  {using(f)
    if position(aStream) - pos > printSize()
    then ^printOn( "...)", aStream);
    endif;
    printOn(f, aStream);
    printOn(tuple('=', self[name(f)], "; "), aStream);
  });
  printOn(')', aStream);
}!!

/* Store the field with the given name. Where val
  is the value to be stored and name is the field
  name. */
Def put(self, val, name | field)
{ errorIfNil(field := fieldAt(self, name),
  #putFieldNameError);
  ^store(field, data, val, 0);
}!!

/* Store a logical element of an arrayed field. Where
  val is the value to be stored, name is the field name
  and offs is element offset into the field array. */
Def putElement(self, val, name, offs | field)
{ errorIfNil(field := fieldAt(self, name), #putFieldNameError);
  storeElement(field, data, val, offs, 0);
  ^val;
}!!

/* Allow the user to set new data (such as a Handle). */
Def setData(self, d)
{ ^data := d;
}!!

/* Set both the field descriptions and data to the
  specified values of the arguments. */
Def setFields(self, f, d)
{ fields := f;
  data := d;
}!!

/* Return the contents of the field with the given
  name. Same as at method, except returns nil if
  given field name is invalid. */
Def shortAt(self, name | field)
{ if field := fieldAt(self, name)
  then ^fetch(field, data, 0);
  endif;
  ^nil;
}!!

/* Return the size (in bytes) of the receiver's
  binary data portion. */
Def size(self)
{ ^size(data);
}!!
