/*!
 * \file    OMS_ContainerDictionary.cpp
 * \author  IvanS, MarkusSi, PeterG
 * \brief   Implementation of global container dictionary
 */
/*

    ========== licence begin  GPL
    Copyright (c) 2002-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



*/

#include "Oms/OMS_ContainerDictionary.hpp"
#include "Oms/OMS_SinkCriticalSection.hpp"
#include "Oms/OMS_ContainerInfo.hpp"
#include "Oms/OMS_HResult.hpp"
#include "Oms/OMS_DumpInterface.hpp"
#include "Oms/OMS_TraceInterface.hpp"
#include "Oms/OMS_Globals.hpp"

static tsp00_Uint4 omsContainerHandle = 0;

/*----------------------------------------------------------------------------------*/

OMS_ContainerDictionary::OMS_ContainerDictionary() : OMS_IGuidHash(), m_dropId(0) 
{
  this->Init();
}

/*----------------------------------------------------------------------------------*/

tgg00_BasisError OMS_ContainerDictionary::DropContainer (IliveCacheSink* lcSink, 
                                                         const ClassIDRef guid, 
                                                         OmsSchemaHandle schema,
                                                         OmsContainerNo cno,
                                                         size_t arrayByteSize) 
{
  tgg00_BasisError DBError = e_ok;
  ClassID rguid = guid;
  if (arrayByteSize) {
    omsMakeArrayGuid(rguid, arrayByteSize);
  }
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  cs.Enter();
  GUID_TEMP(lguid, rguid);
  OMS_HResult hr = lcSink->DropContainer (schema, lguid, cno, DBError);
  //if (e_ok == DBError) {
  //PTS 1134184 OMS_ContainerInfo* p = this->FindGuid(guid, schema, cno);   
  //PTS 1134184  if (NULL != p) {
  //PTS 1134184    p->MarkDropped();
  //PTS 1134184    ++m_dropId;   // PTS 1120873
  //PTS 1134184  }
  //}
  return DBError;
}

/*----------------------------------------------------------------------------------*/

tgg00_BasisError OMS_ContainerDictionary::DropSchema (IliveCacheSink* lcSink, OmsSchemaHandle Schema) 
{
  tgg00_BasisError DBError = 0;
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  cs.Enter();
  OMS_HResult hr = lcSink->DropSchema (Schema, &DBError);
  // Mark all containers belonging to the given schema as deleted, even if not
  // all containers have been dropped. This will prevent inconsistencies in which
  // containers are still marked as valid, while in the kernel the container does
  // not exist any more. By marking all containers as dropped there might be some
  // containers left in the kernel which are not accessible, but this shoud be
  // - besides the space consumption - no problem.
  // PTS 1127421
  //if (0 == DBError) {
  //PTS PTS 1134184  for (OMS_ContainerDictionaryIterator iter(this); iter; ++iter) {
  //PTS PTS 1134184    if (iter()->GetSchema() == Schema) {
  //PTS PTS 1134184      iter()->MarkDropped();
  //PTS PTS 1134184    }
  //PTS PTS 1134184  }
  //PTS PTS 1134184  ++m_dropId;
  //}
  return DBError;
}

/*----------------------------------------------------------------------------------*/

void  OMS_ContainerDictionary::Dump(OMS_DumpInterface& dumpObj)
{
  IliveCacheSink* lcSink = NULL;
  if (dumpObj.Synchronize())
  {
    lcSink = OMS_Globals::GetCurrentLcSink();
  }
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  dumpObj.SetDumpLabel(LABEL_OMS_CLASS);
  if (dumpObj.Synchronize())
  {
    cs.Enter();
  }
  for (int hashSlot = 0; hashSlot < OMS_ContainerDictionary::MaxHeadEntries; ++hashSlot)
  {
    OMS_ClassDumpInfo dumpInfo(hashSlot);
    OMS_ClassInfo* p = m_classInfoHead[hashSlot];
    while (NULL != p)
    {
      p->Dump(dumpInfo);
      dumpObj.Dump(&dumpInfo, sizeof(dumpInfo));
      p = p->m_HashNext;
    }
  }
  dumpObj.SetDumpLabel(LABEL_OMS_CONTAINER);
  for (OMS_ContainerDictionaryIterator iter(this); iter; ++iter)
  {
    OMS_ContainerDumpInfo dumpInfo;
    iter()->Dump(dumpInfo);
    dumpObj.Dump(&dumpInfo, sizeof(dumpInfo));
  }
}

/*----------------------------------------------------------------------------------*/

OMS_ClassInfo* OMS_ContainerDictionary::FindGuid (const ClassIDRef guid) 
{
  int HashSlot  = HashValue(guid, OMS_ContainerDictionary::MaxHeadEntries);
  OMS_ClassInfo* p = m_classInfoHead[HashSlot];
  while (NULL != p) {
    if (p->GetGuid() == guid) {
      return p;
    }
    else {
      p = p->m_HashNext;
    }
  }
  return NULL;
}

