/*

    ========== licence begin  GPL
    Copyright (c) 2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under 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.

    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 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.
    ========== licence end

*/
#ifndef __OMS_OBJECTCONTAINERDEF_HPP
#define __OMS_OBJECTCONTAINERDEF_HPP

#include "Oms/OMS_AbstractObject.hpp"
#include "ggg91.h"

class OmsObjectContainer;
class OMS_ClassIdEntry;
class OMS_GuidEntry;
class OMS_Context;

typedef OmsObjectContainer* OmsObjectContainerPtr; 

/// Object Container for the storage in the local cache.  
class OmsObjectContainer{ 
  friend class OmsHandle;
  friend class OMS_OidHash;

public:
  /*! State of an object container */
  enum ct_state {
    /*! This flag is used to mark an object for being stored to the kernel. 
    ** This can be caused either by an explicit call of omsStore or when an object is newly 
    ** created. The flag is reset if the object is flushed to the kernel or when it is 
    ** deleted before it has been flushed. */
    CN_STORE = 1, 
    /*! This flag indicates whether an object is locked in the kernel. So every time a lock 
    ** is requested from the kernel this flag is set, too. The advantage of this local 
    ** bookkeeping is, that it is possible to check whether a lock is aquired on the object 
    ** without asking the kernel. */
    CN_LOCKED = 2, 
    /*! Flag is set if an object is deleted. In this case the flag CN_STORE is reset to initial. */
    CN_DELETE = 4, 
    /*! CN_NEW : This flag is only used in conjunction with before images and there it means, 
    ** that the corresponding object has been created in a subtransaction. If such an object is
    ** found in a rollback the corresponding object in the cache must be deleted. */
    CN_NEW = 8, 
    /*! This flag is set for objects with variable length */
    CN_VAROBJ = 16, 
    /*! Flag is set if object is newly created */
    CN_IS_NEW = 32,
    /*! Flag is set if an keyed object is deleted and a new keyed object with the same key is
    **  created in the same transaction. In this case the oid is reused, but the generation
    **  counter of the new oid is incremented. The frame in which the deleted object is stored
    **  is marked as replaced.
    **  \since PTS 1125361 */
    CN_REPLACED = 64
  };

  /*! Additional state of an object container in a version */
  enum ct_ver_state { 
    /*! This flag is set when the object is copied into the local cache. */
    CN_VER_NEW = 1, 
    /*! This flag is set after the object has been inserted into the structure for newly 
    ** created objects in versions (omsContext_co12::m_NewObjCache) */
    CN_VER_LINKED = 2
  };

private:
  /*! Link to next object in a hashlist. This links is either used to link together 
  ** data-objects belonging to the same hash value, freelist objects with the same size, 
  ** before-images belonging to the same subtransaction level ... */
  OmsObjectContainerPtr m_hashnext;

public:
  /*! Object identifier of the object stored in this container */
  OmsObjectId           m_oid;
  tgg91_PageRef         m_objseq;

private:
  /*! Bit array which indicates the state of the object. See OmsObjectContainer::ct_state
  ** for a description of the possible states. */ 
  unsigned char         m_state;
  /*! Bit array which indicates the state of the object in a version. See 
  ** OmsObjectContainer::ct_ver_state for a description of the possible states. */
  unsigned char         m_verstate;

public:
  /*! Bit-array which indicates, on which level of subtransaction there exists a before-image 
  **  for this object */
  tsp00_Uint4           m_beforeImages;
#if defined(BIT64)
  /*! Fill bytes which are necessary on 64 bit platforms for alignement. If this space is needed
  **  for new variables, then the filler should be removed */
  tsp00_Uint4           filler;
#endif
  /*! Pointer to the class container entry which corresponds to object stored in this container. */
  OMS_ClassIdEntry      *m_containerInfo; 
  /*! User data. At the beginning of this chunk the vtable-pointer is stored. Beside of this the structure is unknown; 
  **  only the size of this chunk is known. */
  OmsAbstractObject     m_pobj;

  inline OmsObjectContainer*  GetNext();
  inline OmsObjectContainer** GetNextAddr();
  inline void                 SetNext(OmsObjectContainer*);

  inline void                 InitializeForFreeList(int caller);
  inline void                 InitializeForAllocator(int caller);
  inline void                 CheckFreeListState();
  inline OmsObjectContainer*  GetNextFreeList();
  inline void                 SetNextFreeList(OmsObjectContainer*);
  void                        error(char* msg, OmsObjectContainer*p);

  inline static tsp00_Int4 headerSize();

  //inline void ChainFree(OMS_Context* context);

  bool DeletedFlag() const {
    return ((m_state & OmsObjectContainer::CN_DELETE) > 0);
  }

  bool existBeforeImage (int subtransLvl, bool inVersion) const
  {
    if (0 == subtransLvl) {
      return true;
    }
    if ((!inVersion) && (1 == subtransLvl)) {
      return true;
    }
    return ((m_beforeImages & (1 << (subtransLvl - 1))) > 0);
  }

  inline tsp00_Uint4       GetBeforeImageMask() const { return m_beforeImages; }

  inline OMS_ClassIdEntry* GetContainerInfo(OMS_Context* context);

  inline OMS_ClassIdEntry* GetContainerInfoNoCheck(OMS_Context* context);

