/*!
 * \file    OMS_Globals.cpp
 * \author  MarkusSi, PeterG
 * \brief   Global methods and variables.
 */
/*

    ========== 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_Defines.h"
#include "Oms/OMS_Globals.hpp"
#include "Oms/OMS_DummyKernelCallback.hpp"
#include "Oms/OMS_KernelDefaultInterface.hpp"
#include "Oms/OMS_DumpInterface.hpp"
#include "Oms/OMS_ContainerDictionary.hpp"
#include "Oms/OMS_VersionDictionary.hpp"
#include "Oms/OMS_MonitorDirectory.hpp"
#include "Oms/OMS_Session.hpp"
#include "Oms/OMS_ObjectContainer.hpp"
#include "Oms/OMS_AFX.h"
#include "SAPDBCommon/SAPDB_MemCopyMove.hpp"
#include "RunTime/Synchronisation/RTESync_Spinlock.hpp"
#include "hsp77.h"

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

static OMS_DummyKernelCallback DummySink;

IliveCacheSink *OMS_GetProxySinkAddr() 
{
  return &DummySink; 
}

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

extern "C" void co10_initSingletons()
{
  // initialize all singletons to sensible values - on load in xregcomp
  OMS_Globals::InitSingletons();
}

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

extern "C" void* co10_GetInterface() 
{
  if (OMS_Globals::m_libOmsInterfaceInstance == NULL)
    OMS_Globals::InitSingletons();
  return reinterpret_cast<void*>(OMS_Globals::m_libOmsInterfaceInstance);
}

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

extern void omsGetLiveCacheVersion(char* pBuf, int bufSize)
{
  if (NULL == OMS_Globals::KernelInterfaceInstance->GetKnlVersion(pBuf, bufSize))
  {
    throw DbpError (e_buffer_too_small, "omsGetLiveCacheVersion");
  }
}

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

extern void omsSimConsoleMessage(const char* msg)
{
  if (OMS_Globals::KernelInterfaceInstance) OMS_Globals::KernelInterfaceInstance->ConsoleMessage(msg);
}

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

extern void AFX_EXT_CLASS omsResetForSimulator()
{
  OMS_Globals::m_globalsInstance->ResetInfo();
}

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

OMS_Globals			            *OMS_Globals::m_globalsInstance = NULL;
OMS_GetSinkPtrFunc		      OMS_Globals::GetSinkAddrFunc = OMS_GetProxySinkAddr;
LVC_KernelInterface*		    OMS_Globals::KernelInterfaceInstance = NULL;
OMS_LibOmsInterfaceInstance	*OMS_Globals::m_libOmsInterfaceInstance = NULL;

static RTESync_Spinlock omsSyncDumpError;

static double			omsGlobalsInstanceSpace[sizeof(OMS_Globals) / sizeof(double) + 1];
static double			omsVersionDictInstanceSpace[sizeof(OMS_VersionDictionary) / sizeof(double) + 1];
static double			omsContainerDictInstanceSpace[sizeof(OMS_ContainerDictionary) / sizeof(double) + 1];
static double			omsMonitorDirInstanceSpace[sizeof(OMS_MonitorDirectory) / sizeof(double) + 1];
static double			omsContainerIterInstanceSpace[sizeof(OMS_ContainerDictionaryIterator) / sizeof(double) + 1];
static void       *omsLibOmsInterfaceInstanceSpace = NULL;

/*----------------------------------------------------------------------*/
/* PTS 1115231 */
externCpp
bool omsIsUnicodeInstance()
{
    IliveCacheSink* pSink = OMS_Globals::GetCurrentLcSink();
    bool  isUnicodeInstance = false;
    if (pSink)
    {
        short error;
        pSink->IsUnicodeInstance(isUnicodeInstance, error);
        if (0 != error)
        {
            throw DbpError(error, "omsIsUnicodeInstance");
        }
    }
    return isUnicodeInstance;
}

/*----------------------------------------------------------------------*/
/* PTS 1135287 */
externCpp
bool omsIsOneDBSystem()
{
    return OMS_Globals::GetKernelInterface()->IsOneDBSystem();
}

