//------------------------------------------------------------------------------
// emRec.h - Recordable data structures
//
// Copyright (C) 2005-2010,2012,2014,2016,2018,2022 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emRec_h
#define emRec_h

#ifndef emColor_h
#include <emCore/emColor.h>
#endif

class emRecReader;
class emRecWriter;
class emStructRec;

class emRecNode : public emUncopyable {
public:
        virtual ~emRecNode();
private:
        friend class emRec;
        friend class emRecListener;
        virtual bool IsListener() const = 0;
        virtual void ChildChanged() = 0;
        emRecNode * UpperNode;
};


//==============================================================================
//=================================== emRec ====================================
//==============================================================================

class emRec : public emRecNode {

public:

        // This is the abstract base class for all record classes. The record
        // classes can be used to design data structures whose contents can be
        // converted to and from ASCII text. A typical example is the definition
        // of a configuration file format. It takes just a few lines of
        // programming to define a new file format and a structured C++
        // interface with fastest get-methods and operators on the data fields.
        // A disadvantage is that the records may take round about ten times
        // more memory than a raw C data structure would take. Here comes a
        // tiny example:
        //
        // Assumed our data structure would look like this in simple C:
        //
        //   struct Person {
        //     char Name[64];
        //     int Age;
        //     unsigned Male : 1;
        //   };
        //
        //   struct Person Persons[2];
        //
        // With the record classes, it looks like this:
        //
        //   class Person : public emStructRec {
        //   public:
        //     Person();
        //     emStringRec Name;
        //     emIntRec Age;
        //     emBoolRec Male;
        //   };
        //
        //   Person::Person() :
        //     emStructRec(),
        //     Name(this,"name"),
        //     Age(this,"age"),
        //     Male(this,"male")
        //   {}
        //
        //   emTArrayRec<Person> Persons;
        //
        // This fills the data structure with some information and writes it to
        // a file:
        //
        //   int main(int argc, char * argv[])
        //   {
        //     Persons.SetCount(2);
        //     Persons[0].Name="Fred";
        //     Persons[0].Age=12;
        //     Persons[0].Male=true;
        //     Persons[1].Name="Clara";
        //     Persons[1].Age=11;
        //     Persons[1].Male=false;
        //     Persons.TrySave("test.rec");
        //   }
        //
        // Here is the resulting contents of the file test.rec:
        //
        //   {
        //     name = "Fred"
        //     age = 12
        //     male = yes
        //   }
        //   {
        //     name = "Clara"
        //     age = 11
        //     male = no
        //   }


        emRec();
                // Construct this record without being a member of an
                // emStructRec.

        emRec(emStructRec * parent, const char * varIdentifier);
                // Construct this record as a member of an emStructRec.
                // Arguments:
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.

        virtual ~emRec();
                // Destructor.

        emRec * GetParent();
        const emRec * GetParent() const;
                // Get the parent record. Returns NULL if this is a root record.
                // (This is not super-fast, because the chain of listeners is
                // walked to find the parent).

        emRec * GetRoot();
        const emRec * GetRoot() const;
                // Get the root record (this is not super-fast).

        virtual const char * GetFormatName() const;
                // If this record is the root of records defining a file format,
                // this method should be overloaded and it should return a handy
                // name for the file format. Thereby, emRecWriter and
                // emRecReader are writing and checking a file format magic at
                // the beginning of the file. When reading an empty file, the
                // missing file format magic does not lead to an error. The
                // default implementation returns NULL which means to have no
                // file format magic.

        void TryLoad(const emString & filePath);
        void TrySave(const emString & filePath);
                // Load or save this tree of records from or to a file.
                // Arguments:
                //   filePath - Path/name of the file.

        void TryLoadFromMem(const char * buf, int len);
        void TryLoadFromMem(const emArray<char> & buf);
        void SaveToMem(emArray<char> & buf);
                // Load or save this tree of records from or to memory.
                // SaveToMem adds the data to the array without emptying it
                // first.

        void TryCopy(emRec & source);
        void Copy(emRec & source);
                // Copy the given tree of records to this tree of records via
                // the SaveToMem and TryLoadFromMem methods. This is slow and
                // works only if the records are compatible. The second version
                // calls emFatalError on error.

        virtual void SetToDefault() = 0;
                // Set this record to default state. When reading an
                // emStructRec, omitted members are set to their defaults.

        virtual bool IsSetToDefault() const = 0;
                // Ask whether this record is set to its default state. When
                // writing an emStructRec, members with default state may be
                // omitted.

