/****************************************************************************

  module      : SAPDBErr_MessageList.cpp

  -------------------------------------------------------------------------

  responsible : RaymondR

  special area: message list handling
  description : Creating, queueing and managing message lists



  last changed: 2000-06-16  14:55
  see also    : 

  -------------------------------------------------------------------------

  copyright:    (c) 1999-2004 SAP AG




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




*****************************************************************************/



/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#if defined(WIN32)
# include <tchar.h>
#else
# include <ctype.h>
# include <stdio.h>
# include <string.h>
#endif

#include  "RunTime/RTE_ISystem.hpp"
#include  "SAPDBCommon/SAPDB_Types.hpp"
#include  "SAPDBCommon/MemoryManagement/SAPDBMem_Alloca.h"
#include  "SAPDBCommon/ErrorsAndMessages/SAPDBErr_MessageList.hpp"
#include  "RunTime/MemoryManagement/RTEMem_RteAllocator.hpp"
#include  "SAPDBCommon/MemoryManagement/SAPDBMem_NewDestroy.hpp"

/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/

# define TIME_HEADER_FORMAT_STR                "%04d-%02d-%02d %02d:%02d:%02d"
# define TIME_HEADER_FORMAT_STR_LEN            19

# define BIG_TIME_HEADER_FORMAT_STR            "%04d-%02d-%02d %02d:%02d:%02d.%03d"
# define BIG_TIME_HEADER_FORMAT_STR_LEN        23

# define ADDITIONAL_ARGS_STR                   " Additional Arguments: "

/* not used */
//# define USE_NUM_OF_ARG_HINT

/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/

#define  NUM_OF_ELEMENTS(_Array) (sizeof(_Array) / sizeof(_Array[0]))
#define  ALIGNMENT_VAL_64BIT     sizeof( SAPDB_UInt8 )
#define  ALIGN_VAL_64BIT(_val)                    \
           ((((_val) - 1) / (ALIGNMENT_VAL_64BIT) + 1 ) * (ALIGNMENT_VAL_64BIT))


/*===========================================================================*
 *  LOCAL CLASSES, STRUCTURES, TYPES, UNIONS ...                             *
 *===========================================================================*/

const SAPDBErr_MessageList::MessageDateTime  
                SAPDBErr_MessageList::EmptyDateTimeValue = { 0,0,0,0,0,0,0,0 };


/*===========================================================================*
 *  STATIC/INLINE FUNCTIONS (PROTOTYPES)                                     *
 *===========================================================================*/



/*===========================================================================*
 *  METHODS                                                                  *
 *===========================================================================*/

