/*

    ========== 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_CLASSIDHASH_HPP
#define __OMS_CLASSIDHASH_HPP

#include "Oms/OMS_ClassIdHashDef.hpp"
#include "Oms/OMS_ClassIdEntryDef.hpp"
#include "Oms/OMS_ContainerInfo.hpp"
#include "Oms/OMS_ContextDef.hpp"
#include "Oms/OMS_FreeListHeaderDef.hpp"
#include "Oms/OMS_Globals.hpp"
#include "Oms/OMS_ClassIdEntry.hpp"
#include "Oms/OMS_FreeListHeader.hpp"

/*----------------------------------------------------------------------*/
/* Implementation of class OMS_ClassIdHash inline methods               */
/*----------------------------------------------------------------------*/

inline bool OMS_ClassIdHash::CheckIfNotDropped(OMS_ClassIdEntry* curr, bool excsafe) 
{
  if (curr->GetContainerInfoPtr()->IsDropped()) {
    if (excsafe) {
      return false;
    }
    this->ThrowUnknownContainer(curr);
  }
  return true;
}

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

inline void OMS_ClassIdHash::ClearFreeLists(int caller) 
{
  OMS_FreeListHeader* p = freeListHeader;
  while (p != NULL) {
    if (m_context->IsVersion()) {
      OmsObjectContainerPtr curr = p->free;
      OmsObjectContainerPtr toFree;
      while (NULL != curr) {
        toFree  = curr;
        curr    = curr->GetNextFreeList();
        p->free = curr;
        toFree->InitializeForAllocator(caller);
        m_context->deallocate(toFree);
      }
    }
    p->free = NULL;
    p = p->next;
  }
}

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

inline void OMS_ClassIdHash::HashInsert (OMS_ClassIdEntry* h)
{
  int hashSlot;
  hashSlot              = (h->GetContainerHandle()) % headentries;
  h->m_clsidHashNext    = m_clsidHead[hashSlot];
  m_clsidHead[hashSlot] = h;

  hashSlot = HashValue(h->GetGuid(), h->GetSchema(), h->GetContainerNo(), headentries);
  h->m_guidHashNext    = m_guidHead[hashSlot];
  m_guidHead[hashSlot] = h;

  h->m_freeHead = this->GetFreeListHeader(h->GetObjectSize()); 
  h->m_freeHeadInVersion = this->GetFreeListHeader((h->GetObjectSize() + 
    3*sizeof(void*) - 1) & ~(sizeof(void*) - 1));
  m_classDir.HashInsert(h->GetContainerInfoPtr()->m_clsInfo, h->m_freeHead, h->m_freeHeadInVersion);
  ++m_count;
  if (m_count > 2 * headentries)
  {
      this->Resize();
  }
};

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

inline OMS_ClassIdEntry* OMS_ClassIdHash::HashFindViaContainerHandle (tsp00_Uint4 containerHandle, bool checkExistence, bool excsafe) 
{
  int hashSlot           = containerHandle % headentries;
  OMS_ClassIdEntry* curr = m_clsidHead[hashSlot];
  while (curr != NULL) {
    if (curr->GetContainerHandle() == containerHandle) {
      if (checkExistence) {
        if (!CheckIfNotDropped(curr)) {
          return NULL;
        }
      }
      return curr;
    }
    curr = curr->m_clsidHashNext;
  }
  OMS_ContainerInfo* pContainerInfo = OMS_Globals::FindContainerInfo(m_context->LcSink(), containerHandle);
  if (NULL != pContainerInfo) {
    curr = new(m_context) OMS_ClassIdEntry (pContainerInfo, m_context);
    if (NULL != curr) {
      this->HashInsert (curr);
    }
    return curr;
  }
  OMS_TRACE(omsTrAlways, m_context->LcSink(), "OMS_ClassIdHash::HashFindViaContainerHandle : " << containerHandle << " not found");
  if (!excsafe) {
    throw DbpError (DbpError::DB_ERROR, e_unknown_guid, __MY_FILE__, __LINE__);
  }
  return NULL;
};

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