        virtual void TryStartReading(emRecReader & reader) = 0;
        virtual bool TryContinueReading(emRecReader & reader) = 0;
        virtual void QuitReading() = 0;
        virtual void TryStartWriting(emRecWriter & writer) = 0;
        virtual bool TryContinueWriting(emRecWriter & writer) = 0;
        virtual void QuitWriting() = 0;
                // This is only to be called by emRecReader/Writer and through
                // recursion in the tree. Try to read or write this tree of
                // records via the given reader or writer. First, the start
                // method is called, and then the continue method is called
                // again and again until it returns true. The quit method is
                // called at the end, even on error or when aborting. The quit
                // mechanism should be quite stable, because with
                // emRecFileModel, it could happen that quit is called in
                // response to OnChanged through a user modification.

        virtual emUInt64 CalcRecMemNeed() const = 0;
                // Calculate best known number of bytes, which are allocated for
                // this record, or which will be allocated after reading has
                // completed.

        static void CheckIdentifier(const char * identifier);
                // "identifiers" are names of variables, enumeration variants
                // and so on. The syntactical rules are the same as with
                // identifiers in the C programming language: begin with a
                // letter or underscore and continue with letter, underscore or
                // digit. But they are not case sensitive. This functions checks
                // whether the given string is a valid identifier. If not,
                // emFatalError is called.

protected:

        void Changed();
                // This must be called by derived classes after each change of
                // the record, even through TryStartReading and
                // TryContinueReading, but never through constructors or
                // destructors. It informs the listeners of all the records on
                // the path from this record up to the root record.

        void BeTheParentOf(emRec * child);
                // This makes this record the parent of the given child. It is
                // called by dynamic containers like emUnionRec and emArrayRec
                // on creation of children.

private:
        virtual bool IsListener() const;
        virtual void ChildChanged();
};

inline emRec::emRec()
{
        UpperNode=NULL;
}

inline void emRec::Changed()
{
        if (UpperNode) UpperNode->ChildChanged();
}


//==============================================================================
//=============================== emRecListener ================================
//==============================================================================

class emRecListener : public emRecNode {

public:

        // Abstract base class for a listener on a record and it descendants.

        emRecListener(emRec * rec=NULL);
        virtual ~emRecListener();

        const emRec * GetListenedRec() const;
        emRec * GetListenedRec();
        void SetListenedRec(emRec * rec);
                // Get/set the record to be listened. NULL means not to listen
                // any record. On deletion of the listened record, this is
                // automatically set to NULL.

protected:

        virtual void OnRecChanged() = 0;
                // Called on each change of the record and its descendants. This
                // is a synchronous call - the implementation must not modify
                // the records or the listeners.

private:
        virtual bool IsListener() const;
        virtual void ChildChanged();
        emRec * Rec;
};

inline const emRec * emRecListener::GetListenedRec() const
{
        return Rec;
}

inline emRec * emRecListener::GetListenedRec()
{
        return Rec;
}


//==============================================================================
//=============================== emRecAllocator ===============================
//==============================================================================

typedef emRec * (* emRecAllocator)();
        // Data type for a pointer to function which can allocate a new record.
        // This is used for dynamic containers like emUnionRec and emArrayRec.

#define EM_DEFAULT_REC_ALLOCATOR(REC) (&emDfltRecAllocImp<REC >::Allocate)
        // This macro expands to an emRecAllocator which allocates a record of
        // type REC with its default constructor.

// private stuff for the macro above.
template <class REC> class emDfltRecAllocImp : public emUnconstructable {
public:
        static emRec * Allocate() { return new REC(); }
};


//==============================================================================
//================================= emBoolRec ==================================
//==============================================================================

class emBoolRec : public emRec {

public:

        // Record class for a boolean value.

        emBoolRec(bool defaultValue=false);
        emBoolRec(emStructRec * parent, const char * varIdentifier,
                  bool defaultValue=false);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this boolean record.

        bool Get() const;
        operator bool () const;
                // Get the boolean value.

        void Set(bool value);
        emBoolRec & operator = (bool value);
                // Set the boolean value.

        void Invert();
                // Invert the boolean value.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:
        bool DefaultValue, Value;
};

inline bool emBoolRec::Get() const
{
        return Value;
}

inline emBoolRec::operator bool () const
{
        return Value;
}

inline emBoolRec & emBoolRec::operator = (bool value)
{
        Set(value);
        return *this;
}


