/*!

  @file           FBM_DataVolume.hpp
  @author         TorstenS
  @author         AlexanderK
  @ingroup        FBM

\if EMIT_LICENCE
    ========== 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

\endif
*/

#ifndef FBM_DATAVOLUME_H
#define FBM_DATAVOLUME_H

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

#include "Container/Container_Vector.hpp"
#include "FreeBlockManagement/FBM_IDataVolume.hpp"
#include "FreeBlockManagement/FBM_BlockStateList.hpp"

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

/*!
  @class          FBM_DataVolume
  @brief          representation of one data volume
 */
class FBM_DataVolume : public FBM_IDataVolume
{
public:
    typedef Container_Vector<SAPDB_UInt2>  FBM_CounterList;
    SAPDB_Int4         m_NumBlocksToAddUntilOptIsReached;
    SAPDB_Int4         m_ActBlockNoForBackup;            
    SAPDB_Int4         m_NumBlocksMarkedForBackup;
    SAPDB_Int4         m_NumBlocksFreeAfterSVP;
    SAPDB_Int4         m_NumBlocksUsed;

private: 

    const SAPDB_Int4           m_NumBlocks;
    const RTE_VolumeAccessMode m_VolMode;

    SAPDB_Int4         m_LastUsedBlock;
    SAPDB_Int4         m_FirstFreeBlock;
    SAPDB_Int4         m_LastFragmentedBlock;
    SAPDB_Int4         m_FirstFragmentedBlock;
    SAPDB_Int4         m_NumberOfSections;
    FBM_BlockStateList m_BlockStateList;         
    FBM_CounterList    m_NumBlocksFreeInSection;
    FBM_CounterList    m_NumBlocksMarkedForBackUpInSection;
    FBM_CounterList    m_NumBlocksFreeAfterSVPInSection;
    SAPDB_Int4         m_StartBlockNoToSearchFreeBlock;
    SAPDB_Bool         m_VolumeIsInCompressingMode;

    static const SAPDB_Int2 m_LogSectionSize;
    static const SAPDB_Int2 m_SectionSize;

    SAPDB_Int4 SectionEnd(SAPDB_Int4 SectionNo) const
    {
        return SAPDB_MIN (((SectionNo + 1) << m_LogSectionSize), m_NumBlocks) - 1;
    }
    
    SAPDB_Int4 SectionBegin(SAPDB_Int4 SectionNo) const
    {
        return (SectionNo << m_LogSectionSize);
    }

    SAPDB_Int4 GetOptLastUsedBlock() const
    {
        return SAPDB_MIN((m_NumBlocksUsed + (m_NumBlocksUsed >> 2)),(m_NumBlocks-1));
    }

    void FindPairOfFreeBlocks (
        SAPDB_Int4  StartSearchBlockNo, 
        SAPDB_Bool &bFreePairFound,
        SAPDB_Int4 &FreePairBlockNo,
        SAPDB_Int4 &StartBlockNoForNextSearch) const;

    void FindFreeBlock (
        SAPDB_Int4  StartSearchBlockNo, 
        SAPDB_Int4 &FreeBlockNo,
        SAPDB_Int4 &CurrSection,
        SAPDB_Bool &bFirstFreeBlockWasUsed);

    void pSetBlockStateMarkedForCompressionToFree (SAPDB_Int4 BlockNo)
    {
        /* check if the FBM is in the compression mode where this state can only exist */ 
        SAPDBERR_ASSERT_STATE (m_VolumeIsInCompressingMode);
        
        /* check if the transition is correct */
        SAPDBERR_ASSERT_STATE ((GetBlockStateAfterBackup (BlockNo) == BlockState_Free) &&
            (GetBlockState            (BlockNo) == BlockState_BackUp));
        
        /* update counter of blocks marked for back up */
        --m_NumBlocksMarkedForBackup;
        --m_NumBlocksMarkedForBackUpInSection[BlockNo>>m_LogSectionSize];
        
        /* set block state to free */
        m_BlockStateList.SetBlockStateAfterBackup (BlockNo, BlockState_Free);
        SetBlockStateToFree (BlockNo);
    }
    