inline OMS_ClassIdEntry* OMS_ClassIdHash::HashFind (const ClassIDRef guid, OmsSchemaHandle sh, OmsContainerNo ContainerNo) 
{
  int hashSlot = HashValue(guid, sh, ContainerNo, headentries);
  OMS_ClassIdEntry* curr = m_guidHead[hashSlot];
  while (curr != NULL) {
    if (omsIsCompatibleGuid(curr->GetGuid(), guid) && (curr->GetSchema() == sh) && (curr->GetContainerNo() == ContainerNo)) {
      CheckIfNotDropped(curr);
      return curr;
    }
    curr = curr->m_guidHashNext;
  }
  return this->AutoRegister(guid, sh, ContainerNo);
}

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

inline OMS_ClassIdEntry* OMS_ClassIdHash::HashFindForReg (const ClassIDRef guid, OmsSchemaHandle sh, OmsContainerNo ContainerNo) 
{
  int hashSlot = HashValue(guid, sh, ContainerNo, headentries);
  OMS_ClassIdEntry* curr = m_guidHead[hashSlot];
  while (curr != NULL) {
    if (omsIsCompatibleGuid(curr->GetGuid(), guid) && (curr->GetSchema() == sh) && (curr->GetContainerNo() == ContainerNo)) {
      CheckIfNotDropped(curr);
      return curr;
    }
    curr = curr->m_guidHashNext;
  }
  return this->AutoRegisterForReg(guid, sh, ContainerNo);
}

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

inline OMS_ClassIdHash::Iter OMS_ClassIdHash::First ()
{
  Iter iter(this);
  for (iter.headIndex = 0; iter.headIndex < headentries; ++iter.headIndex) {
    iter.curr = m_clsidHead[iter.headIndex];
    if (NULL != iter.curr) {
      break;
    }
  }
  return iter;
};

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

inline OMS_GuidEntry* OMS_ClassIdHash::GetClassInfo(const ClassIDRef guid) 
{
  OMS_GuidEntry* found = m_classDir.HashFind(guid);
  if (NULL == found) {
    OMS_ClassInfo* pClassInfo = OMS_Globals::FindClassInfo(m_context->LcSink(), guid);
    if (NULL != pClassInfo) {
      OMS_FreeListHeader* freeHead = this->GetFreeListHeader(pClassInfo->GetObjectSize()); 
      OMS_FreeListHeader* freeHeadInVersion = this->GetFreeListHeader(
        (pClassInfo->GetObjectSize() + 3*sizeof(void*) - 1) & ~(sizeof(void*) - 1));
      m_classDir.HashInsert(pClassInfo, freeHead, freeHeadInVersion);
      found = m_classDir.HashFind(guid); 
    }
    else {
      OMS_TRACE(omsTrAlways, m_context->LcSink(), "OMS_ClassIdHash::GetClassInfo : " << guid << " not found");
      throw DbpError (DbpError::DB_ERROR, e_unknown_guid, __MY_FILE__, __LINE__);
    }
  }
  return found;
}

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

inline OMS_ClassIdHash::Iter::Iter(OMS_ClassIdHash* h) : hash(h), curr(NULL), headIndex(0) 
{
}

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

inline OMS_ClassIdHash::Iter::Iter(const Iter& iter) 
{
  *this = iter;
}

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

inline void OMS_ClassIdHash::Iter::operator++() 
{
  if (NULL != curr) {
    curr = curr->m_clsidHashNext;
  }
  if (NULL == curr) {
    ++headIndex; 
    while (headIndex < hash->headentries) {
      curr = hash->m_clsidHead[headIndex];
      if (NULL != curr) {
        break;
      }
      ++headIndex;
    }
  }
}

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

inline OMS_ClassIdHash::Iter::operator bool() const 
{
  return (curr != NULL);
}

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

inline OMS_ClassIdEntryPtr OMS_ClassIdHash::Iter::operator()() const
{
  return curr;
}


#endif  // __OMS_CLASSIDHASH_HPP