//==============================================================================
//================================== emIntRec ==================================
//==============================================================================

class emIntRec : public emRec {

public:

        // Record class for an integer value.

        emIntRec(int defaultValue=0, int minValue=INT_MIN,
                 int maxValue=INT_MAX);
        emIntRec(emStructRec * parent, const char * varIdentifier,
                 int defaultValue=0, int minValue=INT_MIN,
                 int maxValue=INT_MAX);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this integer record.
                //   minValue      - The value will never be less than this.
                //   maxValue      - The value will never be greater than this.

        int Get() const;
        operator int () const;
                // Get the integer value.

        void Set(int value);
        emIntRec & operator = (int value);
                // Set the integer value. It is clipped if it would be less than
                // the minimum value or greater than the maximum value.

        int GetMinValue() const;
        int GetMaxValue() const;
                // Get minimum and maximum values.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:
        int DefaultValue, MinValue, MaxValue, Value;
};

inline int emIntRec::Get() const
{
        return Value;
}

inline emIntRec::operator int () const
{
        return Value;
}

inline emIntRec & emIntRec::operator = (int value)
{
        Set(value);
        return *this;
}

inline int emIntRec::GetMinValue() const
{
        return MinValue;
}

inline int emIntRec::GetMaxValue() const
{
        return MaxValue;
}


//==============================================================================
//================================ emDoubleRec =================================
//==============================================================================

class emDoubleRec : public emRec {

public:

        // Record class for a double value.

        emDoubleRec(double defaultValue=0.0, double minValue=-3.4E+38,
                    double maxValue=3.4E+38);
        emDoubleRec(emStructRec * parent, const char * varIdentifier,
                    double defaultValue=0.0, double minValue=-3.4E+38,
                    double maxValue=3.4E+38);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this double record.
                //   minValue      - The value will never be less than this.
                //   maxValue      - The value will never be greater than this.

        virtual ~emDoubleRec();
                // Destructor.

        double Get() const;
        operator double () const;
                // Get the value.

        void Set(double value);
        emDoubleRec & operator = (double value);
                // Set the double value. It is clipped if it would be less than
                // the minimum or greater than the maximum.

        double GetMinValue() const;
        double GetMaxValue() const;
                // Get minimum and maximum values.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:
        double DefaultValue, MinValue, MaxValue, Value;
};

inline double emDoubleRec::Get() const
{
        return Value;
}

inline emDoubleRec::operator double () const
{
        return Value;
}

inline emDoubleRec & emDoubleRec::operator = (double value)
{
        Set(value);
        return *this;
}

inline double emDoubleRec::GetMinValue() const
{
        return MinValue;
}

inline double emDoubleRec::GetMaxValue() const
{
        return MaxValue;
}


//==============================================================================
//================================= emEnumRec ==================================
//==============================================================================

class emEnumRec : public emRec {

public:

        // Record class for an enumeration value. It is like emIntRec, but each
        // possible value is given an identifier.

        emEnumRec(int defaultValue, const char * identifier0, ...);
        emEnumRec(emStructRec * parent, const char * varIdentifier,
                  int defaultValue, const char * identifier0, ...);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this enumeration
                //                   record.
                //   identifier0   - The identifier for value=0. The identifier
                //                   is not copied - the pointer must be valid
                //                   for the life time of this record.
                //   ...           - Any number of further identifiers for
                //                   values 1, 2, 3 and so on, terminated by a
                //                   NULL.

        virtual ~emEnumRec();
                // Destructor.

        int Get() const;
        operator int () const;
                // Get the value.

        void Set(int value);
        emEnumRec & operator = (int value);
                // Set the value. It is clipped if out of range.

        const char * GetIdentifier() const;
                // Get the identifier for the current value.

        int GetIdentifierCount() const;
                // Get number of possible values.

        const char * GetIdentifierOf(int value) const;
                // Get the identifier for the given value. Returns NULL if the
                // value is out of range.

        int GetValueOf(const char * identifier) const;
                // Get the value for the given identifier. Returns -1 if there
                // is no such identifier.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:

        void Init(int defaultValue, const char * identifier0, va_list args);

        const char * * Identifiers;
        int IdentifierCount, DefaultValue, Value;
};

inline int emEnumRec::Get() const
{
        return Value;
}

inline emEnumRec::operator int () const
{
        return Value;
}

inline emEnumRec & emEnumRec::operator = (int value)
{
        Set(value);
        return *this;
}

