//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
//
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// hierdbnode.h
//
// these classes are used by cHierDatabase as the nodes in the hierarchy
//
#ifndef __HIERDBNODE_H
#define __HIERDBNODE_H

#ifndef __TYPES_H
#include "types.h"
#endif
#ifndef __ARCHIVE_H
#include "archive.h"
#endif

///////////////////////////
// BIG TODO -- I need to change all of these strings to be wchar_t instead of TSTRINGS
//              Also, they should serialize themselves in a platform-neutral byte order
///////////////////////////

//-----------------------------------------------------------------------------
// cHierNode -- the base class for all other nodes; this class contains type
//      information
//-----------------------------------------------------------------------------
class cHierNode
{
public:
    enum Type
    {
        TYPE_INVALID = -1,
        TYPE_MIN     = 0,
        TYPE_ROOT    = 1,
        TYPE_ENTRY,
        TYPE_ARRAY,
        TYPE_ARRAY_INFO,
        TYPE_MAX
    };


    explicit cHierNode(Type type = TYPE_INVALID) : mType(type)
    {
    }
    virtual ~cHierNode()
    {
    }

    int32 mType;

    /////////////////////////////////////////
    // methods to override
    /////////////////////////////////////////
    virtual int32 CalcArchiveSize() const
    {
        return (sizeof(mType));
    }
    // this method should return the amount of space that this class will take up in an archive
    // derived classes should override this if they have any data members that will be stored
    // with Read() or Write()
    virtual void Write(cArchive& arch) const //throw(eArchive)
    {
        arch.WriteInt32(mType);
    }
    virtual void Read(cArchive& arch) //throw(eArchive)
    {
        arch.ReadInt32(mType);
        //
        // do a rudimentary integrity check...
        //
        if ((mType <= TYPE_MIN) || (mType >= TYPE_MAX))
        {
            ASSERT(false);
            throw eArchiveFormat(_T("Invalid type encountered in cHierNode::Read"));
        }
    }
    // these methods read and write the class to an archive. We had better be sure that
    // we don't write more than we said we would in CalcArchiveSize() !
};

//-----------------------------------------------------------------------------
// cHierAddr -- the "address" of different items in the hierarchy
//-----------------------------------------------------------------------------
class cHierAddr
{
public:
    int32 mBlockNum;
    int32 mIndex;

    cHierAddr(int32 block = -1, int32 index = -1) : mBlockNum(block), mIndex(index)
    {
    }

    bool IsNull() const
    {
        return (mBlockNum == -1);
    }
    // returns true if the address points to "null" (ie -- no valid address)
    bool operator==(const cHierAddr& rhs) const
    {
        return ((mBlockNum == rhs.mBlockNum) && (mIndex == rhs.mIndex));
    }


    /////////////////////////////////////////////////
    // serialization methods
    /////////////////////////////////////////////////
    int32 CalcArchiveSize() const
    {
        return (sizeof(mBlockNum) + sizeof(mIndex));
    }
    void Write(cArchive& arch) const //throw(eArchive)
    {
        arch.WriteInt32(mBlockNum);
        arch.WriteInt32(mIndex);
    }
    void Read(cArchive& arch) //throw(eArchive)
    {
        arch.ReadInt32(mBlockNum);
        arch.ReadInt32(mIndex);
    }
};


//-----------------------------------------------------------------------------
// cHierRoot -- the root of the hierarchy; you can always count on this being
//      at (0, 0)
//-----------------------------------------------------------------------------
class cHierRoot : public cHierNode
{
public:
    cHierRoot() : cHierNode(TYPE_ROOT), mbCaseSensitive(true), mDelimitingChar('/')
    {
    }

    cHierAddr mChild;          // points to a cHierArray or an invalid address
    bool      mbCaseSensitive; // determines the case-sensitiveness of lookups, ordering, etc.
    TCHAR     mDelimitingChar; // the delimiting character; this is used when displaying a path to the user

