/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef _MORKATOM_
#define _MORKATOM_ 1

#ifndef _MORK_
#include "mork.h"
#endif

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789


#define morkAtom_kMaxByteSize 255 /* max for 8-bit integer */
#define morkAtom_kForeverCellUses 0x0FF /* max for 8-bit integer */
#define morkAtom_kMaxCellUses 0x07F /* max for 7-bit integer */

#define morkAtom_kKindWeeAnon  'a'  /* means morkWeeAnonAtom subclass */
#define morkAtom_kKindBigAnon  'A'  /* means morkBigAnonAtom subclass */
#define morkAtom_kKindWeeBook  'b'  /* means morkWeeBookAtom subclass */
#define morkAtom_kKindBigBook  'B'  /* means morkBigBookAtom subclass */
#define morkAtom_kKindFarBook  'f'  /* means morkFarBookAtom subclass */
#define morkAtom_kKindRowOid   'r'  /* means morkOidAtom subclass */
#define morkAtom_kKindTableOid 't'  /* means morkOidAtom subclass */

/*| Atom: .
|*/
class morkAtom { //
 
public: 

  mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  mork_change   mAtom_Change;    // how has this atom been changed?
  mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes

public: 
  morkAtom(mork_aid inAid, mork_u1 inKind);
  
  mork_bool IsWeeAnon() const { return mAtom_Kind == morkAtom_kKindWeeAnon; }
  mork_bool IsBigAnon() const { return mAtom_Kind == morkAtom_kKindBigAnon; }
  mork_bool IsWeeBook() const { return mAtom_Kind == morkAtom_kKindWeeBook; }
  mork_bool IsBigBook() const { return mAtom_Kind == morkAtom_kKindBigBook; }
  mork_bool IsFarBook() const { return mAtom_Kind == morkAtom_kKindFarBook; }
  mork_bool IsRowOid() const { return mAtom_Kind == morkAtom_kKindRowOid; }
  mork_bool IsTableOid() const { return mAtom_Kind == morkAtom_kKindTableOid; }

  mork_bool IsBook() const { return this->IsWeeBook() || this->IsBigBook(); }

public: // clean vs dirty

  void SetAtomClean() { mAtom_Change = morkChange_kNil; }
  void SetAtomDirty() { mAtom_Change = morkChange_kAdd; }
  
  mork_bool IsAtomClean() const { return mAtom_Change == morkChange_kNil; }
  mork_bool IsAtomDirty() const { return mAtom_Change == morkChange_kAdd; }

public: // atom space scope if IsBook() is true, or else zero:

  mork_scope GetBookAtomSpaceScope(morkEnv* ev) const;
  // zero or book's space's scope

  mork_aid   GetBookAtomAid() const;
  // zero or book atom's ID
 
public: // empty construction does nothing
  morkAtom() { }

public: // one-byte refcounting, freezing at maximum
  void       MakeCellUseForever(morkEnv* ev);
  mork_u1    AddCellUse(morkEnv* ev);
  mork_u1    CutCellUse(morkEnv* ev);
  
  mork_bool  IsCellUseForever() const 
  { return mAtom_CellUses == morkAtom_kForeverCellUses; }
  
private: // warnings

  static void CellUsesUnderflowWarning(morkEnv* ev);

public: // errors

  static void BadAtomKindError(morkEnv* ev);
  static void ZeroAidError(morkEnv* ev);
  static void AtomSizeOverflowError(morkEnv* ev);

public: // yarns

  static mork_bool AliasYarn(const morkAtom* atom, mdbYarn* outYarn);
  mork_bool   GetYarn(mdbYarn* outYarn) const;

private: // copying is not allowed
  morkAtom(const morkAtom& other);
  morkAtom& operator=(const morkAtom& other);
};