inline const char * emEnumRec::GetIdentifier() const
{
        return Identifiers[Value];
}

inline int emEnumRec::GetIdentifierCount() const
{
        return IdentifierCount;
}


//==============================================================================
//================================= emFlagsRec =================================
//==============================================================================

class emFlagsRec : public emRec {

public:

        // Record class for a set of flags. It is a bit mask where each possible
        // bit is given an identifier.

        emFlagsRec(int defaultValue, const char * identifier0, ...);
        emFlagsRec(emStructRec * parent, const char * varIdentifier,
                  int defaultValue, const char * identifier0, ...);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this flags
                //                   record.
                //   identifier0   - The identifier for bit 0. The identifier
                //                   is not copied - the pointer must be valid
                //                   for the life time of this record.
                //   ...           - Any number of further identifiers for
                //                   bits 1, 2, 3, ... 31, terminated by a
                //                   NULL.

        virtual ~emFlagsRec();
                // Destructor.

        int Get() const;
        operator int () const;
                // Get the value. It's the bit mask.

        void Set(int value);
        emFlagsRec & operator = (int value);
                // Set the value. Undefined flag bits are set to zero.

        int GetIdentifierCount() const;
                // Get number of possible flag bits.

        const char * GetIdentifierOf(int bit) const;
                // Get the identifier for the given flag bit (0...31). Returns
                // NULL when no identifier has been defined for the bit.

        int GetBitOf(const char * identifier) const;
                // Get the flag bit for the given identifier. Returns -1 if
                // there is no such identifier.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:

        void Init(int defaultValue, const char * identifier0, va_list args);

        const char * * Identifiers;
        int IdentifierCount, DefaultValue, Value;
};

inline int emFlagsRec::Get() const
{
        return Value;
}

inline emFlagsRec::operator int () const
{
        return Value;
}

inline emFlagsRec & emFlagsRec::operator = (int value)
{
        Set(value);
        return *this;
}

inline int emFlagsRec::GetIdentifierCount() const
{
        return IdentifierCount;
}


//==============================================================================
//=============================== emAlignmentRec ===============================
//==============================================================================

class emAlignmentRec : public emRec {

public:

        // Record class for an emAlignment value.

        emAlignmentRec(emAlignment defaultValue=EM_ALIGN_CENTER);
        emAlignmentRec(emStructRec * parent, const char * varIdentifier,
                       emAlignment defaultValue=EM_ALIGN_CENTER);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this record.

        virtual ~emAlignmentRec();
                // Destructor.

        emAlignment Get() const;
        operator emAlignment () const;
                // Get the alignment value.

        void Set(emAlignment value);
        emAlignmentRec & operator = (emAlignment value);
                // Set the alignment value.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:

        emAlignment DefaultValue, Value;
};

inline emAlignment emAlignmentRec::Get() const
{
        return Value;
}

inline emAlignmentRec::operator emAlignment () const
{
        return Value;
}

inline emAlignmentRec & emAlignmentRec::operator = (emAlignment value)
{
        Set(value);
        return *this;
}


//==============================================================================
//================================ emStringRec =================================
//==============================================================================

class emStringRec : public emRec {

public:

        // Record class for an emString value.

        emStringRec(const emString & defaultValue=emString());
        emStringRec(emStructRec * parent, const char * varIdentifier,
                    const emString & defaultValue=emString());
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this string record.

        virtual ~emStringRec();
                // Destructor.

        const emString & Get() const;
        operator const emString & () const;
                // Get the string value.

        void Set(const emString & value);
        emStringRec & operator = (const emString & value);
                // Set the string value.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:
        emString DefaultValue, Value;
};

inline const emString & emStringRec::Get() const
{
        return Value;
}

inline emStringRec::operator const emString & () const
{
        return Value;
}

inline emStringRec & emStringRec::operator = (const emString & value)
{
        Set(value);
        return *this;
}


//==============================================================================
//================================= emColorRec =================================
//==============================================================================

class emColorRec : public emRec {

public:

        // Record class for an emColor value.

        emColorRec(emColor defaultValue=emColor(0,0,0,255),
                   bool haveAlpha=false);
        emColorRec(emStructRec * parent, const char * varIdentifier,
                   emColor defaultValue=emColor(0,0,0,255),
                   bool haveAlpha=false);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   defaultValue  - The default value for this color record.
                //   haveAlpha     - false if the color has to be opaque.

        emColor Get() const;
        operator emColor () const;
                // Get the color value.

