//=-- Serialize.h - Serialize and deserialize data -----------------*- C++ -*-=
//
//  The class handles serialization of data in a fairly typesafe manner.  Its 
//  pretty much kinda cool, but not as cool as it could be, because it can't
//  serialize/deserialize arbitrary pointers to objects...
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner and stuff...
//=---------------------------------------------------------------------------=

#ifndef SERIALIZE_H
#define SERIALIZE_H

#include "Object.h"

// OP_IN(X,Y) expands into an insertion clause of the Serialize class.
//   Example:  OP_IN(char, INT) expands to the following:
//
//  inline Serialize &operator <<(const char i) { 
//    return Write(INT, &i, sizeof(i));
//  }
//
#define OP_IN(X,Y) inline Serialize&operator<<(const X i) { \
                   return Write(Y, &i, sizeof(i)); }

// OP_EX(X,Y) expands into an extraction clause of the Serialize class.
//   Example:  OP_EX(char, INT) expands to the following:
//
//  inline Serialize &operator>>(char &i) { 
//    return Read(INT, &i, sizeof(i));
//  }
//
#define OP_EX(X,Y) inline Serialize&operator>>(X &i) { \
                   return Read(Y, &i, sizeof(i)); }

class MSAPI Serialize {
protected :
    enum DataType { 
    UINT = 0,                     // int, and signed int.  The specific type 
    SINT = 1,                     // of integer (char,short,int,long) is 
                                  // determined by the length.
    FLOAT = 2, 
    DOUBLE = 3, 
    LNGDBL = 4,        // Floating point types
    CHARPTR = 5,                      // String type
    SIZE = 6,                         // Special type for when data is > 31 byes long
    SECTION = 7                       // Section Heads.
  };

public :

  // Serialize() - Default Constructor
  //
  inline Serialize() { 
    ErrorMethod = 0; F = 0;
    Close();                              // Close initializes flags...
  }

  // Obviously, you can create an object with a filename...   If the
  //   OpenForReading parameter is true, the file is opened for reading, 
  //   if it is false, the file is opened for writing...
  //
  Serialize(const char *Filename, int OpenForReading);

  // ~Serialize() - Destructor.
  //
  inline virtual ~Serialize() { 
    Close();
  }
  
  // Load() - Open a file for reading...
  //
  void Load(const char *Filename);

  // Save() - Open a file for writing...
  //
  void Save(const char *Filename);
  
  // Close() - Close the data file associated with SerMan object...
  //
  void Close();

  // NewSection() - Starts a new section in the output file.
  //
  void NewSection();

  // NextSection() - Goes to the next section in an input file
  //
  void NextSection();

  // EndOfSection() - Returns true if the input file is at the end of the 
  //   current section.  If so, call NextSection() to continue.
  inline int EndOfSection() {
    return AtSection;
  }

  // PeekLength() - Return length in bytes of next symbol and optionally
  //   the type of the symbol.  If Restore is set the 0, the stream is 
  //   advanced past this symbol, otherwise, the stream is not modified.
  //
  int PeekLength(unsigned char *TYPE = 0, int Restore = 1);

  // eof() - Returns true if past end of file...
  //
  int eof();

  // PeekEOF() - Returns true if the next byte is past the end of file
  //
  int PeekEOF();

  //=-------------------------------------------------------------------------=
  //                         Error Handling Functions
  //=-------------------------------------------------------------------------=

  // Error() - Returns true if the error flag is set.
  //
  inline int Error() {
    return ErrorFlag;
  }

  // ResetError() - Resets the internal state to acknowledge the error.
  //
  inline void ResetError() {
    ErrorFlag = 0;
  }

  // ReportError() - This function is called whenever an error happens.  In
  //   subclasses, this can be overriden to provide customized error handling.
  //   In the default implementation, the error flags are set and an optional
  //   action is performed.  See the following action methods...
  //
  virtual void ReportError(const char *Message);

  // SetErrorFlags() - Calling this function instructs the error flags to be
  //   set on an error.  Note, the flags are always set, even when not in this
  //   mode.  This just turns off the other modes.
  //
  inline void SetErrorFlags() {
    ErrorMethod = ERR_SET_FLAGS;
  }

  // PrintErrors() - Calling this function instructs the error message to be
  //   printed to standard output.
  //
  inline void PrintErrors() {
    ErrorMethod = ERR_STDOUT;
  }

