/*!
    @ingroup        Restart
    @file           Rst_RedoFile.cpp
    @author         UweH
    @brief          Implementation for class Rst_RedoFile.
*/
/*
    ========== 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

*/



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

#include "SAPDBCommon/ErrorsAndMessages/SAPDBErr_Assertions.hpp"
#include "SAPDBCommon/Tracing/SAPDBTrace_Usage.hpp"
#include "KernelCommon/Kernel_VTrace.hpp"
#include "DataAccess/Data_PageAccessManager.hpp"
#include "DataAccess/Data_Types.hpp"
#include "Restart/Rst_Exceptions.hpp"
#include "Restart/Rst_RedoFile.hpp"

class SAPDBMem_IRawAllocator;

/* --------------------------------------------------------------------------- */
Rst_RedoFile::Rst_RedoFile (tgg00_TransContext &Trans,
                            const Data_PageNo   Root,
                            const Data_PageNo   Last)
: Data_BaseFile ( Data_RedoFile,
                  Data_PageId(Root, Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable)),
                  Data_PageAccessManager(Trans, Data_RedoFile, 
                  Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Root) ),
  m_Container (m_PageAccessManager, m_RootId, Last),
  m_Iterator  (m_PageAccessManager,*(REINTERPRET_CAST (SAPDBMem_IRawAllocator*, Trans.trAllocator_gg00))),
  m_TransNo   (Trans.trWriteTransId_gg00)
{
    SAPDBTRACE_METHOD_DEBUG ("ctor::Rst_RedoFile", LogTrans_Trace, 5);
}

/* --------------------------------------------------------------------------- */
Rst_RedoFile::Rst_RedoFile (Data_PageAccessManager &Pam,
                            SAPDBMem_IRawAllocator &Allocator,
                            const tgg91_TransNo    &TransNo,
                            const Data_PageNo       Root,
                            const Data_PageNo       Last)
: Data_BaseFile ( Data_RedoFile,
                  Data_PageId(Root, Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable)),
                  Data_PageAccessManager(Pam, Data_RedoFile, 
                  Data_PageRecoveryMode(Data_Dynamic,Data_Recoverable), Root) ),
  m_Container (m_PageAccessManager, m_RootId, Last),
  m_Iterator  (m_PageAccessManager, Allocator),
  m_TransNo   (TransNo)
{
    SAPDBTRACE_METHOD_DEBUG ("ctor::Rst_RedoFile(pam)", LogTrans_Trace, 5);
}

/* --------------------------------------------------------------------------- */
Rst_RedoFile::~Rst_RedoFile ()
{
    SAPDBTRACE_METHOD_DEBUG ("dtor::Rst_RedoFile", LogTrans_Trace, 5);
    if (IsCreated())
        Drop();
}