/*| OidAtom: an atom that references a row or table by identity.
|*/
class morkOidAtom : public morkAtom { //

  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
 
public:
  mdbOid           mOidAtom_Oid;       // identity of referenced object

public: // empty construction does nothing
  morkOidAtom() { }
  void InitRowOidAtom(morkEnv* ev, const mdbOid& inOid);
  void InitTableOidAtom(morkEnv* ev, const mdbOid& inOid);

private: // copying is not allowed
  morkOidAtom(const morkOidAtom& other);
  morkOidAtom& operator=(const morkOidAtom& other);
};

/*| WeeAnonAtom: an atom whose content immediately follows morkAtom slots
**| in an inline fashion, so that morkWeeAnonAtom contains both leading
**| atom slots and then the content bytes without further overhead.  Note
**| that charset encoding is not indicated, so zero is implied for Latin1.
**| (Non-Latin1 content must be stored in a morkBigAnonAtom with a charset.)
**|
**|| An anon (anonymous) atom has no identity, with no associated bookkeeping
**| for lookup needed for sharing like a book atom.
**|
**|| A wee anon atom is immediate but not shared with any other users of this
**| atom, so no bookkeeping for sharing is needed.  This means the atom has
**| no ID, because the atom has no identity other than this immediate content,
**| and no hash table is needed to look up this particular atom.  This also
**| applies to the larger format morkBigAnonAtom, which has more slots.
|*/
class morkWeeAnonAtom : public morkAtom { //

  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes
  
public:
  mork_u1 mWeeAnonAtom_Body[ 1 ]; // 1st byte of immediate content vector

public: // empty construction does nothing
  morkWeeAnonAtom() { }
  void InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf);
  
  // allow extra trailing byte for a null byte:
  static mork_size SizeForFill(mork_fill inFill)
  { return sizeof(morkWeeAnonAtom) + inFill; }

private: // copying is not allowed
  morkWeeAnonAtom(const morkWeeAnonAtom& other);
  morkWeeAnonAtom& operator=(const morkWeeAnonAtom& other);
};

/*| BigAnonAtom: another immediate atom that cannot be encoded as the smaller
**| morkWeeAnonAtom format because either the size is too great, and/or the
**| charset is not the default zero for Latin1 and must be explicitly noted.
**|
**|| An anon (anonymous) atom has no identity, with no associated bookkeeping
**| for lookup needed for sharing like a book atom.
|*/
class morkBigAnonAtom : public morkAtom { //

  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
 
public:
  mork_cscode   mBigAnonAtom_Form;      // charset format encoding
  mork_size     mBigAnonAtom_Size;      // size of content vector
  mork_u1       mBigAnonAtom_Body[ 1 ]; // 1st byte of immed content vector

public: // empty construction does nothing
  morkBigAnonAtom() { }
  void InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm);
  
  // allow extra trailing byte for a null byte:
  static mork_size SizeForFill(mork_fill inFill)
  { return sizeof(morkBigAnonAtom) + inFill; }

private: // copying is not allowed
  morkBigAnonAtom(const morkBigAnonAtom& other);
  morkBigAnonAtom& operator=(const morkBigAnonAtom& other);
};

#define morkBookAtom_kMaxBodySize 1024 /* if larger, cannot be shared */

/*| BookAtom: the common subportion of wee book atoms and big book atoms that
**| includes the atom ID and the pointer to the space referencing this atom
**| through a hash table.
|*/
class morkBookAtom : public morkAtom { //
  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes
  
public:
  morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is atom scope 
  mork_aid       mBookAtom_Id;    // identity token for this shared atom

public: // empty construction does nothing
  morkBookAtom() { }

  static void NonBookAtomTypeError(morkEnv* ev);

public: // Hash() and Equal() for atom ID maps are same for all subclasses:

  mork_u4 HashAid() const { return mBookAtom_Id; }
  mork_bool EqualAid(const morkBookAtom* inAtom) const
  { return ( mBookAtom_Id == inAtom->mBookAtom_Id); }

public: // Hash() and Equal() for atom body maps know about subclasses:
  
  // YOU CANNOT SUBCLASS morkBookAtom WITHOUT FIXING Hash and Equal METHODS:

  mork_u4 HashFormAndBody(morkEnv* ev) const;
  mork_bool EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const;
  
public: // separation from containing space