    void pSetBlockStateMarkedForCompressionToOccupied  (SAPDB_Int4 BlockNo)
    {
        /* check if the FBM is in the compression mode where this state can only exist */ 
        SAPDBERR_ASSERT_STATE (m_VolumeIsInCompressingMode);
        
        /* check if the transition is correct */
        SAPDBERR_ASSERT_STATE ((GetBlockStateAfterBackup (BlockNo) == BlockState_BackUp) &&
            (GetBlockState            (BlockNo) == BlockState_BackUp));
        
        /* update counter of blocks marked for back up */
        --m_NumBlocksMarkedForBackup;
        --m_NumBlocksMarkedForBackUpInSection[BlockNo>>m_LogSectionSize];
        
        /* set block state to free */
        m_BlockStateList.SetBlockStateAfterBackup (BlockNo, BlockState_Free);
        m_BlockStateList.SetBlockState (BlockNo, BlockState_Occupied);

        /* remove compression mode if there are no more blocks to compress */
        m_VolumeIsInCompressingMode = (m_NumBlocksMarkedForBackup != 0);
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }


public:
    
    FBM_DataVolume (SAPDBMem_IRawAllocator &Allocator,
                    SAPDB_Int4 VolumeSize,
                    RTE_VolumeAccessMode VolMode)
        :
        FBM_IDataVolume(),
        m_NumBlocks                            (VolumeSize),
        m_VolMode                              (VolMode),
        m_LastUsedBlock                        (FBM_UNDEFINED),
        m_FirstFreeBlock                       (FBM_UNDEFINED),
        m_LastFragmentedBlock                  (FBM_UNDEFINED),
        m_NumberOfSections                     (FBM_UNDEFINED),
        m_FirstFragmentedBlock                 (FBM_UNDEFINED),
        m_NumBlocksToAddUntilOptIsReached      (FBM_UNDEFINED),
        m_ActBlockNoForBackup                  (FBM_UNDEFINED),
        m_NumBlocksMarkedForBackup             (FBM_UNDEFINED),
        m_NumBlocksFreeAfterSVP                (FBM_UNDEFINED),
        m_NumBlocksUsed                        (FBM_UNDEFINED),
        m_BlockStateList                       (Allocator),
        m_NumBlocksFreeInSection               (Allocator),
        m_NumBlocksMarkedForBackUpInSection    (Allocator),
        m_NumBlocksFreeAfterSVPInSection       (Allocator),
        m_StartBlockNoToSearchFreeBlock        (FBM_UNDEFINED),
        m_VolumeIsInCompressingMode            (false) 
    {}

    void InitVolume();
    
    FBM_BlockState GetBlockState(SAPDB_Int4 BlockNo) const
    {
        return m_BlockStateList.GetBlockState(BlockNo); 
    }

    FBM_BlockState GetBlockStateAfterBackup(SAPDB_Int4 BlockNo) const
    {
        return  m_BlockStateList.GetBlockStateAfterBackup(BlockNo);  
    }

    void SetBlockStateAfterBackup(SAPDB_Int4 BlockNo, FBM_BlockState BlockState)
    { 
        m_BlockStateList.SetBlockStateAfterBackup (BlockNo, BlockState);
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }

    void SetBlockStateToBackUp (SAPDB_Int4 BlockNo)
    {
        SAPDBERR_ASSERT_STATE (!m_VolumeIsInCompressingMode);
        
        m_BlockStateList.SetBlockStateAfterBackup (BlockNo, BlockState_Occupied);
        m_BlockStateList.SetBlockState (BlockNo, BlockState_BackUp);
        ++ m_NumBlocksMarkedForBackup;
        ++ m_NumBlocksMarkedForBackUpInSection[BlockNo>>m_LogSectionSize];
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }


