//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1998.
//
//  File:       changlog.hxx
//
//  Contents:   Persistent Change Log manager and related classes.
//
//  Classes:    CDocNotification, CDocChunk, CChunkList, CChangeLog
//
//  History:    5-26-94   srikants   ReWrite Change Log implementation.
//
//----------------------------------------------------------------------------

#pragma once

#include <glbconst.hxx>
#include <rcstxact.hxx>
#include <eventlog.hxx>
#include <doclist.hxx>
#include <usninfo.hxx>

class CResManager;
class CNotificationTransaction;

const unsigned   cDocInChunk = 16;


//+---------------------------------------------------------------------------
//
//  Class:      CDocNotification
//
//  Purpose:    Notification
//
//  History:    11-Aug-93   BartoszM    Created.
//              08-Feb-94   DwightKr    Added retries
//              24-Feb-97   SitaramR    Push filtering
//
//----------------------------------------------------------------------------
#include <pshpack2.h>

class CDocNotification
{
    public:
        CDocNotification() { _wid = widInvalid; }

        void Set ( WORKID wid,
                   USN usn,
                   VOLUMEID volumeId,
                   ULONG action,
                   ULONG retries,
                   ULONG secQRetries )
        {
            _wid      = wid;
            _usn      = usn;
            _volumeId = volumeId;
            Win4Assert( action <= 0xffff );
            Win4Assert( retries <= 0xffff );
            Win4Assert( secQRetries <= 0xffff );
            _action   = (USHORT) action;
            _retries  = (USHORT) retries;
            _secQRetries  = (USHORT) secQRetries;
        }

        WORKID Wid() const              { return _wid; }
        USN    Usn() const              { return _usn; }
        VOLUMEID VolumeId() const       { return _volumeId; }
        ULONG  Action() const           { return _action; }
        ULONG  Retries() const          { return _retries; }
        ULONG  SecQRetries() const      { return _secQRetries; }
        BOOL   IsDeletion () const      { return (_action & CI_DELETE_OBJ) != 0; }

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

    private:
        USN      _usn;
        WORKID   _wid;
        VOLUMEID _volumeId;
        USHORT   _action;
        USHORT   _retries;
        USHORT   _secQRetries;
};

#include <poppack.h>

//+---------------------------------------------------------------------------
//
//  Class:      CDocChunk
//
//  Purpose:    Array of document notifications
//
//  History:    11-Aug-93   BartoszM    Created.
//
//----------------------------------------------------------------------------

class CDocChunk
{

public:

    CDocChunk () : _pNext(0)
    {
        Win4Assert (sizeof(_aNotify) == (sizeof(CDocNotification) * cDocInChunk));
    }

    CDocChunk* GetNext() const        { return _pNext; }
    void SetNext ( CDocChunk* pNext ) { _pNext = pNext; }

    CDocNotification* GetDoc( unsigned offset )
    {
        Win4Assert( offset < cDocInChunk );
        return( &_aNotify[offset] );
    }

    CDocNotification* GetArray() { return &_aNotify[0]; }

    unsigned Size()              { return cDocInChunk; }

    void UpdateMaxUsn( CCountedDynArray<CUsnFlushInfo> & aUsnFlushInfo );


#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

private:

    CDocChunk*          _pNext;
    CDocNotification    _aNotify[cDocInChunk];

};

//+---------------------------------------------------------------------------
//
//  Class:      CChunkList
//
//  Purpose:    A singly linked list of CDocChunks.
//
//  History:    5-26-94   srikants   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

class CChunkList
{
public:

    CChunkList() : _pHead(0), _pTail(0), _count(0)
    {
    }

    ~CChunkList()
    {
        DestroyUpto();
    }

    CDocChunk * GetFirst() { return _pHead; }

    CDocChunk * GetLast()  { return _pTail; }

    BOOL IsEmpty() const { return 0 == _pHead; }

    ULONG Count( ) const { return _count; }

    void Append( CDocChunk * pChunk );

    CDocChunk * Pop();

    void DestroyUpto( CDocChunk * pChunk = 0 );

    CDocChunk * Acquire()
    {
        CDocChunk * pTemp = _pHead;
        _pHead = _pTail = 0;
        _count = 0;
        return(pTemp);
    }

    void TruncateFrom( CDocChunk * pChunk );

    void AcquireAndAppend( CChunkList & chunkList );

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

private:

    CDocChunk *     _pHead;             // Head of the linked list.
    CDocChunk *     _pTail;             // Tail of the linked list.

    ULONG           _count;             // number of chunks in the list.

};

//+---------------------------------------------------------------------------
//
//  Function:   Append
//
//  Synopsis:   Appends the specified chunk to the list.
//
//  Arguments:  [pChunk] -- Chunk to append.
//
//  History:    5-26-94   srikants   Created
//
//----------------------------------------------------------------------------

inline void CChunkList::Append( CDocChunk * pChunk )
{
    Win4Assert( 0 != pChunk );
    pChunk->SetNext(0);

    if ( 0 != _pHead )
    {
        Win4Assert( 0 != _pTail );
        _pTail->SetNext( pChunk );
    }
    else
    {
        Win4Assert( 0 == _pTail );
        _pHead = pChunk;
    }

    _pTail = pChunk;
    _count++;
}