void SAPDBErr_MessageList::FillMessageList( SAPDB_Char  const * const  Component,
                                            SAPDB_Char  const * const  FileName,
                                            SAPDB_UInt4 const          LineNumber,
                                            MessageType const          Type,
                                            MessageID   const          ID,
                                            SAPDB_UInt4 const          SystemRC,
                                            SAPDB_Char  const * const  Message,
                                            SAPDB_UInt4 const          NumOfArgs,
                                            SAPDB_Char  const * const  Arg0,
                                            SAPDB_Char  const * const  Arg1,
                                            SAPDB_Char  const * const  Arg2,
                                            SAPDB_Char  const * const  Arg3,
                                            SAPDB_Char  const * const  Arg4,
                                            SAPDB_Char  const * const  Arg5,
                                            SAPDB_Char  const * const  Arg6,
                                            SAPDB_Char  const * const  Arg7,
                                            SAPDB_Char  const * const  Arg8,
                                            SAPDB_Char  const * const  Arg9 )
{ 
  SAPDB_Char *  pMessageString    = NULL;
  SAPDB_UInt4   MessageStringSize = 0;

  if ( Message != NULL )
  {
    SAPDB_Char  const * Args[10];
    SAPDB_UInt4         ArgCnt  = 0;
#   if defined USE_NUM_OF_ARG_HINT
     SAPDB_UInt4         MaxArgs = (NUM_OF_ELEMENTS(Args) < NumOfArgs) ? NUM_OF_ELEMENTS(Args) : NumOfArgs;
#   else
     SAPDB_UInt4         MaxArgs = NUM_OF_ELEMENTS(Args);
#   endif

    MessageStringSize = (SAPDB_UInt4)(strlen( Message ) + strlen(ADDITIONAL_ARGS_STR) + 1);

    if ((ArgCnt < MaxArgs) && ( Arg0 != NULL )) { Args[ArgCnt] = Arg0; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg1 != NULL )) { Args[ArgCnt] = Arg1; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg2 != NULL )) { Args[ArgCnt] = Arg2; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg3 != NULL )) { Args[ArgCnt] = Arg3; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg4 != NULL )) { Args[ArgCnt] = Arg4; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg5 != NULL )) { Args[ArgCnt] = Arg5; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg6 != NULL )) { Args[ArgCnt] = Arg6; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg7 != NULL )) { Args[ArgCnt] = Arg7; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg8 != NULL )) { Args[ArgCnt] = Arg8; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }
    if ((ArgCnt < MaxArgs) && ( Arg9 != NULL )) { Args[ArgCnt] = Arg9; MessageStringSize += (SAPDB_UInt4)strlen( Args[ArgCnt++] ); }

    pMessageString = (SAPDB_Char*)alloca(MessageStringSize);

    if ( pMessageString != NULL )
      MessageStringSize = BuildMessageString  ( Message, Args, ArgCnt, pMessageString );
  }

  SAPDB_UInt4 DateTimeSize      = TIME_HEADER_FORMAT_STR_LEN + 1;
  SAPDB_UInt4 BigDateTimeSize   = BIG_TIME_HEADER_FORMAT_STR_LEN + 1; 
  SAPDB_UInt4 ComponentSize     = (SAPDB_UInt4)strlen(Component) + 1;
  SAPDB_UInt4 FileNameSize      = (SAPDB_UInt4)strlen(FileName) + 1;

  SAPDB_UInt4 MessageDataSize   = ALIGN_VAL_64BIT( ComponentSize + FileNameSize + 
                                                   DateTimeSize  + BigDateTimeSize + 
                                                   MessageStringSize + sizeof(*m_pMessageData) );

  m_pMessageData  = (MessageData*)RTEMem_RteAllocator::Instance().Allocate(MessageDataSize);
  m_pNextMessage  = NULL;
  m_ObjectRefCnt  = 1;
  m_NumOfMessages = 1;

  if ( m_pMessageData != NULL )
  {
#   if defined _DEBUG
    memset ( m_pMessageData, 0xAA, MessageDataSize );
#   endif

    m_pMessageData->Version                   = MESSAGE_LIST_VERSION;
    m_pMessageData->DataLen                   = MessageDataSize;
    m_pMessageData->DataRefCnt                = 1; // - This data is referenced once

    m_pMessageData->SystemRC                  = SystemRC;
    m_pMessageData->ID                        = ID;
    m_pMessageData->Type                      = Type;
    m_pMessageData->LineNumber                = LineNumber;

    m_pMessageData->DateTimeStringOffset      = 0;
    m_pMessageData->BigDateTimeStringOffset   = DateTimeSize;
    m_pMessageData->ComponentStringOffset     = m_pMessageData->BigDateTimeStringOffset  + BigDateTimeSize; 
    m_pMessageData->FileNameStringOffset      = m_pMessageData->ComponentStringOffset    + ComponentSize;
    m_pMessageData->MessageStringOffset       = m_pMessageData->FileNameStringOffset     + FileNameSize;
    m_pMessageData->Filler                    = 0;

                                                                                
    // --- build up data and time info!
    CreateDateTime( m_pMessageData->DateTime,
                    m_pMessageData->Strings + m_pMessageData->DateTimeStringOffset,    DateTimeSize,
                    m_pMessageData->Strings + m_pMessageData->BigDateTimeStringOffset, BigDateTimeSize );

    strcpy( m_pMessageData->Strings + m_pMessageData->ComponentStringOffset, Component );
    strcpy( m_pMessageData->Strings + m_pMessageData->FileNameStringOffset,  FileName );

    if ( pMessageString != NULL )
      strcpy( m_pMessageData->Strings + m_pMessageData->MessageStringOffset, pMessageString );
    else
      strcpy( m_pMessageData->Strings + m_pMessageData->MessageStringOffset, "" );
  }
}

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

SAPDBErr_MessageList::~SAPDBErr_MessageList()
{ 
  ClearMessageList();
}

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