    /////////////////////////////////////////////////
    // serialization methods
    /////////////////////////////////////////////////
    virtual int32 CalcArchiveSize() const
    {
        return (cHierNode::CalcArchiveSize() + mChild.CalcArchiveSize());
    }
    virtual void Write(cArchive& arch) const //throw(eArchive)
    {
        cHierNode::Write(arch);
        mChild.Write(arch);
        arch.WriteInt32(mbCaseSensitive ? 1 : 0);
        TSTRING dc(&mDelimitingChar, 1);
        arch.WriteString(dc);
    }
    virtual void Read(cArchive& arch) //throw(eArchive)
    {
        cHierNode::Read(arch);
        //
        // make sure the type is correct
        //
        if (mType != TYPE_ROOT)
        {
            ASSERT(false);
            throw eArchiveFormat(_T("Invalid type encountered; expected ROOT node"));
        }
        mChild.Read(arch);
        int32 cs;
        arch.ReadInt32(cs);
        mbCaseSensitive = cs ? true : false;
        TSTRING dc;
        arch.ReadString(dc);

        if (dc.length() != 1)
        {
            ASSERT(false);
            throw eArchiveFormat(_T("Read of the root node failed; invalid delimiting character."));
        }
        mDelimitingChar = dc[0];
    }
};

//-----------------------------------------------------------------------------
// cHierEntry -- an entry that contains a name and points to some data
//-----------------------------------------------------------------------------
class cHierEntry : public cHierNode
{
public:
    cHierEntry() : cHierNode(TYPE_ENTRY)
    {
    }

    TSTRING   mName; // TODO -- change this to a wchar_t string
    cHierAddr mData;
    cHierAddr mChild; // points to a cHierArray or an invalid address
    cHierAddr mNext;  // the next peer entry in the linked list, or Null() if done

    /////////////////////////////////////////////////
    // serialization methods
    /////////////////////////////////////////////////
    virtual int32 CalcArchiveSize() const
    {
        return (cHierNode::CalcArchiveSize() + mChild.CalcArchiveSize() + mData.CalcArchiveSize() +
                cArchive::GetStorageSize(mName) + mNext.CalcArchiveSize());
    }
    virtual void Write(cArchive& arch) const //throw(eArchive)
    {
        cHierNode::Write(arch);
        arch.WriteString(mName);
        mChild.Write(arch);
        mData.Write(arch);
        mNext.Write(arch);
    }
    virtual void Read(cArchive& arch) //throw(eArchive)
    {
        cHierNode::Read(arch);
        //
        // make sure the type is correct
        //
        if (mType != TYPE_ENTRY)
        {
            ASSERT(false);
            throw eArchiveFormat(_T("Invalid type encountered; expected ENTRY node"));
        }

        arch.ReadString(mName);
        mChild.Read(arch);
        mData.Read(arch);
        mNext.Read(arch);
    }
};

//-----------------------------------------------------------------------------
// cHierArrayInfo -- constant-size struct that contains information about an array
//-----------------------------------------------------------------------------
class cHierArrayInfo : public cHierNode
{
public:
    cHierArrayInfo() : cHierNode(TYPE_ARRAY_INFO)
    {
    }

    cHierAddr mParent; // points to a cHierArrayInfo or cHierRoot
    cHierAddr mArray;  // points to cHierEntryArray

    /////////////////////////////////////////////////
    // serialization methods
    /////////////////////////////////////////////////
    virtual int32 CalcArchiveSize() const
    {
        return (cHierNode::CalcArchiveSize() + mParent.CalcArchiveSize() + mArray.CalcArchiveSize());
    }
    virtual void Write(cArchive& arch) const //throw(eArchive)
    {
        cHierNode::Write(arch);
        mParent.Write(arch);
        mArray.Write(arch);
    }
    virtual void Read(cArchive& arch) //throw(eArchive)
    {
        cHierNode::Read(arch);
        //
        // make sure the type is correct
        //
        if (mType != TYPE_ARRAY_INFO)
        {
            ASSERT(false);
            throw eArchiveFormat(_T("Invalid type encountered; expected TYPE_ARRAY_INFO node"));
        }

        mParent.Read(arch);
        mArray.Read(arch);
    }
};


#endif //__HIERDBNODE_H