/*----------------------------------------------------------------------*/
OMS_Globals::OMS_Globals()
  : m_versionDictionary(*reinterpret_cast<OMS_VersionDictionary*>(omsVersionDictInstanceSpace))
  , m_classDictionary(*reinterpret_cast<OMS_ContainerDictionary*>(omsContainerDictInstanceSpace))
  , m_monitorDirectory(*reinterpret_cast<OMS_MonitorDirectory*>(omsMonitorDirInstanceSpace))
  , ContainerIter(*reinterpret_cast<OMS_ContainerDictionaryIterator*>(omsContainerIterInstanceSpace))
  , m_versionIter() 
  , m_host(HostUnknown)
  #if defined(OMSTST)
  , m_isOmsTestLib(true)
  #else
  , m_isOmsTestLib(false)
  #endif 
  , m_cntDumpErrors(0)
  , m_heapThreshold(100)
  , m_omsVersionThreshold(MAX_INT4_SP00)
  , m_tracer(0)
  , m_resetSink() 
  , m_nilOid()
  , m_versionBuffer()  // PTS 1128597
{
  new(&m_versionDictionary) OMS_VersionDictionary();
  new(&m_classDictionary) OMS_ContainerDictionary();
  new(&m_monitorDirectory) OMS_MonitorDirectory();
  new(&ContainerIter) OMS_ContainerDictionaryIterator();
  m_nilSeq.gg91SetNilRef();
}

void OMS_Globals::InitSingletons()
{
  if (m_globalsInstance == NULL) 
    m_globalsInstance = new(&omsGlobalsInstanceSpace) OMS_Globals();
  if (KernelInterfaceInstance == NULL) 
    KernelInterfaceInstance = &OMS_KernelDefaultInterface::Instance();
  if (m_libOmsInterfaceInstance == NULL)
    m_libOmsInterfaceInstance = new(&omsLibOmsInterfaceInstanceSpace) OMS_LibOmsInterfaceInstance();
}

void OMS_Globals::MakeKnlIdentifier (const char* Source, tsp00_KnlIdentifier& Dest) {
  size_t copyLen = strlen(Source);
  if (copyLen > sizeof(Dest)) {
    Throw(DbpError (DbpError::DB_ERROR, -2014, "too long identifier", __MY_FILE__, __LINE__));
    //copyLen = sizeof(Dest);
  }
  SAPDB_MemMoveNoCheck(&Dest[0], Source, copyLen);
  memset(&Dest[copyLen], ' ', sizeof(Dest) - copyLen);
}

void OMS_Globals::ResetInfo()
{
  // TODO: this is not perfect, but it will do the job...
  m_versionDictionary.Shutdown();
  m_classDictionary.Shutdown();
}

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

void OMS_Globals::Dump(OMS_DumpInterface& dumpObj)
{
  this->m_classDictionary.Dump(dumpObj);
  this->DumpVersionDirectory(dumpObj);
  OMS_Session* pSession = REINTERPRET_CAST(OMS_Session*, dumpObj.GetOmsSession());
  if (NULL != pSession)
  {
    pSession->Dump(dumpObj);
  }
}

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

bool OMS_Globals::ActionOnError(tsp00_Int4 errorNo, bool &dump, bool &stop, bool &flush) 
{
  dump = stop = flush = false;

  RTESync_LockedScope sync(omsSyncDumpError);
  for (int ix = 0; ix < m_cntDumpErrors; ++ix)
  {
    if (m_actionOnError[ix].errorNo == errorNo)
    {
      stop  = m_actionOnError[ix].stop;
      flush = m_actionOnError[ix].flush;
      dump  = m_actionOnError[ix].dump;

      // Ensure, that 'dump' and 'stop' are only executed once. 
      // If 'flush' is not switched on, then the whole entry can
      // be deleted
      if (!m_actionOnError[ix].flush){
        while (ix < m_cntDumpErrors - 1){
          m_actionOnError[ix] = m_actionOnError[ix+1];
        }
        --m_cntDumpErrors;
      }
      else {
        m_actionOnError[ix].stop = false;
        m_actionOnError[ix].dump = false;
      }

      return (stop || flush || dump);
    }
  }
  return false;
}

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