//+---------------------------------------------------------------------------
//
//  Function:   Pop
//
//  Synopsis:   Removes and returns the first element in the list.
//
//  History:    5-26-94   srikants   Created
//
//----------------------------------------------------------------------------

inline CDocChunk * CChunkList::Pop()
{
    CDocChunk * pChunk = 0;
    if ( 0 != _pHead )
    {
        pChunk = _pHead;
        _pHead = _pHead->GetNext();
        if ( 0 == _pHead )
        {
            Win4Assert( 1 == _count );
            Win4Assert( pChunk == _pTail );
            _pTail = 0;
        }
        pChunk->SetNext(0);
        Win4Assert( _count > 0 );
        _count--;
    }
    else
    {
        Win4Assert( 0 == _pTail );
    }
    return(pChunk);
}


//+---------------------------------------------------------------------------
//
//  Function:   DestroyUpto
//
//  Synopsis:   Destroys all the elements in the list and frees them.
//
//  History:    5-26-94   srikants   Created
//
//----------------------------------------------------------------------------

inline void CChunkList::DestroyUpto( CDocChunk * pChunk )
{
    while ( _pHead != pChunk )
    {
        CDocChunk * pDelete = Pop();
        delete pDelete;
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   TruncateFrom
//
//  Synopsis:   Removes all the elements in the queue from the specified node
//              to the end of the list. It has been assumed that the specified
//              node is part of the list.
//
//  Arguments:  [pChunk] --  The first chunk to be deleted from the list.
//
//  History:    5-26-94   srikants   Created
//
//----------------------------------------------------------------------------

inline void CChunkList::TruncateFrom( CDocChunk * pChunk )
{
    Win4Assert( 0 != pChunk );

    CDocChunk  * pDelete = pChunk->GetNext();
    while ( 0 != pDelete )
    {
        CDocChunk * pNext = pDelete->GetNext();
        delete pDelete;
        Win4Assert( _count > 0 );
        _count--;
        pDelete = pNext;
    }

    Win4Assert( _count > 0 );
    _pTail = pChunk;
    pChunk->SetNext(0);
}

class CFreshTest;
class PStorage;

//+---------------------------------------------------------------------------
//
//  Class:      CChangeLog
//
//  Purpose:    Persistent change list manager. This class is capable of
//              doing operations on the persistent change log, eg.
//              serializing, de-serializing, and compacting.
//
//  History:    5-26-94   srikants   Created
//              2-24-97   SitaramR   Push filtering
//
//----------------------------------------------------------------------------


const LONGLONG eSigChangeLog = 0x474f4c474e414843i64; // "CHANGLOG"


class CChangeLog
{
public:

    CChangeLog( WORKID widChangeLog, PStorage & storage,
                PStorage::EChangeLogType type );
   ~CChangeLog();

    void  LokEmpty();

    BOOL  IsEmpty() const { return 0 == _cChunksAvail; }

    BOOL  IsPrimary() const { return PStorage::ePrimChangeLog == _type; }
    BOOL  IsSecondary() const { return PStorage::eSecChangeLog == _type; }

    ULONG AvailChunkCount() const { return _cChunksAvail; }

    ULONG DeSerialize( CChunkList & deserializedList,
                       ULONG cChunksToRead );

    ULONG Serialize( CChunkList & listToSerialize,
                     CCountedDynArray<CUsnFlushInfo> & aUsnFlushInfo );

    ULONG LokDeleteWIDsInPersistentIndexes( ULONG cFilteredChunks,
                                            CFreshTest & freshTestLatest,
                                            CFreshTest & freshTestAtMerge,
                                            CDocList & docList,
                                            CNotificationTransaction & notifTrans );

    void  LokDisableUpdates();

    void  LokEnableUpdates( BOOL fFirstTimeUpdatesAreEnabled );

    BOOL  AreUpdatesEnabled() { return _fUpdatesEnabled; }

    void  SkipChunk()
    {
        Win4Assert( _cChunksAvail > 0 );
        Win4Assert( _oChunkToRead + _cChunksAvail == _cChunksTotal );
        _oChunkToRead++;
        _cChunksAvail--;
    }

    void SetResMan( CResManager * pResManager, BOOL fPushFiltering );

    BOOL FPushFiltering()     { return _fPushFiltering; }

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

private:

    void  InitSize();

    void  LokVerifyConsistency();

    BOOL  IsWidInDocList( WORKID wid, CDocList& docList );

    const LONGLONG     _sigChangeLog;
    CResManager *      _pResManager;        // Resman
    WORKID             _widChangeLog;       // WorkId of the changelog.
    PStorage        &  _storage;            // Persistent storage object.
    PStorage::EChangeLogType    _type;      // Type of change log
    BOOL               _fUpdatesEnabled;    // Flag indicating whether updates
                                            //   are allowed or not.
    BOOL               _fPushFiltering;     // Using push model of filtering ?

    SRcovStorageObj    _PersDocQueue;       // The persistent doc queue.
    ULONG              _oChunkToRead;       // Offset of the next chunk to
                                            //   read from the log.
    ULONG              _cChunksAvail;       // Number of chunks available in
                                            //   the change log for reading.
    ULONG              _cChunksTotal;       // Total number of chunks.
                                            //   (For debugging).
};