  // PrintErrErrors() - Calling this function instructs the error message to be
  //   printed to standard error.
  //
  inline void PrintErrErrors() {
    ErrorMethod = ERR_STDERR;
  }

  // AssertErrors() - Calling this function instructs an assert to be triggered
  //   on each error.
  //
  inline void AssertErrors() {
    ErrorMethod = ERR_ASSERT;
  }

#ifdef EXCEPTIONS
  // ThrowErrors() - Calling this function instructs an exception to be thrown
  //   on each error.
  //
  inline void ThrowErrors() {
    ErrorMethod = ERR_EXCEPTION;
  }
#endif

  //=-------------------------------------------------------------------------=
  //               Insertion/Extraction Operator Definitions
  //=-------------------------------------------------------------------------=

  // Insertion Operator Definitions...  These allow you to use the
  //   <<operator to insert data into a file.
  //
  OP_IN(signed char   , SINT);   // Handle insertion of chars
  OP_IN(signed short  , SINT);   // Handle insertion of shorts
  OP_IN(signed int    , SINT);   // Handle insertion of ints
  OP_IN(signed long   , SINT);   // Handle insertion of longs
  OP_IN(unsigned char , UINT);   // Handle insertion of unsigned chars
  OP_IN(unsigned short, UINT);   // Handle insertion of unsigned shorts
  OP_IN(unsigned int  , UINT);   // Handle insertion of unsigned ints
  OP_IN(unsigned long , UINT);   // Handle insertion of unsigned longs

  OP_IN(float         , FLOAT);  // Handle insertion of floats
  OP_IN(double        , DOUBLE); // Handle insertion of doubles
  OP_IN(long double   , LNGDBL); // Handle insertion of long doubles

                                 // Handle insertion of unsigned char *'s
  inline Serialize &operator <<(const unsigned char *i) {
    return operator <<((const char *)i); 
  }

  // Use a special case for char* // Handle insertion of char *'s
  Serialize &operator <<(const char *P);

  // Extraction Operator Definitions...  These allow you to use the
  //   >>operator to extract data from a file
  //
  OP_EX(signed char   , SINT);   // Handle extraction of chars
  OP_EX(signed short  , SINT);   // Handle extraction of shorts
  OP_EX(signed int    , SINT);   // Handle extraction of ints
  OP_EX(signed long   , SINT);   // Handle extraction of longs
  OP_EX(unsigned char , UINT);   // Handle extraction of unsigned chars
  OP_EX(unsigned short, UINT);   // Handle extraction of unsigned shorts
  OP_EX(unsigned int  , UINT);   // Handle extraction of unsigned ints
  OP_EX(unsigned long , UINT);   // Handle extraction of unsigned longs

  OP_EX(float         , FLOAT);  // Handle extraction of floats
  OP_EX(double        , DOUBLE); // Handle extraction of doubles
  OP_EX(long double   , LNGDBL); // Handle extraction of long doubles

                                 // Handle extraction of unsigned char *'s
  inline Serialize& operator>>(unsigned char * &i) {
    return operator>>((char * &)i); 
  }

  // Use a special case for char* // Handle extraction of char *'s
  Serialize &operator >>(char *i);

private :  // Private member functions
  // Protect copy constructor and assignment operator so that this object
  //   can't accidentally be passed by value.
  Serialize(Serialize &) {}
  Serialize &operator=(Serialize &) {
    return *this;
  }

  //=-------------------------------------------------------------------------=
  //                           Protected Internals
  //=-------------------------------------------------------------------------=

protected :
  void *F;                        // The file stream used... Which is really of 
                                  //   type FILE.
  signed char ErrorFlag,          // Error?
              ErrorMethod,        // How are we reporting errors?
              IsReading,          // Reading = 1, Writing = 0, Closed = -1
              AtSection;          // At the end of a section, call NextSection

  Serialize &Write(unsigned char, const void *, unsigned int);
  Serialize &Read (unsigned char,       void *, unsigned int);

  enum ErrorTypes {
    ERR_SET_FLAGS = 0, ERR_STDOUT, ERR_STDERR, ERR_ASSERT

    #ifdef EXCEPTIONS
      , ERR_EXCEPTION
    #endif
  };
};

#undef OP_IN
#undef OP_EX

#endif