void SAPDBErr_MessageList::ClearMessageList()
{ 
  // --- first of all we should release next message object!!!!
  if ( m_pNextMessage != NULL )
  {
    // - are we the only one holding this next message object?
    if ( m_pNextMessage->GetObjectRef() == 1 ) 
    {
      destroy (m_pNextMessage, RTEMem_RteAllocator::Instance());
      m_pNextMessage = NULL;
    }
    else
    {
      // --- subtract object reference of the next object!
      m_pNextMessage->SubtractObjectRef();
    }
  }

  SubtractObjectRef();

  if ( m_pMessageData != NULL )
  {
    SubtractDataRef();

    if ( GetDataRef() == 0 )
      RTEMem_RteAllocator::Instance().Deallocate(m_pMessageData);

    m_pMessageData  = NULL;
    m_pNextMessage  = NULL;
    m_NumOfMessages = 0;
  }
}

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

SAPDBErr_MessageList const& SAPDBErr_MessageList::operator = ( SAPDBErr_MessageList const &MessageList )
    
{
  // --- do nothing on Messagelist = Messagelist
  if ( &MessageList != this )
  {
    if ( m_pMessageData != NULL ) 
      ClearMessageList();   // --- remove old content first!

    CopyMessageList( MessageList );
  }

  return *this;
}

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

SAPDBErr_MessageList SAPDBErr_MessageList::operator + ( SAPDBErr_MessageList const &MessageList ) const
{
  SAPDBErr_MessageList       Result   = *this;

  Result.AppendNewMessage ( MessageList );

  return Result;
}

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

void SAPDBErr_MessageList::AppendNewMessage ( SAPDBErr_MessageList const &MessageList )
{
  SAPDBErr_MessageList*      pCurrMsg = this;

  // --- don't append empty message lists!
  if ( MessageList.m_pMessageData != NULL )
  {
    // --- is the current message list empty
    if ( pCurrMsg->m_pMessageData == NULL )
    {
      // --- copy the 
      pCurrMsg->CopyMessageList(MessageList);
    }
    else
    {
      SAPDBErr_MessageList*   pAppendMsg;

      // --- create a new 'MessageList' object which will be appended
      pAppendMsg = new (RTEMem_RteAllocator::Instance()) SAPDBErr_MessageList;

      if ( pAppendMsg != NULL )
      {
        // --- copy the 'MessageList' to the new append object
        pAppendMsg->CopyMessageList(MessageList);

        // --- step to the 'MessageList' end
        for (;pCurrMsg->m_pNextMessage != NULL;)
        {
          // --- check if a list splitting is necessary because 
          //     of a multiple object reference. This means two or more
          //     objects are sharing the same data. A append of a new message
          //     must not affect the contents of other message lists!!
          if ( pCurrMsg->m_pNextMessage->GetObjectRef() > 1 )
          {
            pCurrMsg->m_pNextMessage->SubtractObjectRef();
            pCurrMsg->m_pNextMessage = pCurrMsg->m_pNextMessage->SplitObjectList();
          }

          // --- is there another object?
          if ( pCurrMsg->m_pNextMessage != NULL )
          {
            // --- add the number of message we intend to append
            pCurrMsg->m_NumOfMessages += pAppendMsg->m_NumOfMessages;

            pCurrMsg = pCurrMsg->m_pNextMessage;
          }
        }

        // --- add the number of message we intend to append
        pCurrMsg->m_NumOfMessages += pAppendMsg->m_NumOfMessages;

        // ---  append the previously created append object
        pCurrMsg->m_pNextMessage = pAppendMsg;
      }
    }
  }
}

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

SAPDBErr_MessageList* SAPDBErr_MessageList::SplitObjectList()
{
  SAPDBErr_MessageList*      pMessageList;

  pMessageList = new (RTEMem_RteAllocator::Instance()) SAPDBErr_MessageList;
    
  if ( pMessageList != NULL )
  {
    pMessageList->m_pMessageData  = m_pMessageData;
    pMessageList->m_NumOfMessages = 1;
    pMessageList->AddDataRef();
    pMessageList->AddObjectRef();
    
    if ( m_pNextMessage != NULL )
      pMessageList->m_pNextMessage = m_pNextMessage->SplitObjectList();;
  }

  return pMessageList;
}

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