        void Set(emColor value);
        emColorRec & operator = (emColor value);
                // Set the color value. The alpha channel is set to 255 if this
                // color record has to be opaque.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:
        emColor DefaultValue, Value;
        bool HaveAlpha;
};

inline emColor emColorRec::Get() const
{
        return Value;
}

inline emColorRec::operator emColor () const
{
        return Value;
}

inline emColorRec & emColorRec::operator = (emColor value)
{
        Set(value);
        return *this;
}


//==============================================================================
//================================ emStructRec =================================
//==============================================================================

class emStructRec : public emRec {

public:

        // Base class for a structured record class. The idea is to give derived
        // classes some records as member variables.

        emStructRec();
        emStructRec(emStructRec * parent, const char * varIdentifier);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.

        virtual ~emStructRec();
                // Destructor.

        int GetCount() const;
                // Get number of members.

        const emRec & Get(int index) const;
        emRec & Get(int index);
        const emRec & operator [] (int index) const;
        emRec & operator [] (int index);
                // Get a reference to a member. The index must be within the
                // range of 0 to GetCount()-1.

        const char * GetIdentifierOf(int index) const;
                // Get the identifier for the given member index. Returns NULL
                // if the index is out of range.

        int GetIndexOf(const emRec * member) const;
                // Get the member index for the given member pointer. Returns -1
                // if there is no such member.

        int GetIndexOf(const char * identifier) const;
                // Get the member index for the given identifier. Returns -1 if
                // there is no such identifier.

        virtual bool ShallWriteOptionalOnly(const emRec * child) const;
                // Whether the given member should not be written when it has
                // default state. The default implementation always returns
                // false.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private: friend class emRec;

        void AddMember(emRec * member, const char * identifier);

        struct MemberType {
                const char * Identifier;
                emRec * Record;
        };

        struct RWStateType {
                int Pos;
                bool ChildReady;
                bool Empty;
                emByte Map[1];
        };

        int Count, Capacity;
        MemberType * Members;
        RWStateType * RWState;
};

inline int emStructRec::GetCount() const
{
        return Count;
}

inline const emRec & emStructRec::Get(int index) const
{
        return *Members[index].Record;
}

inline emRec & emStructRec::Get(int index)
{
        return *Members[index].Record;
}

inline const emRec & emStructRec::operator [] (int index) const
{
        return *Members[index].Record;
}

inline emRec & emStructRec::operator [] (int index)
{
        return *Members[index].Record;
}


//==============================================================================
//================================= emUnionRec =================================
//==============================================================================

class emUnionRec : public emRec {

public:

        // Record class for a union. An instance of this class manages one child
        // record with variable type. The possible types are called the
        // "variants".

        emUnionRec(int defaultVariant,
                   const char * identifier0, emRecAllocator allocator0, ...);
        emUnionRec(emStructRec * parent, const char * varIdentifier,
                   int defaultVariant,
                   const char * identifier0, emRecAllocator allocator0, ...);
                // Construct this record.
                //   parent         - The emStructRec this record is a member
                //                    of.
                //   varIdentifier  - Identifier for this record within the
                //                    emStructRec. The string is not copied -
                //                    the pointer must be valid for the life
                //                    time of this record.
                //   defaultVariant - Index of the default variant.
                //   identifier0    - Identifier for the first variant.
                //                    The string is not copied - the pointer
                //                    must be valid for the life time of the
                //                    record.
                //   allocator0     - Allocator function for the first variant.
                //   ...            - Any number of further pairs of identifier
                //                    and allocator, terminated by a NULL.

        virtual ~emUnionRec();
                // Destructor.

        int GetVariant() const;
                // Get the current variant index.

        void SetVariant(int variant);
                // Set the current variant index. If it is a change, the old
                // child record is deleted and a new one is created by calling
                // the corresponding allocator function.

        emRec & Get();
        const emRec & Get() const;
                // Get a reference to the child record.

        int GetVariantCount() const;
                // Get number of variants.

        const char * GetIdentifierOf(int variant) const;
                // Get the identifier for the given variant index. Returns NULL
                // if there is no such variant.

        int GetVariantOf(const char * identifier) const;
                // Get the variant index for the given identifier. Returns -1 if
                // there is no such identifier.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:

        void Init(int defaultVariant, const char * identifier0,
                  emRecAllocator allocator0, va_list args);

        struct VariantType {
                const char * Identifier;
                emRecAllocator Allocator;
        };

        VariantType * TypeArray;
        int VariantCount, DefaultVariant, Variant;
        emRec * Record;
};