IliveCacheSink* OMS_Globals::GetCurrentLcSink()
{
  return KernelInterfaceInstance->GetSinkPtr();
}

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

void OMS_Globals::DumpVersionDirectory(OMS_DumpInterface& dumpObj)
{
  dumpObj.SetDumpLabel(LABEL_OMS_VERSION);
   
  OMS_VersionDictionary::Iter iter(dumpObj.Synchronize() ? OMS_LOCK_SHARED : OMS_NO_LOCK);
  while (true){
    OMS_Context *pContext = iter.GetFirstInSlot();
    if (pContext == NULL){
      break;
    }

    while (pContext){
    pContext->Dump(dumpObj);
      pContext = iter.GetNextInSlot();
    }
  }
}

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

bool OMS_Globals::InSimulator() const
{
    return (m_host == HostSimulator);
}

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

void OMS_Globals::SetDumpError(tsp00_Int4 e, OMS_DiagnoseAction action)
{
  RTESync_LockedScope sync(omsSyncDumpError);
  for (int i=0; i<m_cntDumpErrors; ++i){
    if (m_actionOnError[i].errorNo == e){
      if (action == OMS_DIAGNOSE_DUMP){
        m_actionOnError[i].dump = true;
      }
      else if (action == OMS_DIAGNOSE_STOP){
        m_actionOnError[i].stop = true;
      }
      else if (action == OMS_DIAGNOSE_FLUSH){
        m_actionOnError[i].flush = true;
      }
      return;
    }
  }
  if (m_cntDumpErrors < MAX_DUMP_ERRORS){
    m_actionOnError[m_cntDumpErrors].errorNo = e;
    if (action == OMS_DIAGNOSE_DUMP){
      m_actionOnError[m_cntDumpErrors].dump = true;
    }
    else if (action == OMS_DIAGNOSE_STOP){
      m_actionOnError[m_cntDumpErrors].stop = true;
    }
    else if (action == OMS_DIAGNOSE_FLUSH){
      m_actionOnError[m_cntDumpErrors].flush = true;
    }
    ++m_cntDumpErrors;
  }
}

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

void OMS_Globals::SetHost(bool isKernel)
{
  m_host = isKernel ? HostKernel : HostSimulator;
}

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

void OMS_Globals::SetOmsVersionThreshold(tsp00_Uint4 threshold) /* PTS 1110149 */
{
  // 100 has been wrong default, set to wanted default
  m_omsVersionThreshold = (100 == threshold) ? 2097152 : threshold;
}

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

OMS_TraceInterface* OMS_Globals::GetTracer()
{
  return m_tracer;
}

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

void OMS_Globals::SetTracer(OMS_TraceInterface* pTraceObj)
{
  m_tracer = pTraceObj;
}

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

OMS_ContainerInfo* OMS_Globals::FindContainerInfo(IliveCacheSink* lcSink, tsp00_Uint4 containerHandle) 
{
  return OMS_Globals::m_globalsInstance->m_classDictionary.FindViaContainerHandle(lcSink, containerHandle);
}

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

OMS_ContainerInfo* OMS_Globals::FindContainerInfo(IliveCacheSink* lcSink, const ClassIDRef guid, OmsSchemaHandle Schema, OmsContainerNo ContainerNo) 
{
  return  OMS_Globals::m_globalsInstance->m_classDictionary.FindViaGuid(lcSink, guid, Schema, ContainerNo);
}


OMS_ClassInfo* OMS_Globals::FindClassInfo(IliveCacheSink* lcSink, const ClassIDRef guid) 
{
  return OMS_Globals::m_globalsInstance->m_classDictionary.FindGuid(lcSink, guid);
}

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