  inline bool IsContainerAssigned() const
  {
    return m_containerInfo != NULL;
  }

  inline tsp00_Uint4 GetContainerHandle() const;

  unsigned char GetState() const
  {
    return m_state;
  }

  inline void InitObjContainer(OMS_GuidEntry& classInfo);
  inline void InitObjContainer(OMS_ClassIdEntry& clsinfo);
  inline void InitState(OMS_GuidEntry* classInfo);
  inline void InitState(OMS_ClassIdEntry* clsinfo);

  void InitVerNew(tsp00_Int4 ofs)
  {
    void **ptr = (void**) (((char*) this) + ofs);
    ptr[0] = ptr[1] = NULL;
    m_verstate |= OmsObjectContainer::CN_VER_NEW;
  }


  bool IsNewObject() const
  {
    return ((m_state & OmsObjectContainer::CN_IS_NEW) > 0);
  }

  bool IsVerNewObject() const
  {
    return ((m_verstate & OmsObjectContainer::CN_VER_NEW) > 0);
  }

  bool IsVerNewRegistered() const
  {
    return ((m_verstate & OmsObjectContainer::CN_VER_LINKED) > 0);
  }

  void UnmarkVerNewObject()
  {
    m_verstate = 0;
  }

  bool LockedFlag()
  {
    return ((m_state & OmsObjectContainer::CN_LOCKED) > 0);
  }

  void MarkAsNew()
  {
    m_state |= OmsObjectContainer::CN_IS_NEW;
  }

  void MarkDeleted()
  {
    m_state &= (OmsObjectContainer::CN_STORE ^ 0xFF);
    m_state |= (OmsObjectContainer::CN_DELETE);
  }

  void MarkLocked()
  {
    m_state |= OmsObjectContainer::CN_LOCKED;
  }

  void MarkNew()
  {
    m_state |= OmsObjectContainer::CN_NEW;
  }

  void MarkReplaced()  // PTS 1125361
  {
    m_state |= OmsObjectContainer::CN_REPLACED;
  }

  void MarkVerNewRegistered()
  {
    m_verstate |= OmsObjectContainer::CN_VER_LINKED;
  }

  void MarkStored()
  {
    m_state |= OmsObjectContainer::CN_STORE;
  }

  void MarkVarObj() 
  {
    m_state |= OmsObjectContainer::CN_VAROBJ;
  }

  bool NewFlag()
  {
    return ((m_state & OmsObjectContainer::CN_NEW) > 0);
  }

  void RemoveStoredFlag() 
  {
    m_state &= (OmsObjectContainer::CN_STORE ^ 0xFF);
  }

  bool ReplacedFlag()   // PTS 1125361
  {
    return ((m_state & OmsObjectContainer::CN_REPLACED) > 0);
  }

  void setBeforeImage (int subtransLvl) 
  {
    m_beforeImages |= (1 << (subtransLvl-1)); 
  }

  static void store (OmsHandle& h, OmsAbstractObject* o) 
  {
    OmsObjectContainerPtr p = containerPtr(o);
    p->m_state |= OmsObjectContainer::CN_STORE;
  }

  void SetContainerInfo(OMS_ClassIdEntry *ContainerInfo) 
  {
    m_containerInfo = ContainerInfo;
  }

  bool StoredFlag() {
    return ((m_state & OmsObjectContainer::CN_STORE) > 0);
  }

  void UnmarkReplaced()  // PTS 1125361
  {
    m_state &= (OmsObjectContainer::CN_REPLACED ^ 0xFF);
  }

  bool VarObjFlag() {
    return ((m_state & OmsObjectContainer::CN_VAROBJ) > 0);
  }

  static OmsObjectContainerPtr containerPtr(OmsAbstractObject* pobj) 
  {
    return (OmsObjectContainerPtr) (((unsigned char*) pobj) - ((sizeof(OmsObjectContainer)) - sizeof (OmsAbstractObject))); 
  }

  void Copy (OMS_Context* c, OmsObjectContainerPtr beforeImage, int ObjectSize);

  /*!
   * \brief Compare object data in this object and before image.
   *
   * This method compares object data in the object with those in before image,
   * except vtable. This is used in simulator to check for not stored updated
   * objects.
   *
   * \param c context,
   * \param beforeImage before image,
   * \param ObjectSize object size excluding vtable (persistent size).
   *
   * \return 0, if data match, !=0, if data differ.
   */
  int Compare (OMS_Context* c, OmsObjectContainerPtr beforeImage, int ObjectSize);
public:
};

/*=====================================================================*/

inline OmsObjectContainer* OmsObjectContainer::GetNext()
{
  unsigned char pattern1[] = {0xfd, 0xfd, 0xfd, 0xfd};
  unsigned char pattern2[] = {0xad, 0xad, 0xad, 0xad};

  if (!memcmp(this, &pattern1[0], 4)){
    // Frame is already released from allocator
    error("Illegal pattern 'fd' found -1-.", this);
  }
  else if (!memcmp(this, &pattern2[0], 4)){
    // Frame is already appended to the freelist
    error("Illegal pattern 'ad' found -1-.", this);
  }
    
  return m_hashnext;
}

inline void OmsObjectContainer::SetNext(OmsObjectContainer *p)
{
  m_hashnext = p;
}


#endif  // __OMS_OBJECTCONTAINERDEF_HPP