inline int emUnionRec::GetVariant() const
{
        return Variant;
}

inline emRec & emUnionRec::Get()
{
        return *Record;
}

inline const emRec & emUnionRec::Get() const
{
        return *Record;
}

inline int emUnionRec::GetVariantCount() const
{
        return VariantCount;
}


//==============================================================================
//================================= emArrayRec =================================
//==============================================================================

class emArrayRec : public emRec {

public:

        // Record class for an array of records. Please even see the template
        // version emTArrayRec more below.

        emArrayRec(emRecAllocator allocator, int minCount=0,
                   int maxCount=INT_MAX);
        emArrayRec(emStructRec * parent, const char * varIdentifier,
                   emRecAllocator allocator, int minCount=0,
                   int maxCount=INT_MAX);
                // Construct this record.
                //   parent        - The emStructRec this record is a member of.
                //   varIdentifier - Identifier for this record within the
                //                   emStructRec. The string is not copied - the
                //                   pointer must be valid for the life time of
                //                   this record.
                //   allocator     - Allocator function for elements of the
                //                   array.
                //   minCount      - Minimum number of elements. This is even
                //                   the default number of elements.
                //   maxCount      - Maximum number of elements.

        virtual ~emArrayRec();
                // Destructor.

        int GetCount() const;
                // Get number of elements.

        void SetCount(int count);
                // Set number of elements. It is clipped if out of range. New
                // elements are set to their defaults.

        void Insert(int index, int insCount=1);
        void Remove(int index, int remCount=1);
                // Insert or remove elements at a particular position. The
                // number of elements is clipped if the resulting total number
                // would be out of range. New elements are set to their
                // defaults.

        int GetMinCount() const;
        int GetMaxCount() const;
                // Get minimum and maximum number of elements.

        const emRec & Get(int index) const;
        emRec & Get(int index);
        const emRec & operator [] (int index) const;
        emRec & operator [] (int index);
                // Get a reference to an element. The index must be within the
                // range of 0 to GetCount()-1.

        virtual void SetToDefault();
        virtual bool IsSetToDefault() const;
        virtual void TryStartReading(emRecReader & reader);
        virtual bool TryContinueReading(emRecReader & reader);
        virtual void QuitReading();
        virtual void TryStartWriting(emRecWriter & writer);
        virtual bool TryContinueWriting(emRecWriter & writer);
        virtual void QuitWriting();
        virtual emUInt64 CalcRecMemNeed() const;
                // See emRec.

private:

        void Init(emRecAllocator allocator, int minCount, int maxCount);

        emRecAllocator Allocator;
        int MinCount, MaxCount, Count, Capacity, RWPos;
        emRec * * Array;
        bool RWChildReady;
};

inline int emArrayRec::GetCount() const
{
        return Count;
}

inline int emArrayRec::GetMinCount() const
{
        return MinCount;
}

inline int emArrayRec::GetMaxCount() const
{
        return MaxCount;
}

inline const emRec & emArrayRec::Get(int index) const
{
        return *Array[index];
}

inline emRec & emArrayRec::Get(int index)
{
        return *Array[index];
}

inline const emRec & emArrayRec::operator [] (int index) const
{
        return *Array[index];
}

inline emRec & emArrayRec::operator [] (int index)
{
        return *Array[index];
}


//==============================================================================
//================================ emTArrayRec =================================
//==============================================================================

template <class REC> class emTArrayRec : public emArrayRec {

public:

        // Template version of emArrayRec.

        emTArrayRec(int minCount=0, int maxCount=INT_MAX);
        emTArrayRec(emStructRec * parent, const char * varIdentifier,
                    int minCount=0, int maxCount=INT_MAX);
                // Like with emArrayRec, but the allocator is
                // EM_DEFAULT_REC_ALLOCATOR(REC).

        const REC & Get(int index) const;
        REC & Get(int index);
        const REC & operator [] (int index) const;
        REC & operator [] (int index);
                // Like with emArrayRec, but the results are cast to template type.
};

template <class REC> inline emTArrayRec<REC>::emTArrayRec(
        int minCount, int maxCount
)
        : emArrayRec(EM_DEFAULT_REC_ALLOCATOR(REC),minCount,maxCount)
{
}

template <class REC> inline emTArrayRec<REC>::emTArrayRec(
        emStructRec * parent, const char * varIdentifier, int minCount,
        int maxCount
)
        : emArrayRec(parent,varIdentifier,EM_DEFAULT_REC_ALLOCATOR(REC),
                      minCount,maxCount)
{
}

