/*!**************************************************************************

  module      : Log_ActionMultiple.cpp
  special area: Logging
  responsible : UweH
  last changed: 2004-07-29  12:00
  copyright:    Copyright by SAP AG, 2000
  description : implements class Log_ActionMultiple



    ========== licence begin  GPL
    Copyright (c) 2000-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 "Logging/Log_ActionMultiple.hpp"
#include "Logging/Log_AfterImage.hpp"
#include "Logging/Log_Transaction.hpp"
#include "KernelCommon/Kernel_VTrace.hpp"

#define ALIGNMENT 4
/* --------------------------------------------------------------------------*/
inline SAPDB_UInt Align(SAPDB_UInt value)
{
    return ((value-1) & ~(ALIGNMENT-1)) + ALIGNMENT;
}

/* --------------------------------------------------------------------------*/
void Log_ActionMultiple::ReadPersistentFormat(Data_SplitSpaceReader  &Reader,
                                              SAPDBMem_IRawAllocator &Allocator,
                                              bool                   &isOK)
{
    // DO NOT USE THIS FUNCTION
    isOK = false;
    return;
}

/* --------------------------------------------------------------------------*/
void Log_ActionMultiple::WritePersistentFormat(Data_SplitSpaceWriter &Writer,
                                               bool                  &isOK) const
{
    if ( m_Head.totalLengthOfActions == 0 )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                   "m_Head.totalLengthOfActions != 0"));
    if ( m_Head.actionCount == 0 )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,
                   "m_Head.actionCount != 0"));

    Head                          *pHead;
    Data_SplitSpaceWriter::Result  result;

    Writer.Reserve (sizeof(Head), reinterpret_cast<SAPDB_Byte*&>(pHead), result);

    isOK = Data_SplitSpaceWriter::moreSpaceAvailable == result;
    if ( ! isOK ) return;

    *pHead = m_Head;

	Writer.Write ( m_Buffer.GetBufferPointer(), m_Head.totalLengthOfActions, result );

    isOK = Data_SplitSpaceWriter::ok== result;
}

/* --------------------------------------------------------------------------*/
void Log_ActionMultiple::WriteToTrace (const char * Title) const
{
    Kernel_VTrace trace;
    
    if ( Title != NULL )
        trace << Title << " ";
	trace << "# Actions: " << m_Head.actionCount
          << ", totalLength: "  << m_Head.totalLengthOfActions
          << FlushLine;
    
    if ( IsEmpty() )
        return;
        
    SAPDB_UInt        actionno = 1;
    Log_AfterImage    image;
    SAPDB_UInt        actionLength;
    const SAPDB_Byte *pActionSpace;
    Iterator          actioniter  = GetFirstAction();

    while ( actioniter.GetAction(image, actionLength, pActionSpace) )
    {
        trace << "#" << actionno
              << " length: " << actionLength << FlushLine;
        image.WriteToTrace();
        actioniter.Next();
        actionno++;
    }
}

/* -------------------------------------------------------------------------------- */
bool
Log_ActionMultiple::Append (const Log_AfterImage &image,
                            const Log_IAction    &action)
{
    if ( ! m_Buffer.IsAssigned() )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"Initialization") );
	
    const SAPDB_UInt reservedSize = sizeof(ActionHeader) + action.GetPersistentLength();

    if ( reservedSize != Align(reservedSize) )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"NOT ALIGNED") );
    
    if ( action.GetType() == Log_MultipleActions )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"RECURSIVE CALL") );
    
    if ( ! m_Buffer.Reserve (reservedSize) )
        return false;

    Data_SplitSpaceWriter          writer (m_Buffer);
    SAPDB_Byte                    *pAuxHeader;
    Data_SplitSpaceWriter::Result  result;
    
    writer.Reserve (sizeof(ActionHeader), pAuxHeader, result);

    if ( result != Data_SplitSpaceWriter::moreSpaceAvailable )
    {
        #ifdef SAPDB_SLOW
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"error during append"));
        #endif
        return false;
    }

    ActionHeader &header = *(reinterpret_cast<ActionHeader*>(pAuxHeader));

    header = ActionHeader(sizeof(ActionHeader) + action.GetPersistentLength(),
                          image.GetActionType(),
                          image.GetSequence(),
                          image.GetLastUndoSequence());

    m_Head.totalLengthOfActions += reservedSize;
    m_Head.actionCount++;
    
    bool isOK = true;
	action.WritePersistentFormat (writer, isOK);
    if ( ! isOK )
        return false;
	#ifdef SAPDB_SLOW
    WriteToTrace ("Log_ActionMultiple::Append() end");
    #endif
    return true;
}

/* -------------------------------------------------------------------------------- */
void Log_ActionMultiple::ActionHeader::Crash()
{
    RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"log action type must be valid"));
}

/* -------------------------------------------------------------------------------- */
void Data_SplitSpaceBuffer::Delete()
{
    if ( 0 == m_Buffer )
        return;
    if ( ! IsEmpty() )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"split space buffer must be empty before Delete()"));
    m_pAllocator->Deallocate(m_Buffer);
    m_pAllocator      = 0;
    m_Buffer          = 0;
    m_BufferSize      = 0;
    m_FirstFreeOffset = 0;
    Release();
}
/* -------------------------------------------------------------------------------- */
bool Log_ActionMultiple::Iterator::GetAction( Log_AfterImage    &image,
                                              SAPDB_UInt        &actionLength,
                                              const SAPDB_Byte* &pActionSpace) const
{
    if ( ! IsValid() )
        return false;

    const SAPDB_Byte *pActionHeader = m_Buffer.GetBufferPointer()
                                      +
                                      m_CurrentOffset;
    const ActionHeader &actionhead = *(reinterpret_cast<const ActionHeader *>(pActionHeader));
    
    actionLength = actionhead.length - sizeof(ActionHeader);
    pActionSpace = pActionHeader + sizeof(ActionHeader);

    image.SetActionType      (Log_ActionType(actionhead.actionType));
    image.SetSequence        (actionhead.redosequence);
    image.SetLastUndoSequence(actionhead.undosequence);

    if ( image.GetActionType() == Log_MultipleActions )
        RTE_Crash( Log_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"RECURSIVE CALL") );

    return true;
}

/* -------------------------------------------------------------------------------- */
bool Log_ActionMultiple::Iterator::Next()
{
    if ( ! IsValid() )
        return false;

    const SAPDB_Byte *pActionHeader = m_Buffer.GetBufferPointer()
                                      +
                                      m_CurrentOffset;
    const ActionHeader &actionhead = *(reinterpret_cast<const ActionHeader *>(pActionHeader));
    
    m_CurrentOffset += actionhead.length;
    
    return IsValid();
}