/*----------------------------------------------------------------------------------*/

OMS_ClassInfo* OMS_ContainerDictionary::FindGuid (IliveCacheSink* lcSink, const ClassIDRef guid) 
{
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  cs.Enter();
  OMS_ClassInfo* p = this->FindGuid(guid);
  return p;
}

/*----------------------------------------------------------------------------------*/

OMS_ContainerInfo* OMS_ContainerDictionary::FindViaContainerHandle  (IliveCacheSink* lcSink, tsp00_Uint4 containerHandle) 
{
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  cs.Enter();
  int clsidHashSlot = containerHandle % OMS_ContainerDictionary::MaxHeadEntries;
  OMS_ContainerInfo* p = m_clsidHead[clsidHashSlot];
  while (NULL != p) {
    if (p->GetContainerHandle() == containerHandle) {
      if (p->IsDropped()) {
        p = NULL;
      }
      break;
    }
    else {
      p = p->m_clsidHashNext;
    }
  }
  return p;
}

/*----------------------------------------------------------------------------------*/

OMS_ContainerInfo* OMS_ContainerDictionary::FindViaGuid (IliveCacheSink* lcSink, const ClassIDRef guid, OmsSchemaHandle sh, OmsContainerNo cno) 
{
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  cs.Enter();
  OMS_ContainerInfo* p = this->FindGuid(guid, sh, cno);
  if ((p != NULL) && (p->IsDropped())) {
    p = NULL;
    OMS_Globals::Throw(DbpError (DbpError::DB_ERROR, e_unknown_guid, __MY_FILE__, __LINE__)); // PTS 1120873
  }
  return p;
}

/*----------------------------------------------------------------------------------*/