template <class REC> inline const REC & emTArrayRec<REC>::Get(int index) const
{
        return (const REC &)emArrayRec::Get(index);
}

template <class REC> inline REC & emTArrayRec<REC>::Get(int index)
{
        return (REC &)emArrayRec::Get(index);
}

template <class REC> inline const REC & emTArrayRec<REC>::operator [] (
        int index
) const
{
        return (const REC &)emArrayRec::Get(index);
}

template <class REC> inline REC & emTArrayRec<REC>::operator [] (int index)
{
        return (REC &)emArrayRec::Get(index);
}


//==============================================================================
//================================ emRecReader =================================
//==============================================================================

class emRecReader : public emUncopyable {

public:

        // Abstract base class for reading a tree of records from a source.

        emRecReader();
        virtual ~emRecReader();

        void TryStartReading(emRec & root);
                // Start reading.
                // Arguments:
                //   root - The root of the records to filled.

        bool TryContinueReading();
                // Continue reading. Returns true when ready, otherwise
                // TryContinueReading has to be called again.

        void TryFinishReading();
                // Continue reading until ready.

        void QuitReading();
                // Abort any reading.

        // - - The following things are for implementing emRec derivatives - -

        enum ElementType {
                ET_DELIMITER,
                ET_IDENTIFIER,
                ET_INT,
                ET_DOUBLE,
                ET_QUOTED,
                ET_END
        };

        ElementType TryPeekNext(char * pDelimiter=NULL);
                // Peek for the type of the next syntactical element to be read.
                // If it is ET_DELIMITER and if pDelimiter is not NULL,
                // *pDelimiter is set to the delimiter character.

        char TryReadDelimiter();
                // Read the next syntactical element as a delimiter or throw an
                // error. Currently, a delimiter can be any character except for
                // white space, letters, digits, '#', '_' and '"'. The
                // characters '.', '-' and '+' can be delimiters only if not
                // confusable with numbers.

        void TryReadCertainDelimiter(char delimiter);
                // Read the next syntactical element as the given delimiter or
                // throw an error.

        const char * TryReadIdentifier();
                // Read the next syntactical element as an identifier or throw
                // an error.

        int TryReadInt();
                // Read the next syntactical element as an integer value or
                // throw an error.

        double TryReadDouble();
                // Read the next syntactical element as a double value or throw
                // an error.

        const char * TryReadQuoted();
                // Read and unquote the next syntactical element as a quoted
                // string or throw an error.

        void ThrowElemError(const char * text) const;
                // Throw an error containing the file name, the current line
                // number and the given text.

        void ThrowSyntaxError() const;
                // Like ThrowElemError("syntax error")

        const emRec * GetRootRec() const;
                // Get the root record of the tree currently read or NULL.

protected:

        virtual int TryRead(char * buf, int maxLen) = 0;
                // Read up to maxLen bytes from the source into the given
                // buffer, or throw an error. Return the number of bytes read,
                // or 0 if the end of source has been reached.

        virtual void TryClose() = 0;
                // Close the source, or throw an error.

        virtual const char * GetSourceName() const = 0;
                // Get the file name or another identification. This is required
                // for making error messages.

private:

        void SetMinNextBufSize(int minSize);
        void TryNextChar();
        void TryParseNext();

        emRec * Root;
        bool RootQuitPending;
        bool ClosePending;
        int Line;
        bool NextEaten;
        int NextLine;
        ElementType NextType;
        char NextDelimiter;
        char * NextBuf;
        int NextBufSize;
        int NextInt;
        double NextDouble;
        int NextChar;
};

inline const emRec * emRecReader::GetRootRec() const
{
        return Root;
}


//==============================================================================
//================================ emRecWriter =================================
//==============================================================================

class emRecWriter : public emUncopyable {

public:

        // Abstract base class for writing a tree of records to a target.

        emRecWriter();
        virtual ~emRecWriter();

        void TryStartWriting(emRec & root);
                // Start writing.
                // Arguments:
                //   root - The root of the records to be written.

        bool TryContinueWriting();
                // Continue writing. Returns true when ready, otherwise
                // TryContinueWriting has to be called again.

        void TryFinishWriting();
                // Continue writing until ready.

        void QuitWriting();
                // Abort any writing.

        // - - The following things are for implementing emRec derivatives - -

        void TryWriteDelimiter(char c);
                // Write the given delimiter character.

