/**
 * @file tobjstrm.h
 *
 * Turbo Vision - Version 2.0
 *
 * Copyright (c) 1994 by Borland International
 * All Rights Reserved.
 *
 * Modified by Sergio Sigala <sergio@sigala.it>
 * Modified by Max Okumoto <okumoto@ucsd.edu>
 */

#include <ios>
#include <iostream>
#include <fstream>

#include <inttypes.h>
#include <limits.h>

/**
 * Undocumented.
 */
typedef unsigned P_id_type;

/* ------------------------------------------------------------------------*/
/*   class TStreamable                                                     */
/*                                                                         */
/*   This is the base class for all storable objects.  It provides         */
/*   three member functions, streamableName(), read(), and write(), which  */
/*   must be overridden in every derived class.                            */
/* ------------------------------------------------------------------------*/

#if !defined( __fLink_def )
#define __fLink_def
/**
 * Internal structure.
 * @short Internal structure
 */
struct fLink
{
    /**
     * Undocumented.
     */
    fLink *f;
    /**
     * Undocumented.
     */
    class TStreamableClass *t;
};
#endif

#define __link( s )             \
  extern TStreamableClass s;    \
  static fLink force ## s =     \
    { (fLink *)&force ## s, (TStreamableClass *)&s };

#if defined( Uses_TStreamable ) && !defined( __TStreamable )
#define __TStreamable

/**
 * Gives the streamable property to a class.
 *
 * @ref TView has two base classes, @ref TObject and the abstract class
 * TStreamable. All the viewable classes, derived ultimately from @ref TView,
 * therefore also inherit from TStreamable.
 *
 * Several non-view classes, such as @ref TCollection, @ref TStrListMaker and
 * @ref TStringList, also have TStreamable as a base class. Such classes are
 * known as streamable, meaning that their objects can be written to and read
 * from streams using the TVision stream manager.
 *
 * If you want to develop your own streamable classes, make sure that
 * TStreamable is somewhere in their ancestry. Using an existing streamable
 * class as a base class, of course, is an obvious way of achieving this.
 *
 * Since TStreamable is an abstract class, no objects of this class can be
 * instantiated. Before a streamable class can be used with streams, the class
 * must override the three pure virtual functions @ref streamableName(),
 * @ref read(), and @ref write().
 * @short Gives the streamable property to a class
 */
class TStreamable
{
    friend class opstream;
    friend class ipstream;
private:
    /**
     * Class TStreamable has no constructor. This function must be overridden
     * (or redeclared as pure virtual) by every derived class. Its purpose is
     * to return the name of the streamable class of the object that invokes
     * it.
     *
     * This name is used in the registering of streams by the stream manager.
     * The name returned must be a unique, 0-terminated string, so the safest
     * strategy is to use the name of the streamable class.
     */
    virtual const char *streamableName() const = 0;
protected:
    /**
     * This pure virtual function must be overridden (or redeclared as pure
     * virtual) in every derived class. The overriding read() function for
     * each streamable class must read the necessary data members from the
     * @ref ipstream object `is'. read() is usually implemented by calling
     * the base class's read() (if any), then extracting any additional data
     * members for the derived class.
     */
    virtual void *read( ipstream& is ) = 0;
    /**
     * This pure virtual function must be overridden (or redeclared as pure
     * virtual) in every derived class. The overriding write() function for
     * each streamable class must write the necessary data members to the
     * @ref opstream object `os'. write() is usually implemented by calling
     * the base class's write() (if any), then inserting any additional data
     * members for the derived class.
     */
    virtual void write( opstream& os ) = 0;
};

#endif  // Uses_TStreamable

/* ------------------------------------------------------------------------*/
/*   class TStreamableClass                                                */
/* ------------------------------------------------------------------------*/

#if defined( Uses_TStreamableClass ) && !defined( __TStreamableClass )
#define __TStreamableClass

/**
 * Undocumented.
 */
const P_id_type P_id_notFound = UINT_MAX;

/** \enum BUILDER
 * Each streamable class has a builder function of type BUILDER. The builder
 * provides raw memory of the correct size and initializes the virtual table
 * pointers when objects are created by certain stream read operations.
 * The read() function of the streamable class reads data from the stream into
 * the raw object provided by the builder.
 * @see TStreamable::read
 */
typedef TStreamable *(*BUILDER)();

#define __DELTA( d ) ((intptr_t)(TStreamable*)(d*)1-1 )

/**
 * TStreamableClass is used by @ref TStreamableTypes and @ref pstream in the
 * registration of streamable classes.
 * @short TStreamableClass is used by TStreamableTypes and pstream in the
 * registration of streamable classes
 */
class TStreamableClass
{
    friend class TStreamableTypes;
    friend class opstream;
    friend class ipstream;
public:
    /**
     * Creates a TStreamable object with the given name and the given builder
     * function, then calls @ref pstream::registerTypes().
     *
     * Each streamable class TClassName has a build member function. There are
     * also the familiar non-member overloaded >> and << operators for stream
     * I/O associated with each streamable class.
     *
     * For type-safe object-stream I/O, the stream manager needs to access the
     * names and the type information for each class.
     *
     * To ensure that the appropriate functions are linked into any
     * application using the stream manager, you must provide an extern
     * reference such as this:
     *
     * <pre>
     * extern TStreamableClass registerTClassName;
     * </pre>
     *
     * where TClassName is the name of the class for which objects need to be
     * streamed. @ref BUILDER is typedefed as follows:
     *
     * <pre>
     * typedef TStreamable *(*BUILDER)();
     * </pre>
     */
    TStreamableClass(const char *aName, BUILDER aBuild, intptr_t aDelta );
private:
    const char *name;
    BUILDER build;
    intptr_t delta;
};

#endif  // Uses_TStreamableClass

/* ------------------------------------------------------------------------*/
/*   class TStreamableTypes                                                */
/* ------------------------------------------------------------------------*/

#if defined( Uses_TStreamableTypes ) && !defined( __TStreamableTypes )
#define __TStreamableTypes

/**
 * TStreamableTypes, derived privately from @ref TNSSortedCollection,
 * maintains a database of all registered streamable types used in an
 * application.
 *
 * Classes @ref opstream and @ref ipstream use this database to determine the
 * correct @ref read() and @ref write() functions for particular objects.
 * Because of the private derivation, all the inherited members are private
 * within TStreamableTypes.
 * @short Maintains a database of all registered streamable types used in an
 * application
 */
class TStreamableTypes : private TNSSortedCollection
{
public:
    /**
     * Calls the base @ref TNSSortedCollection constructor to create a
     * TStreamableTypes collection.
     * @see TNSSortedCollection::TNSSortedCollection
     */
    TStreamableTypes();
    /**
     * Sets the collection @ref limit to 0 without destroying the collection
     * (since the @ref shouldDelete data member is set to False).
     */
    ~TStreamableTypes();
    /**
     * Registers the argument class by inserting `d' in the collection.
     */
    void registerType(const TStreamableClass *d );
    /**
     * Returns a pointer to the class in the collection corresponding to the
     * argument `name', or returns 0 if no match.
     */
    const TStreamableClass *lookup(const char name[]);
private:
    /**
     * Undocumented.
     */
    virtual void *keyOf( void * );
    /**
     * Undocumented.
     */
    int compare( void *, void * );
};

#endif  // Uses_TStreamableTypes

/* ------------------------------------------------------------------------*/
/*   class TPWrittenObjects                                                */
/* ------------------------------------------------------------------------*/

#if defined( Uses_TPWrittenObjects ) && !defined( __TPWrittenObjects )
#define __TPWrittenObjects

/**
 * TPWrittenObjects maintains a database of all objects that have been written
 * to the current object stream. This is used by @ref opstream when it writes a
 * pointer onto a stream.
 * @short Maintains a database of all objects that have been written to the
 * current object stream
 */
class TPWrittenObjects : public TNSSortedCollection
{
    friend class opstream;
public:
    /**
     * Undocumented.
     */
    void removeAll() { curId = 0; TNSSortedCollection::freeAll(); }
private:
    /**
     * This private constructor creates a non-streamable collection by calling
     * the base @ref TNSSortedCollection constructor. It is accessible only
     * by the member functions and friends.
     */
    TPWrittenObjects();
    /**
     * Sets the collection @ref limit to 0 without destroying the collection.
     */
    ~TPWrittenObjects();
    /**
     * Undocumented.
     */
    void registerObject(const void *adr );
    /**
     * Undocumented.
     */
    P_id_type find(const void *adr );
    /**
     * Undocumented.
     */
    void *keyOf( void * );
    /**
     * Undocumented.
     */
    int compare( void *, void * );
    /**
     * Undocumented.
     */
    P_id_type curId;
};

/* ------------------------------------------------------------------------*/
/*   class TPWObj                                                          */
/* ------------------------------------------------------------------------*/

/**
 * TPWObj is used internally by @ref TPWrittenObjects. The class
 * TPWrittenObjects is a friend of TPWobj, so all its member functions can
 * access the private members of TPWObj.
 * @short Used internally by TPWrittenObjects
 */
class TPWObj
{
    friend class TPWrittenObjects;
private:
    TPWObj(const void *adr, P_id_type id );
    const void *address;
    P_id_type ident;
};

#endif  // Uses_TPWrittenObjects

/* ------------------------------------------------------------------------*/
/*   class TPReadObjects                                                   */
/* ------------------------------------------------------------------------*/

#if defined( Uses_TPReadObjects ) && !defined( __TPReadObjects )
#define __TPReadObjects

/**
 * TPReadObjects maintains a database of all objects that have been read from
 * the current object stream. This is used by @ref ipstream when it reads a
 * pointer from a stream to determine if other addresses for that objects
 * exist.
 * @short Maintains a database of all objects that have been read from the
 * current object stream
 */
class TPReadObjects : public TNSCollection
{
    friend class ipstream;
public:
    /**
     * Undocumented.
     */
    void removeAll() { curId = 0; TNSCollection::removeAll(); }
private:
    /**
     * This private constructor creates a non-streamable collection by calling
     * the base @ref TNSCollection constructor. It is accessible only by
     * member functions and friends.
     * @see TNSCollection::TNSCollection
     */
    TPReadObjects();
    /**
     * Sets the collection @ref limit to 0 without destroying the collection.
     */
    ~TPReadObjects();
    void registerObject(const void *adr );
    const void *find( P_id_type id );
    P_id_type curId;
};

#endif  // Uses_TPReadObjects

/* ------------------------------------------------------------------------*/
/*   class pstream                                                         */
/* ------------------------------------------------------------------------*/

#if defined( Uses_pstream ) && !defined( __pstream )
#define __pstream

class TStreamableTypes;

/**
 * pstream is the base class for handling streamable objects.
 * @short The base class for handling streamable objects
 */
class pstream {
protected:
    /**
     * Pointer to the @ref TStreamableTypes data base of all registered types
     * in this application.
     */
    static TStreamableTypes *types;

public:
    pstream();
    virtual ~pstream();

    /**
     * Undocumented.
     */
    enum StreamableError { peNotRegistered, peInvalidType };

    /**
     * Undocumented.
     */
    enum PointerTypes { ptNull, ptIndexed, ptObject };

    void error(StreamableError);
    void error(StreamableError, const TStreamable &);

    static void initTypes();
    static void registerType(TStreamableClass *ts);
};

#endif  // Uses_pstream

/* ------------------------------------------------------------------------*/
/*   class ipstream                                                        */
/* ------------------------------------------------------------------------*/

#if defined( Uses_ipstream ) && !defined( __ipstream )
#define __ipstream

class TStreamableClass;

/**
 * ipstream, a specialized input stream derivative of @ref pstream, is the
 * base class for reading (extracting) streamable objects. ipstream is
 * analogous to istream, defined in `iostream' for the standard C++ stream
 * library. ipstream is a friend class of @ref TPReadObjects.
 *
 * The overloaded operators >> extract (read) from the given ipstream object
 * to the given argument. A reference to the stream is returned, allowing you
 * to chain >> operations in the usual way. The data type of the argument
 * determines how the read is performed. For example, reading a signed char
 * is implemented using @ref readByte().
 *
 * @see opstream
 *
 * @short The base class for reading (extracting) streamable objects from
 * streams
 */
class ipstream : virtual public pstream, public std::istream {
private:
    TPReadObjects objs;

protected:
    const TStreamableClass *readPrefix();
    void *readData(const TStreamableClass *c, TStreamable *mem);
    void readSuffix();
    const void	*find(P_id_type id);
    void registerObject(const void *adr);

    ushort readWord();
    ulong readLong();

public:
    ipstream(std::streambuf *buf);
    ~ipstream();

    ipstream &seekg(std::streampos pos);
    ipstream &seekg(std::streamoff off, std::ios::seekdir dir);

    uchar readByte();
    void readBytes(void *data, std::streamsize sz);
    char *readString();
    char *readString(char *buf, unsigned maxLen);

    ipstream &operator>>(char &);
    ipstream &operator>>(signed char &);
    ipstream &operator>>(unsigned char &);
    ipstream &operator>>(signed short &);
    ipstream &operator>>(unsigned short &);
    ipstream &operator>>(signed int &);
    ipstream &operator>>(unsigned int &);
    ipstream &operator>>(signed long &);
    ipstream &operator>>(unsigned long &);
    ipstream &operator>>(float &);
    ipstream &operator>>(double &);
    ipstream &operator>>(long double &);
    ipstream &operator>>(TStreamable &);
    ipstream &operator>>(void *&);
};

#endif  // Uses_ipstream

/* ------------------------------------------------------------------------*/
/*   class opstream                                                        */
/* ------------------------------------------------------------------------*/

#if defined( Uses_opstream ) && !defined( __opstream )
#define __opstream

class TStreamableClass;

/**
 * opstream, a specialized output stream derivative of @ref pstream, is the
 * base class for writing (inserting) streamable objects. opstream is
 * analogous to ostream, defined in `iostream' for the standard C++ stream
 * library. opstream is a friend class of @ref TPWrittenObjects.
 *
 * The overloaded operators << insert (write) the given argument to the given
 * opstream object. A reference to the stream is returned, allowing you
 * to chain << operations in the usual way. The data type of the argument
 * determines the form of write operation employed. For example, writing a
 * signed char is implemented using @ref writeByte().
 *
 * @see ipstream
 *
 * @short The base class for writing (inserting) streamable objects into
 * streams
 */
class opstream : virtual public pstream, public std::ostream {
    TPWrittenObjects objs;

protected:
    void writePrefix(const TStreamable &);
    void writeData(TStreamable &);
    void writeSuffix(const TStreamable &);
    P_id_type find(const void *adr);
    void registerObject(const void *adr);

public:
    opstream(std::streambuf *buf);
    ~opstream();
    opstream& seekp(std::streampos pos);
    opstream& seekp(std::streamoff off, std::ios::seekdir dir);
    opstream& flush();

    void writeByte(uchar ch);
    void writeBytes(const void *data, std::streamsize sz);
    void writeWord(ushort us);
    void writeString(const char *str);

    opstream &operator<<(char);
    opstream &operator<<(signed char);
    opstream &operator<<(unsigned char);
    opstream &operator<<(signed short);
    opstream &operator<<(unsigned short);
    opstream &operator<<(signed int);
    opstream &operator<<(unsigned int);
    opstream &operator<<(signed long);
    opstream &operator<<(unsigned long);
    opstream &operator<<(float);
    opstream &operator<<(double);
    opstream &operator<<(long double);
    opstream &operator<<(TStreamable &);
    opstream &operator<<(TStreamable *);
};

#endif  // Uses_opstream

/* ------------------------------------------------------------------------*/
/*   class iopstream                                                       */
/* ------------------------------------------------------------------------*/

#if defined( Uses_iopstream ) && !defined( __iopstream )
#define __iopstream

/**
 * Class iopstream is a simple "mix" of its bases, @ref opstream and
 * @ref ipstream. It provides the base class for simultaneous writing and
 * reading streamable objects.
 *
 * @short The base class for simultaneous writing and reading streamable
 * objects to and from streams
 */
class iopstream : public ipstream, public opstream {
public:
    iopstream(std::streambuf *buf);
    ~iopstream();
};

#endif  // Uses_iopstream

/* ------------------------------------------------------------------------*/
/*   class ifpstream                                                       */
/* ------------------------------------------------------------------------*/

#if defined( Uses_ifpstream ) && !defined( __ifpstream )
#define __ifpstream

/**
 * ifpstream provides the base class for reading (extracting)
 * streamable objects from file streams.
 *
 * @short The base class for reading (extracting) streamable objects
 * from file streams.
 */
class ifpstream : public ipstream {
    std::filebuf	buf;
public:
    ifpstream();
    ifpstream(const char name[], std::ios::openmode omode = std::ios::in);
    ~ifpstream();
    void open(const char name[], std::ios::openmode omode = std::ios::in);
};

#endif  // Uses_ifpstream

/* ------------------------------------------------------------------------*/
/*   class ofpstream                                                       */
/* ------------------------------------------------------------------------*/

#if defined( Uses_ofpstream ) && !defined( __ofpstream )
#define __ofpstream

/**
 * ofpstream provides the base class for writing (inserting)
 * streamable objects to file streams.
 *
 * @short The base class for writing (inserting) streamable objects
 * to file streams.
 */
class ofpstream : public opstream {
    std::filebuf	buf;
public:
    ofpstream();
    ofpstream(const char name[], std::ios::openmode omode = std::ios::out);
    ~ofpstream();
    void open(const char name[], std::ios::openmode omode = std::ios::out);
};

#endif  // Uses_ofpstream

/* ------------------------------------------------------------------------*/
/*   class fpstream                                                        */
/* ------------------------------------------------------------------------*/

#if defined( Uses_fpstream ) && !defined( __fpstream )
#define __fpstream

/**
 * fpstream provides the base class for simultaneous writing and
 * reading streamable objects to bidirectional file streams.
 *
 * @short The base class for simultaneous writing and reading
 * streamable objects to bidirectional file streams.
 */
class fpstream : public iopstream {
    std::filebuf	buf;
public:
    fpstream();
    fpstream(const char name[], std::ios::openmode omode);
    ~fpstream();
    void open(const char name[], std::ios::openmode omode);
};

#endif  // Uses_fpstream