  void CutBookAtomFromSpace(morkEnv* ev);

private: // copying is not allowed
  morkBookAtom(const morkBookAtom& other);
  morkBookAtom& operator=(const morkBookAtom& other);
};

/*| FarBookAtom: this alternative format for book atoms was introduced
**| in May 2000 in order to support finding atoms in hash tables without
**| first copying the strings from original parsing buffers into a new
**| atom format.  This was consuming too much time.  However, we can
**| use morkFarBookAtom to stage a hash table query, as long as we then
**| fix HashFormAndBody() and EqualFormAndBody() to use morkFarBookAtom
**| correctly.
**|
**|| Note we do NOT intend that instances of morkFarBookAtom will ever
**| be installed in hash tables, because this is not space efficient.
**| We only expect to create temp instances for table lookups.
|*/
class morkFarBookAtom : public morkBookAtom { //
  
  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms

  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
  
public:
  mork_cscode   mFarBookAtom_Form;      // charset format encoding
  mork_size     mFarBookAtom_Size;      // size of content vector
  mork_u1*      mFarBookAtom_Body;      // bytes are elsewere, out of line

public: // empty construction does nothing
  morkFarBookAtom() { }
  void InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf,
    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid);
  
private: // copying is not allowed
  morkFarBookAtom(const morkFarBookAtom& other);
  morkFarBookAtom& operator=(const morkFarBookAtom& other);
};

/*| WeeBookAtom: .
|*/
class morkWeeBookAtom : public morkBookAtom { //
  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes

  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
  
public:
  mork_u1     mWeeBookAtom_Body[ 1 ]; // 1st byte of immed content vector

public: // empty construction does nothing
  morkWeeBookAtom() { }
  explicit morkWeeBookAtom(mork_aid inAid);
  
  void InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf,
    morkAtomSpace* ioSpace, mork_aid inAid);
  
  // allow extra trailing byte for a null byte:
  static mork_size SizeForFill(mork_fill inFill)
  { return sizeof(morkWeeBookAtom) + inFill; }

private: // copying is not allowed
  morkWeeBookAtom(const morkWeeBookAtom& other);
  morkWeeBookAtom& operator=(const morkWeeBookAtom& other);
};

/*| BigBookAtom: .
|*/
class morkBigBookAtom : public morkBookAtom { //
  
  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms

  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
  
public:
  mork_cscode   mBigBookAtom_Form;      // charset format encoding
  mork_size     mBigBookAtom_Size;      // size of content vector
  mork_u1       mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector

public: // empty construction does nothing
  morkBigBookAtom() { }
  void InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf,
    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid);
  
  // allow extra trailing byte for a null byte:
  static mork_size SizeForFill(mork_fill inFill)
  { return sizeof(morkBigBookAtom) + inFill; }

private: // copying is not allowed
  morkBigBookAtom(const morkBigBookAtom& other);
  morkBigBookAtom& operator=(const morkBigBookAtom& other);
};

/*| MaxBookAtom: .
|*/
class morkMaxBookAtom : public morkBigBookAtom { //
  
  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
  // mork_change   mAtom_Change;    // how has this atom been changed?
  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms

  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
  // mork_aid       mBookAtom_Id;    // identity token for this shared atom

  // mork_cscode   mBigBookAtom_Form;      // charset format encoding
  // mork_size     mBigBookAtom_Size;      // size of content vector
  // mork_u1       mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector
  
public:
  mork_u1 mMaxBookAtom_Body[ morkBookAtom_kMaxBodySize + 3 ]; // max bytes

public: // empty construction does nothing
  morkMaxBookAtom() { }
  void InitMaxBookAtom(morkEnv* ev, const morkBuf& inBuf,
    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
  { this->InitBigBookAtom(ev, inBuf, inForm, ioSpace, inAid); }

private: // copying is not allowed
  morkMaxBookAtom(const morkMaxBookAtom& other);
  morkMaxBookAtom& operator=(const morkMaxBookAtom& other);
};

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

#endif /* _MORKATOM_ */