/* --------------------------------------------------------------------------- */
bool Rst_RedoFile::Create ()
{
    SAPDBTRACE_METHOD_DEBUG ("Create::Rst_RedoFile", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( ! IsCreated() );

    m_PageAccessManager.Invalidate();

    m_RootId.Invalidate();
    
    PageIterator RootPageIter (m_PageAccessManager);

    if ( ! m_Container.Create(RootPageIter) ) // PTS 1121659 UH 2003-04-30
    {
        m_Container.Drop();
        return false;
    }

    if ( ! m_Iterator.Initialize() )
    {
        m_Container.Drop();
        return false;
    }

    if ( m_TransNo.gg90IsNil() )
    {
        RTE_Message(SAPDBErr_Exception(__CONTEXT__,SAPDBERR_ASSERT_STATE_FAILED,"m_TransNo not nil"));
        return false;
    }
    (*RootPageIter).SetTransNo (m_TransNo);

    return true;
}

/* --------------------------------------------------------------------------- */
void Rst_RedoFile::Drop ()
{
    SAPDBTRACE_METHOD_DEBUG ("Drop::Rst_RedoFile", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    Delete();
    m_Container.Drop();
    m_RootId.Invalidate();
    m_PageAccessManager.Invalidate();
}

/* --------------------------------------------------------------------------- */
void Rst_RedoFile::Delete ()
{
    SAPDBTRACE_METHOD_DEBUG ("Delete::Rst_RedoFile", LogTrans_Trace, 5);

    m_Iterator.Invalidate();
    m_Iterator.Delete();
}

/*!
   @returns        true, if the file is consistent

 */

bool Rst_RedoFile::Verify (bool isCold)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoFile::Verify", LogTrans_Trace, 5);
    if ( m_Container.Verify (isCold) )
    {
        if ( LogTrans_Check.ChecksLevel(5) )
            RTE_Message( Restart_Exception(__CONTEXT__, RST_INFO_VERIFY,
                         "RedoFile", SAPDB_ToString(GetRootId().PageNo()) ) );
    }
    else
    {
        RTE_Message( Restart_Exception(__CONTEXT__, RST_ERROR_VERIFY,
                     "RedoFile",
                     SAPDB_ToString(GetRootId().PageNo()),
                     SAPDB_ToString(m_PageAccessManager.GetLastError()) ) );
        m_PageAccessManager.ResetLastError();
        return false;
    }
    return true;
}

/*!
   @brief          This traces some basic information about the file to the trace.

 */

void Rst_RedoFile::WriteToTrace (const char * title) const
{
    Data_BaseFile::WriteToTrace (title);
        
    if ( ! IsCreated() )
    {
        Kernel_VTrace() << "LogRedoFile: is no created.";
        return;
    }
}

/* --------------------------------------------------------------------------- */
void Rst_RedoFile::GetEntryInfo (Data_SplitSpaceReader &Reader,
                                 Log_IOSequenceNo      &IOSequence,
                                 Log_EntrySequence     &EntrySequence,
                                 bool                  &isOK)
{
    SAPDBTRACE_ROUTINE_DEBUG ("Rst_RedoFile::GetEntryInfo", LogTrans_Trace, 5);
    
    SAPDB_Byte* aux;
    Data_SplitSpaceReader::Result result;
    
    Reader.Reserve (sizeof(EntryHead), aux, result);
    isOK = Data_SplitSpaceReader::moreSpaceAvailable == result;
    
    EntryHead& entryHead = *(REINTERPRET_CAST(EntryHead*,aux));
    IOSequence    = entryHead.IOSequence;
    EntrySequence = entryHead.Sequence;
}

/* --------------------------------------------------------------------------- */
Data_SplitSpaceWriter
Rst_RedoFile::ReserveSpace (Data_SplitRecordLength Length,
                            Data_RecordLength      MinimalLength,
                            Log_EntrySequence      EntrySequence,
                            Log_IOSequenceNo       IOSequence)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoFile::ReserveSpace", LogTrans_Trace, 5);

    SAPDBERR_ASSERT_STATE( IsCreated() );

    SAPDBERR_ASSERT_ARGUMENT ( Length >= MinimalLength );
    
    if ( ! m_Container.CheckSpace() ) // PTS 1115593 UH 2002-05-07
        RTE_Crash( Data_Exception(__CONTEXT__,DATA_NOT_ENOUGH_SPACE) );
    
    SAPDB_UInt addedPages;
    
    if ( ! m_Container.ReserveSpace (Length        + sizeof(EntryHead),
                                     MinimalLength + sizeof(EntryHead),
                                     m_Iterator,
                                     addedPages) )
    {
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,"ReserveSpace") );
    }

    if ( ! m_Iterator.IsValid() )
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED, "iter.IsValid") );

    if ( Length != (*m_Iterator).Length()-sizeof(EntryHead) )
    {
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED,
                                      "ReserveSpace() requested and allocated length") );
    }

    if ( ! m_Iterator.IsValid() )
        RTE_Crash( SAPDBErr_Exception(__FILE__, __LINE__,
                   SAPDBERR_ASSERT_STATE_FAILED,
                   "Rst_RedoFile::ReserveSpace() m_Container.ReserveSpace failed") );

    Data_SplitSpaceWriter         Writer     (*m_Iterator);
    Data_SplitSpaceWriter::Result NewResult;
    SAPDB_Byte*                   SourcePart;

    Writer.Reserve (sizeof(EntryHead), SourcePart, NewResult);

    if ( Data_SplitSpaceWriter::moreSpaceAvailable != NewResult )
        RTE_Crash( SAPDBErr_Exception(__CONTEXT__, SAPDBERR_ASSERT_STATE_FAILED, "MoreSpace") );

    EntryHead& entryHead = *(REINTERPRET_CAST(EntryHead*,SourcePart));
    entryHead.IOSequence = IOSequence;
    entryHead.Sequence   = EntrySequence;

    if ( (*m_Iterator).NewPageWasAppended() )
    {
        SAPDB_UInt partno = (*m_Iterator).CurrentPart();
        if ( 0 == partno )
        {
            // mark the first page
            Rst_RedoPage& page = (*m_Iterator).GetPage(partno);
            page.SetFirstEntrySequence (EntrySequence);
            page.SetLastEntrySequence  (EntrySequence);
        }
        else
            while ( partno > 0 )
            {
                // mark all pages but not the first
                Rst_RedoPage& page = (*m_Iterator).GetPage(partno);
                page.SetFirstEntrySequence (EntrySequence);
                page.SetLastEntrySequence  (EntrySequence);
                --partno;
            }
    }
    else
    {
        Rst_RedoPage &lastpage = (*m_Iterator).GetPage((*m_Iterator).CurrentPart());
        lastpage.SetLastEntrySequence (EntrySequence);
    }

    return Writer;
}