void SAPDBErr_MessageList::CopyMessageList( SAPDBErr_MessageList const &MessageList )
{
  m_pNextMessage  = MessageList.m_pNextMessage;
  m_pMessageData  = MessageList.m_pMessageData;
  m_NumOfMessages = MessageList.m_NumOfMessages;

  AddDataRef();
  AddObjectRef();

  // --- add object reference!
  if ( m_pNextMessage != NULL )
    m_pNextMessage->AddObjectRef();
}

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

void SAPDBErr_MessageList::CreateDateTime( MessageDateTime        &DateTime,
                                           SAPDB_Char * const     DateTimeStr,
                                           SAPDB_UInt4  const     DateTimeStrSize,
                                           SAPDB_Char * const     BigDateTimeStr,
                                           SAPDB_UInt4  const     BigDateTimeStrSize ) const
{ 
  RTE_ISystem::GetLocalDateTime( DateTime );

  sprintf ( (SAPDB_Char *)BigDateTimeStr, BIG_TIME_HEADER_FORMAT_STR,
            DateTime.Year,
            DateTime.Month,
            DateTime.Day,
            DateTime.Hour,
            DateTime.Minute,
            DateTime.Second,
            DateTime.Milliseconds );

  sprintf ( (SAPDB_Char *)DateTimeStr, TIME_HEADER_FORMAT_STR,
            DateTime.Year,
            DateTime.Month,
            DateTime.Day,
            DateTime.Hour,
            DateTime.Minute,
            DateTime.Second );

  return;
}

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

SAPDB_UInt4 SAPDBErr_MessageList::BuildMessageString  ( SAPDB_Char  const * const  Message,
                                                        SAPDB_Char  const * const  Args[],
                                                        SAPDB_UInt4 const          NumOfArgs,
                                                        SAPDB_Char  * const        MessageString )
{
  SAPDB_Char const *  pMessage       = Message;
  SAPDB_Char *        pMessageString = MessageString;
  SAPDB_UInt4         ArgCnt         = 0;
  SAPDB_Char const *  pArg;

  do
  {
    // --- format specifier '%' found?
    if ( *pMessage == '%' )
    {
      // --- only %s or %S allowed!
      if ((*(pMessage + 1) == 's' ) || ( *(pMessage + 1) == 'S' ))
      {
        pMessage++;
        
        if ( ArgCnt < NumOfArgs )
        {
          pArg = Args[ArgCnt++];
          
          while ( *pArg != '\0' )
            *(pMessageString++) = *(pArg++);
        }
      }
      else
        *(pMessageString++) = *pMessage;
    }
    else
      *(pMessageString++) = *pMessage;
  } 
  while ( *(pMessage++) != '\0' );  // --- loop until end of message


  // --- There are additional arguments but no more %s or %S format specifier.
  if ( ArgCnt < NumOfArgs )
  {
    pMessageString -= 1;
    pArg            = ADDITIONAL_ARGS_STR;

    while ( *pArg != '\0' )
      *(pMessageString++) = *(pArg++);
    
    do
    {
      pArg = Args[ArgCnt++];
      
      while ( *pArg != '\0' )
        *(pMessageString++) = *(pArg++);

      if ( ArgCnt < NumOfArgs )
        *(pMessageString++) = ',';
      else
        *(pMessageString++) = '\0';
    } 
    while ( ArgCnt < NumOfArgs );
  }
	
  return (SAPDB_UInt4)strlen(MessageString);
}

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

SAPDB_Char const * const  SAPDBErr_MessageList::Type( bool const  Long ) const
{ 
  SAPDB_Char const * MsgType;

  if ( m_pMessageData != NULL )
  {
    switch ( (MessageType)m_pMessageData->Type  )
    {
    case Error:
      if ( Long ) 
        MsgType = MESSAGE_TYPE_ERR;
      else
        MsgType = MESSAGE_TYPE_ERR_SHORT;
      break;
    case Warning:
      if ( Long ) 
        MsgType = MESSAGE_TYPE_WRN;
      else
        MsgType = MESSAGE_TYPE_WRN_SHORT;
      break;
    case Info:
      if ( Long ) 
        MsgType = MESSAGE_TYPE_INFO;
      else
        MsgType = MESSAGE_TYPE_INFO_SHORT;
      break;
    }
  }
  else
    MsgType = "";

  return MsgType; 
}

/*===========================================================================*
 *  END OF CODE                                                              *
 *===========================================================================*/