    void SetBlockStateToFree (SAPDB_Int4 BlockNo)
    {
        m_BlockStateList.SetBlockState (BlockNo, BlockState_Free);
        --m_NumBlocksUsed;
        m_NumBlocksFreeInSection[BlockNo>>m_LogSectionSize]++;
        if (m_FirstFreeBlock > BlockNo) 
            m_FirstFreeBlock = BlockNo;
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }
    
    void SetBlockStateToOccupied (SAPDB_Int4 BlockNo)
    {
        m_BlockStateList.SetBlockState (BlockNo, BlockState_Occupied);
        ++m_NumBlocksUsed;
        --m_NumBlocksFreeInSection[BlockNo>>m_LogSectionSize];
        if (m_LastUsedBlock < BlockNo)
            m_LastUsedBlock = BlockNo;
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }
    
    void SetBlockStateToFreeAfterSVP (SAPDB_Int4 BlockNo)
    {
        /* set new block state */
        m_BlockStateList.SetBlockState (BlockNo, BlockState_FreeAfterSVP);
        
        /* update counter of blocks marked as free after savepoint */
        ++m_NumBlocksFreeAfterSVP;
        ++m_NumBlocksFreeAfterSVPInSection[BlockNo>>m_LogSectionSize];
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }

    
    void SetBlockStateMarkedForCompressionToFree (SAPDB_Int4 BlockNo)
    {
        pSetBlockStateMarkedForCompressionToFree (BlockNo);
        
        /* remove compression mode if there are no more blocks to compress */
        m_VolumeIsInCompressingMode = (m_NumBlocksMarkedForBackup != 0);
        
#       if COMPILEMODE_MEO00 >= QUICK_MEO00 
        if( FBM_Check.ChecksLevel( 5 ))
            Verify ();
#       endif
    }

    void SetBlockStateMarkedForCompressionToFreeAfterSVP (SAPDB_Int4 BlockNo);

    void SetBlockStateMarkedForCompressionToOccupied (SAPDB_Int4 BlockNo)
    {
        pSetBlockStateMarkedForCompressionToOccupied(BlockNo);
    }
    
    SAPDB_Int4 GetNumBlocksUsed() const
    {
        return m_NumBlocksUsed;
    }

    SAPDB_Int4 GetNumBlocksFree() const
    {
        return m_NumBlocks - m_NumBlocksUsed;
    }

    RTE_VolumeAccessMode VolMode() const
    {
        return m_VolMode;
    }

    void RestoreAllBlockStatesMarkedForBackup (SAPDB_Int4 &NumBlocksRestoredToFreeAfterSVP);
    
    void RestoreBlockStateMarkedForBackup (
        SAPDB_Int4  BlockNo,
        SAPDB_Bool &bRestoredStateIsFreeAfterSVP);
    
    void SetAllBlocksMarkedAsFreeAfterSVPToFree ();

    SAPDB_Int4 GetMultFreeBlocks (
        const SAPDB_Int4  NumBlocksWanted,
        SAPDB_Int4       &NumBlocksSupplied);

    SAPDB_Int4 GetFreeBlock ();

    void GetNextBlocksForBackUp (const SAPDB_Int4   MaxNumBlocksWanted, 
        SAPDB_Int4        &SuppliedNumBlocks, 
        SAPDB_Int4        &BlockNo);

    void Verify ();
    
    void GetDeviceStatistics (SAPDB_Int4 &NumBlocks,
        SAPDB_Int4 &NumBlocksFree,
        SAPDB_Int4 &NumBlockFreeAfterSVP) const;

    /*--------------------------------------------------------------------------
    function:     Dump()
    description:  insert all state information of a data devspace into the 
                  kernel dump file
    arguments:    dump [in/out] kernel dump file
                  devNo [in] logical identifier for a data devspace
    return value: none
    --------------------------------------------------------------------------*/

    void Dump( 
        Kernel_Dump      &dump,
        const SAPDB_Int4 devNo) const;
};

#endif //FBM_DATAVOLUME_H