/* --------------------------------------------------------------------------- */
void Rst_RedoFile::ReleaseSpace(bool isOK)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoFile::ReleaseSpace", LogTrans_Trace, 5);
    if ( ! isOK )
    {
        EntrySpace   &space = *m_Iterator;

        Rst_RedoPage &firstpage = space.GetPage(0);
        // PTS 1127350 UH 2004-01-22 added following line. Otherwise this entry is never redone.
        firstpage.SetLastEntrySequence(firstpage.GetLastEntrySequence()-1);
        m_Container.UndoReserveSpace(space);
        m_Iterator.Invalidate (false);
        return;
    }
    m_Iterator.Invalidate (true);
}

/* --------------------------------------------------------------------------- */
void Rst_RedoFile::Truncate (Log_EntrySequence stopSequence)
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoFile::Truncate", LogTrans_Trace, 5);
    
    if ( LogTrans_Trace.TracesLevel(6) )
        Kernel_VTrace() << "stopSequence: " << stopSequence;

    if ( stopSequence.IsInvalid() )
    {
        if ( LogTrans_Trace.TracesLevel(6) )
        {
            Kernel_VTrace() << "stopsequence: is invalid: trunc to empty file";
            WriteToTrace();
        }
        m_Container.Truncate ( m_Container.RootId().PageNo(),
                               Data_Page::MinSpaceOffset() );
        return;
    }
    
    PageIterator pageIter (m_PageAccessManager);
    
    if ( LogTrans_Trace.TracesLevel(6) )
        Kernel_VTrace() << "lastpageno: " << m_Container.LastPageNo();

    m_Container.End (pageIter, Data_ForRead);
    
    while ( pageIter.IsValid()
            &&
            (*pageIter).PageNo() != m_Container.RootId().PageNo()
            &&
            (*pageIter).GetFirstEntrySequence() > stopSequence )
    {
        if ( LogTrans_Trace.TracesLevel(6) )
            Kernel_VTrace() << "Pageno: "              << (*pageIter).PageNo()
                            << "FirstSequenceInPage: " << (*pageIter).GetFirstEntrySequence();
        --pageIter;
    }

    if ( ! pageIter.IsValid() )
    {
        Kernel_VTrace() << "stopsequence: " << stopSequence << NewLine;
        WriteToTrace();
        RTE_Crash( SAPDBErr_Exception(__FILE__, __LINE__,
                   SAPDBERR_ASSERT_STATE_FAILED,
                   "Rst_RedoFile::Truncate() file is corrupt (page not found)") );
    }

    // truncate to valid position
    
    Data_PageNo                        pnoOfNewLastPage = (*pageIter).PageNo();
    Data_SplitSpaceForwardReadIterator recordIter;
    Data_PageOffset                    newFreeOffset = 0; // 0 is invalid => no truncation of offset

    (*pageIter).Begin (recordIter);
    
    if ( ! recordIter.IsValid() )
    {
        // page is empty
        #ifdef SAPDB_QUICK
        if ( ! (*pageIter).IsEmpty() )
        {
            Kernel_VTrace() << "stopsequence: " << stopSequence;
            WriteToTrace();
            RTE_Crash( SAPDBErr_Exception(__FILE__, __LINE__,
                       SAPDBERR_ASSERT_STATE_FAILED,
                       "Rst_RedoFile::Truncate() file is corrupt (page not empty)") );
        }
        #endif
        newFreeOffset = Data_Page::MinSpaceOffset();
    }
    else
    {
        // The first logentry in the page has written its sequence to the node header
        // during Reserve().
        // Because the recorditer cannot determine if the current entry part is a restpart
        // of a split logentry also written on the previous page,
        // we can only interpret the second entry on the page and skip the first entry.
		// The root page must always begin with a new logentry, so the first entry is not skipped.

        if ( pnoOfNewLastPage != m_Container.RootId().PageNo() )
			++recordIter;

        Log_EntrySequence sequenceFromPage = (*pageIter).GetFirstEntrySequence();
        Log_EntrySequence sequenceFromEntry;
        
        while ( recordIter.IsValid() )
        {
            sequenceFromEntry = (reinterpret_cast<EntryHead*>(*recordIter))->Sequence;
            
            #ifdef SAPDB_QUICK
            if ( sequenceFromEntry < sequenceFromPage )
            {
                Kernel_VTrace() << "stopsequence: "        << stopSequence
                                << ", sequenceFromPage: "  << sequenceFromPage
                                << ", sequenceFromEntry: " << sequenceFromEntry
                                << NewLine;
                WriteToTrace();
                RTE_Crash( SAPDBErr_Exception(__FILE__, __LINE__,
                           SAPDBERR_ASSERT_STATE_FAILED,
                           "Rst_RedoFile::Truncate() invalid entry sequence") );
            }
            #endif
            
            // stopSequence is the last wanted entry, all others are removed
            if ( sequenceFromEntry > stopSequence )
                break;
            ++recordIter;
        }
        
        if ( recordIter.IsValid() )
            newFreeOffset = recordIter.GetPosition();
    }
    
    (*pageIter).Deassign(); // release for read to get new for update
    
    if ( LogTrans_Trace.TracesLevel(6) )
        Kernel_VTrace() << "truncate to " << pnoOfNewLastPage << "." << newFreeOffset;
    m_Container.Truncate (pnoOfNewLastPage, newFreeOffset);

}

/* --------------------------------------------------------------------------- */
Log_EntrySequence Rst_RedoFile::GetLastEntrySequence ()
{
    SAPDBTRACE_METHOD_DEBUG ("Rst_RedoFile::GetLastEntrySequence", LogTrans_Trace, 5);

    PageIterator pageIter (m_PageAccessManager);

    m_Container.End (pageIter, Data_ForRead);

    if ( pageIter.IsValid() )
        return (*pageIter).GetLastEntrySequence();
    else
        return Log_EntrySequence();
}