OMS_ContainerInfo* OMS_ContainerDictionary::FindGuid(const ClassIDRef guid, OmsSchemaHandle sh, OmsContainerNo cno) 
{
  int guidHashSlot  = HashValue(guid, sh, cno, OMS_ContainerDictionary::MaxHeadEntries);
  OMS_ContainerInfo* p = m_guidHead[guidHashSlot];
  while (NULL != p) {
    if ((p->m_schema == sh) && (p->m_ContainerNo == cno) && omsIsCompatibleGuid(p->m_clsInfo->GetGuid(), guid)) {
      break;
    }
    else {
      p = p->m_guidHashNext;
    }
  }
  return p;
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionary::Init() 
{
  int ix;
  for (ix = 0; ix < MaxHeadEntries; ++ ix) {
    m_classInfoHead[ix] = NULL;
    m_clsidHead[ix]     = NULL;
    m_guidHead [ix]     = NULL;
  }
}

/*----------------------------------------------------------------------------------*/
// PTS 1134184
void OMS_ContainerDictionary::MarkDropped (const ClassIDRef guid, 
                                           OmsSchemaHandle sh, 
                                           OmsContainerNo cno)
{
  /*  Note: the function is called as callback of 
  OMS_ContainerDictionary::DropContainer or OMS_ContainerDictionary::DropSchema
  within RGN_CLASS_DIR region                     */ 

  OMS_ContainerInfo* p = OMS_Globals::m_globalsInstance->m_classDictionary.FindGuid(guid, sh, cno);
  if (NULL != p) {
    if (p->m_refCnt > 0){
      p->MarkDropped();
      ++m_dropId;   
    }
    else {
      IliveCacheSink *pSink = OMS_Globals::GetCurrentLcSink();
      Delete(pSink, p);
    }
  }
}; 

/*----------------------------------------------------------------------------------*/

OMS_ClassInfo* OMS_ContainerDictionary::RegisterClass (IliveCacheSink*        lcSink, 
                                                       const char*            ClassName,
                                                       const ClassIDRef       guid,
                                                       const ClassIDPtr       pBaseClassGuid,
                                                       tsp00_Int4             keyPos,         // PTS 1122540  
                                                       tsp00_Int4             keyLen,         // PTS 1122540
                                                       bool                   partitionedKey, // PTS 1122540
                                                       size_t                 ObjectSize, 
                                                       void*                  vtptr) 
{
  OMS_ClassInfo* p = FindGuid(guid);
  if (NULL == p) 
  {
    OMS_ClassInfo* pBaseClass = NULL;
    int HashSlot = HashValue(guid, OMS_ContainerDictionary::MaxHeadEntries);
#ifdef	LIVECACHE_INTGUIDS
    // now register GUID for array objects, if needed
    ClassID arrayBaseGuid = omsMaskGuid(guid);
    if (arrayBaseGuid != guid) {
      // we need to register base guid for array objects of this class
      OMS_ClassInfo *pb = RegisterClass(lcSink, ClassName, arrayBaseGuid,
        pBaseClassGuid, keyPos, keyLen, partitionedKey, sizeof(void*), vtptr);
      pb->SetBaseClass();
    }
    if (0 != pBaseClassGuid) {
      pBaseClass = FindGuid(pBaseClassGuid);
#else
    if (NULL != pBaseClassGuid) {
      pBaseClass = FindGuid(*pBaseClassGuid);
#endif
      if (NULL == pBaseClass)
      {
        OMS_Globals::Throw(DbpError (DbpError::DB_ERROR, e_unknown_guid, "RegisterClass (Base Class)", __MY_FILE__, __LINE__));
      }
      else
      {
        pBaseClass->SetBaseClass();
      }
    }
    p = new OMS_ClassInfo (ClassName, guid, pBaseClass, keyPos, keyLen, partitionedKey, 
                           ObjectSize, vtptr, m_classInfoHead[HashSlot]);
    m_classInfoHead[HashSlot] = p;
  }
  else
  { // check, if previous and current definition are compatible
    if (ObjectSize != (size_t) p->GetObjectSize())
    {
      DbpBase base(lcSink);
      base.dbpOpError("Register Class :  Size old %d, new Size %d", 
        ObjectSize, p->GetObjectSize());
      OMS_Globals::Throw(DbpError (DbpError::DB_ERROR, e_incompatible_datatypes, "RegisterClass", __MY_FILE__, __LINE__));
    }
  }
  return p;
}

/*----------------------------------------------------------------------------------*/

OMS_ContainerInfo* OMS_ContainerDictionary::RegisterContainer (IliveCacheSink* lcSink,
                                                               const ClassIDRef       guid, 
                                                               const char*            ClassName, 
                                                               size_t                 PersistentSize, 
                                                               size_t                 ObjectSize,
                                                               const ClassIDPtr       pBaseClass,
                                                               tsp00_Int4             keyPos,         // PTS 1122540
                                                               tsp00_Int4             keyLen,         // PTS 1122540
                                                               bool                   partitionedKey, // PTS 1122540
                                                               OmsSchemaHandle        sh, 
                                                               OmsContainerNo         ContainerNo,
                                                               void*                  vtptr,
                                                               size_t                 arrayByteSize) 
{
  HRESULT      hr = S_OK;
  tsp00_Int2   DBError = 0;
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  cs.Enter();
  ClassID rguid = guid;
  if (arrayByteSize) {
    omsMakeArrayGuid(rguid, arrayByteSize);
  }
  OMS_ContainerInfo* p = FindGuid (rguid, sh, ContainerNo);
  if (NULL == p) {
    tgg01_ContainerId   containerId;
    tsp00_KnlIdentifier knlIdentifier;
    OMS_Globals::MakeKnlIdentifier(ClassName, knlIdentifier);
    GUID_TEMP(lguid, rguid);
    hr = lcSink->RegisterClass (sh, CONST_CAST(GUID*, &lguid), &knlIdentifier, (tsp00_Int4) PersistentSize,
      ContainerNo, keyPos, keyLen, partitionedKey, 
      (unsigned char*) &containerId, &DBError);
    if ((!FAILED(hr)) && (0 == DBError)) {
      ++omsContainerHandle;
      OMS_ClassInfo* pClass = RegisterClass (lcSink, ClassName, rguid, pBaseClass, 
        keyPos, keyLen, partitionedKey, ObjectSize, vtptr);
      p = new OMS_ContainerInfo (pClass, containerId, omsContainerHandle, PersistentSize, sh, ContainerNo);
      this->Insert(p);
    }
  }
  if (FAILED(hr)) {
    OMS_Globals::Throw(DbpError (DbpError::HRESULT_ERROR, hr, __MY_FILE__, __LINE__));
  }
  if (0 != DBError) {
    OMS_Globals::Throw(DbpError (DbpError::DB_ERROR, DBError, "RegisterClass", __MY_FILE__, __LINE__));
  }
  return p;
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionary::Delete (IliveCacheSink* lcSink, OMS_ContainerInfo* pContainerInfo)
{
  OMS_TRACE(omsTrContainerDir, lcSink, "OMS_ContainerDictionary::Delete : " << pContainerInfo->GetClassInfoPtr()->GetClassName());
  int clsidHashSlot = pContainerInfo->GetContainerHandle() % OMS_ContainerDictionary::MaxHeadEntries;
  OMS_ContainerInfoPtr* prev = &m_clsidHead[clsidHashSlot]; 
  OMS_ContainerInfoPtr  curr = *prev;
  bool found = false;
  while (NULL != curr) {
    if (curr == pContainerInfo) {
      *prev = pContainerInfo->m_clsidHashNext;
      found = true;
      break;
    }
    else {
      prev = &curr->m_clsidHashNext;
      curr = curr->m_clsidHashNext;
    }
  }
  if (found) {
    found = false;
    int guidHashSlot  = HashValue(pContainerInfo->m_clsInfo->GetGuid(), pContainerInfo->m_schema,
      pContainerInfo->m_ContainerNo, OMS_ContainerDictionary::MaxHeadEntries);
    prev = &m_guidHead[guidHashSlot];
    curr = *prev;
    while (NULL != curr) {
      if (curr == pContainerInfo) {
        *prev = pContainerInfo->m_guidHashNext;
        found = true;
        break;
      }
      else {
        prev = &curr->m_guidHashNext;
        curr = curr->m_guidHashNext;
      }
    }
  }
  if (found) {
    pContainerInfo->DeleteSelf();
  }
  else {
    OMS_Globals::Throw(DbpError (DbpError::DB_ERROR, -28999, "OMS_ContainerDictionary::Delete", __MY_FILE__, __LINE__));
  }
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionary::Insert (OMS_ContainerInfo* pContainerInfo)
{ 
  int clsidHashSlot = pContainerInfo->GetContainerHandle() % OMS_ContainerDictionary::MaxHeadEntries;
  int guidHashSlot  = HashValue(pContainerInfo->m_clsInfo->GetGuid(), pContainerInfo->m_schema,
    pContainerInfo->m_ContainerNo, OMS_ContainerDictionary::MaxHeadEntries);
  pContainerInfo->m_clsidHashNext = m_clsidHead[clsidHashSlot]; 
  m_clsidHead[clsidHashSlot]      = pContainerInfo;
  pContainerInfo->m_guidHashNext  = m_guidHead[guidHashSlot];
  m_guidHead[guidHashSlot]        = pContainerInfo;
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionary::ReleaseContainerInfo(IliveCacheSink* lcSink, OMS_ContainerInfoPtr p) 
{
  OMS_SinkCriticalSection cs(lcSink, RGN_CLASS_DIR);
  if (!OMS_Globals::m_globalsInstance->InSimulator()) {
    cs.Enter();
  }
  OMS_TRACE(omsTrContainerDir, lcSink, "ReleaseContainerInfo, RefCnt : " << p->m_refCnt);
  OMS_TRACE(omsTrContainerDir, lcSink, "Name : " << p->GetClassInfoPtr()->GetClassName() << "Schema : " 
    << p->GetSchema() << ", CNo : " << p->m_ContainerNo);
  if ((0 == p->DecRefCnt()) && (p->IsDropped())) {
    this->Delete(lcSink, p);
  }
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionary::Shutdown()
{
  int hashSlot;
  for (hashSlot = 0; hashSlot < OMS_ContainerDictionary::MaxHeadEntries; ++hashSlot)
  {
    OMS_ClassInfo* p = m_classInfoHead[hashSlot];
    while (NULL != p)
    {
      OMS_ClassInfo* pToFree = p;
      p = p->m_HashNext;
      pToFree->DeleteSelf();
    }
  }
  for (hashSlot = 0; hashSlot < OMS_ContainerDictionary::MaxHeadEntries; ++hashSlot)
  {
    OMS_ContainerInfo* curr = m_clsidHead[hashSlot];
    while (NULL != curr)
    {
      OMS_ContainerInfo* pToFree = curr;
      curr = curr->m_clsidHashNext;
      pToFree->DeleteSelf();
    }
  }
  this->Init();
}

/*----------------------------------------------------------------------------------*/

OMS_ContainerDictionaryIterator::OMS_ContainerDictionaryIterator(OMS_ContainerDictionary* clsDir) 
  : m_clsDir(clsDir), m_index(0), m_curr(clsDir->m_clsidHead[0]) 
{
  while ((NULL == m_curr) && (++m_index < OMS_ContainerDictionary::MaxHeadEntries)) {
    m_curr = m_clsDir->m_clsidHead[m_index];
  }
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionaryIterator::reset(OMS_ContainerDictionary* clsDir)
{
  m_clsDir = clsDir;
  m_index = 0;
  m_curr = clsDir->m_clsidHead[0];
  while ((NULL == m_curr) && (++m_index < OMS_ContainerDictionary::MaxHeadEntries)) {
    m_curr = m_clsDir->m_clsidHead[m_index];
  }
}

/*----------------------------------------------------------------------------------*/

void OMS_ContainerDictionaryIterator::operator++() 
{
  if (NULL != m_curr) { 
    m_curr = m_curr->m_clsidHashNext;
    while (NULL == m_curr) { // PTS 1106820
      ++m_index;
      if (m_index < OMS_ContainerDictionary::MaxHeadEntries) {
        m_curr = m_clsDir->m_clsidHead[m_index];
      }
      else {
        return;
      }
    }
  }
}

/*----------------------------------------------------------------------------------*/