void OMS_Globals::Throw(const DbpError& e)
{

  // Check whether a certain action for this error-code is registered.
  // Registration is possible via the sql-command:
  //    diagnose object catch <error number> dump|stop|flush
  bool dump, stop, flush;
  IliveCacheSink* pLcSink = GetCurrentLcSink();
  if (pLcSink && m_globalsInstance->ActionOnError(e.dbpError(), dump, stop, flush)){
    tsp00_Int2      errorNo     = (short)e.dbpError();
    tsp00_Int4      BufLen      = sizeof(errorNo);
    tsp00_Int2      DummyError  = 0;
    if (dump){
      BufLen = sizeof(errorNo);
      OMS_HResult hr  = pLcSink->MultiPurpose (m_diagnose, mm_outcopy, &BufLen, 
                                              (unsigned char*)&errorNo, 
                                              &DummyError);
    }
    if (stop){
      BufLen = sizeof(errorNo);
      OMS_HResult hr  = pLcSink->MultiPurpose (m_diagnose, mm_write_off, &BufLen, 
                                              (unsigned char*)&errorNo, 
                                              &DummyError);
    }
    if (flush){
      BufLen = sizeof(errorNo);
      OMS_HResult hr  = pLcSink->MultiPurpose (m_diagnose, mm_file, &BufLen, 
                                              (unsigned char*)&errorNo, 
                                              &DummyError);
    }
  }

  if (e_new_failed == e.dbpError()) // PTS 1109338
  {
    pasbool*         pToCancel;
    OMS_Session* pSession;
    tsp00_TaskId     taskId;
    IliveCacheSink* lcSink = OMS_Globals::GetCurrentLcSink();
    lcSink->GetDefaultContext(REINTERPRET_CAST(void**, &pSession), &pToCancel, taskId);
    if (NULL != pSession)
    {
      pSession->IncOutOfMemory();
    }
  }
  throw e;
}

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

void OMS_Globals::DetachContainerInfo(IliveCacheSink* lcSink, OMS_ContainerInfo* p) 
{
  m_globalsInstance->m_classDictionary.ReleaseContainerInfo (lcSink, p);
}

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

//PTS 1117690
// strlen on strings of type OmsTypeWyde
int OMS_Globals::WideStrlen(const OmsTypeWyde* s)
{
  int len=-1;
  while (s[++len]);
  return len;
}

/*----------------------------------------------------------------------*/
// PTS 1124170
int OMS_Globals::GetLockIdForVersionDirectory (const OmsVersionId &versionId) const { 
  return m_versionDictionary.GetSingleLockId(versionId);
}

/*----------------------------------------------------------------------*/
// PTS 1124170
bool OMS_Globals::GetLockMethodForVersionDictionary() const{  
  return m_versionDictionary.UseRWLocks();
}


void OmsObjectContainer::error(char* msg, OmsObjectContainer*p)
{
  char buf[64];
  IliveCacheSink* pLcSink = OMS_Globals::GetCurrentLcSink();
  DbpBase base(pLcSink);
  base.dbpOpError(msg);
  sp77sprintf(buf, sizeof(buf), "HashNext: %p", p->m_hashnext);
  base.dbpOpError(buf);
  sp77sprintf(buf, sizeof(buf), "Oid: %d.%d(%d)", p->m_oid.getPno(), p->m_oid.getPagePos(), p->m_oid.getGeneration());
  base.dbpOpError(buf);
  sp77sprintf(buf, sizeof(buf), "ObjSeq: %d.%d", p->m_objseq.gg91RefPno(), p->m_objseq.gg91RefPos());
  base.dbpOpError(buf);
  sp77sprintf(buf, sizeof(buf), "State: %d", p->m_state);
  base.dbpOpError(buf);
  sp77sprintf(buf, sizeof(buf), "VerState: %d", p->m_verstate);
  base.dbpOpError(buf);
  sp77sprintf(buf, sizeof(buf), "BeforeImages: %d", p->m_beforeImages);
  base.dbpOpError(buf);
#if defined(BIT64)
  base.dbpOpError(buf);
  sp77sprintf(buf, sizeof(buf), "Filler: %d", p->filler);
#endif
  sp77sprintf(buf, sizeof(buf), "ContainerInfo: %p", p->m_containerInfo);
  base.dbpOpError(buf);
}