        void TryWriteIdentifier(const char * idf);
                // Write the given identifier.

        void TryWriteInt(int i);
                // Write the given integer value.

        void TryWriteDouble(double d);
                // Write the given double value.

        void TryWriteQuoted(const char * q);
                // Quote and write the given string.

        void TryWriteSpace();
                // Write a space character.

        void TryWriteNewLine();
                // Write a new-line character.

        void TryWriteIndent();
                // Write an indent (to be called only at beginning of a new
                // line). It is a number of tabulator characters.

        void IncIndent();
        void DecIndent();
                // Increase or decrease the number of tabulator characters to be
                // written by TryWriteIndent.

        const emRec * GetRootRec() const;
                // Get the root record of the tree currently written or NULL.

protected:

        virtual void TryWrite(const char * buf, int len) = 0;
                // Write len bytes from the given buffer to the target, or throw
                // an error.

        virtual void TryClose() = 0;
                // Flush and close the target, or throw an error.

private:

        void TryWriteChar(char c);
        void TryWriteString(const char * s);

        emRec * Root;
        bool RootQuitPending;
        bool ClosePending;
        int Indent;
};

inline void emRecWriter::IncIndent()
{
        Indent++;
}

inline void emRecWriter::DecIndent()
{
        Indent--;
}

inline const emRec * emRecWriter::GetRootRec() const
{
        return Root;
}


//==============================================================================
//============================== emRecFileReader ===============================
//==============================================================================

class emRecFileReader : public emRecReader {

public:

        // Class for reading a tree of records from a regular file.

        emRecFileReader();
        virtual ~emRecFileReader();

        void TryStartReading(emRec & root,
                             const emString & filePath);
                // Start reading.
                // Arguments:
                //   root     - The root of the tree of records to be read from
                //              the file.
                //   filePath - Path/name of the file.

        emUInt64 GetFileSize() const;
                // Size of the file.

        emUInt64 GetFilePos() const;
                // Read position in the file.

        double GetProgress() const;
                // Progress in percent.

protected:

        virtual int TryRead(char * buf, int maxLen);
        virtual void TryClose();
        virtual const char * GetSourceName() const;

private:

        emString FilePath;
        FILE * File;
        emUInt64 FileSize, FilePos;
};

inline emUInt64 emRecFileReader::GetFileSize() const
{
        return FileSize;
}

inline emUInt64 emRecFileReader::GetFilePos() const
{
        return FilePos;
}


//==============================================================================
//============================== emRecFileWriter ===============================
//==============================================================================

class emRecFileWriter : public emRecWriter {

public:

        // Class for writing a tree of records to a regular file.

        emRecFileWriter();
        virtual ~emRecFileWriter();

        void TryStartWriting(emRec & root, const emString & filePath);
                // Start writing.
                // Arguments:
                //   root     - The root of the tree of records to be written to
                //              the file.
                //   filePath - Path/name of the file.

protected:

        virtual void TryWrite(const char * buf, int len);
        virtual void TryClose();

private:

        emString FilePath;
        FILE * File;
};


//==============================================================================
//=============================== emRecMemReader ===============================
//==============================================================================

class emRecMemReader : public emRecReader {

public:

        // Class for reading a tree of records from memory.

        emRecMemReader();

        void TryStartReading(emRec & root, const char * buf,
                             int len);
                // Start reading.
                // Arguments:
                //   root  - The root of the tree of records to be read from
                //           memory.
                //   buf   - Memory buffer to be read, must be valid until
                //           reading has finished.
                //   len   - Number of bytes in the buffer.

protected:

        virtual int TryRead(char * buf, int maxLen);
        virtual void TryClose();
        virtual const char * GetSourceName() const;

private:

        const char * MemPos, * MemEnd;
};


//==============================================================================
//=============================== emRecMemWriter ===============================
//==============================================================================

class emRecMemWriter : public emRecWriter {

public:

        // Class for writing a tree of records to memory.

        emRecMemWriter();
        virtual ~emRecMemWriter();

        void TryStartWriting(emRec & root, emArray<char> & buf);
                // Start writing. This and the other Try methods should never
                // fail.
                // Arguments:
                //   root - The root of the tree of records to be written to
                //          memory.
                //   buf  - Memory buffer where the output is to be added. The
                //          reference must be valid until the writing has
                //          finished.

protected:

        virtual void TryWrite(const char * buf, int len);
        virtual void TryClose();

private:

        emArray<char> * Buf;
};


#endif