/*!
  @file           FBM_Manager.hpp
  @author         TorstenS
  @author         AlexanderK
  @ingroup        FBM
  @brief          General header file of the FBM

\if EMIT_LICENCE
    ========== licence begin  GPL
    Copyright (c) 2001-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_MANAGER_HPP
#define FBM_MANAGER_HPP

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

#include "gsp00.h"
#include "ggg00.h"
#include "heo51.h"
#include "heo55k.h"                                // RTE   : vbegexcl,venexcl 
#include "hgg08.h"                                 // region identifier
#include "FreeBlockManagement/FBM_IManager.hpp"
#include "FreeBlockManagement/FBM_DataVolumeArray.hpp"
#include "KernelCommon/Kernel_Common.hpp"
#include "KernelCommon/Kernel_Dump.hpp"
#include "RunTime/MemoryManagement/RTEMem_AllocatorWrapper.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_IRawAllocator.hpp"


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

/*===========================================================================*
 *  CLASS DECLARATIONS                                                       *
 *===========================================================================*/

/*!
  @class          FBM_Manager

 */


class FBM_Manager : public FBM_IManager
{

public:

    /*!
       @brief          static method to create a singelton of the type FBM_Manager
       @param          TrError [out] returns the error e_sysbuff_overflow in case
                                 that there is not enough memory to create an instance
                                 of FBM_Manager otherwise e_ok is returned
       @return         (FBM_Manager&amp;) reference to the instance of FBM_Manager
     */
    static        FBM_Manager& CreateInstance (tgg00_BasisError TrError);

    /*!
       @brief          returns the reference to the sigelton instance of FBM_Manager
       @return         (FBM_Manager&amp;) reference to the instance of FBM_Manager
     */
    static FBM_Manager& Instance ()
    {
        return *m_Instance;
    }

    /*!
       @brief          restarts the FBM
       @param          TaskId [in] task id
       @param          MaxNumDev [in] maximum number of devices which can be handled 
                                   by the FBM_Manager
       @param          ReservedBlocks [in] collection of reserved block address
       @return         true if successful executed else false
     */
    bool Restart(
        const tsp00_TaskId                  TaskId,
        const SAPDB_Int4                    MaxNumDev,
        const IOMan_ReservedBlockAddress    &ReservedBlocks );

    /*!
       @brief          add a new volume to the FBM
       @param          TaskId [in] task id
       @param          DevNo [in] device number
       @param          DevSize [in] number of pages the device can accomodate
       @param          DevMode [in] access mode of the volume 
       @return         true if successful executed else false
     */
    bool AddVolume(
        const tsp00_TaskId          TaskId,
        const SAPDB_Int2            DevNo,
        const SAPDB_Int4            DevSize,
        const RTE_VolumeAccessMode  DevMode);

    /*!
       @brief          Sets the state of block to 'marked for backup'
       @param          TaskId [in] task id
       @param          BlockAddress [in] I/O adress of the block to change
       @param          bAbortIfErrorOccured [in] terminate kernel if an error occure.
                                        default is set to no. Therefore
                                        the calling method has to make the
                                        error handlig.
       @return         (SAPDB_Bool) SAPDB_TRUE if operation was executed successfully

       - If the state could not be changed successfully the kernel aborts, if
      the given parameter bAbortIfErrorOccured is set to SAPDB_TRUE.
     */
    SAPDB_Bool SetBlockStateToBackup(
        const tsp00_TaskId        TaskId,
        const IOMan_BlockAddress &BlockAddress,
        const SAPDB_Bool          bAbortIfErrorOccured = ! FBM_ABORT_IF_ERROR );

    SAPDB_Bool SetBlockStateToBackup(
        const tsp00_TaskId           TaskId,
        IOMan_IBlockAddressIterator& BlockIterator,
		SAPDB_UInt&                  numBlocks,
        const SAPDB_Bool             bAbortIfErrorOccured = ! FBM_ABORT_IF_ERROR );

    /*!
       @brief          Sets the state of block to 'occupied'
       @param          TaskId [in] task id
       @param          BlockIterator [in] iterator of I/O adresses with the blocks to change
       @param          bAbortIfErrorOccured [in] terminate kernel if an error occure.
                                        default is set to no. Therefore
                                        the calling method has to make the
                                        error handlig.
       @return         (SAPDB_Bool) SAPDB_TRUE if operation was executed successful

       - If the state could not be changed successfully the kernel aborts if
      the given parameter bAbortIfErrorOccured is set to SAPDB_TRUE.
     */
    SAPDB_Bool SetBlockStateToOccupied(
        const tsp00_TaskId                 TaskId,
        IOMan_IBlockAddressIterator &BlockIterator,
        const SAPDB_Bool                   bAbortIfErrorOccured = ! FBM_ABORT_IF_ERROR);


    /*!
       @brief          Sets the state of block to 'occupied'
       @param          TaskId [in] task id
       @param          BlockAddress [in] I/O adress of the block to change
       @param          bAbortIfErrorOccured [in] terminate kernel if an error occure.
                                        default is set to no. Therefore
                                        the calling method has to make the
                                        error handlig.
       @return         (SAPDB_Bool) SAPDB_TRUE if operation was executed successful

       - If the state could not be changed successfully the kernel aborts if
      the given parameter bAbortIfErrorOccured is set to SAPDB_TRUE.
     */
    SAPDB_Bool SetBlockStateToOccupied(
        const tsp00_TaskId        TaskId,
        const IOMan_BlockAddress &BlockAddress,
        const SAPDB_Bool          bAbortIfErrorOccured = ! FBM_ABORT_IF_ERROR);


    /*!
       @brief          Sets the state of cluster to 'free'
       @param          TaskId [in] task id
       @param          ClusterAddress [in] I/O adress and length of the cluster to change
       @return         none

       - If the state could not be changed successfully the kernel aborts
     */
    void SetClusterStateToFree(
        const tsp00_TaskId         TaskId,
        const IOMan_ClusterAddress &ClusterAddress );

    /*!
       @brief          Sets the state of block to 'free'
       @param          TaskId [in] task id
       @param          BlockAddress [in] I/O adress of the block to change
       @return         none

       - If the state could not be changed successfully the kernel aborts
     */
    void SetBlockStateToFree(
        const tsp00_TaskId       TaskId,
        const IOMan_BlockAddress &BlockAddress );

    /*!
       @brief   Sets the state of block to 'free'
       @param   blockAddress [in] I/O adress of the block to change
       @return  none

       - If the state could not be changed successfully the kernel aborts
     */

    inline void SetBlockStateToFree( const IOMan_BlockAddress &blockAddress )
    {
        SetBlockStateToFree( GetTaskId(), blockAddress );
    }

    /*!
       @brief          Sets the state of block to 'free after completion of the next save point'
       @param          TaskId [in] task id
       @param          BlockAddress [in] I/O adress of the block to change
       @return         none

       - If the state could not be changed successfully the kernel aborts
     */

    void SetBlockStateToFreeAfterSVP(
        const tsp00_TaskId       TaskId,
        const IOMan_BlockAddress &BlockAddress );

    /*!
       @brief   Sets the state of block to 'free after completion of the next save point'
       @param   blockAddress [in] I/O adress of the block to change
       @return  none

       - If the state could not be changed successfully the kernel aborts
    */

    inline void SetBlockStateToFreeAfterSVP( const IOMan_BlockAddress &blockAddress )
    {
        SetBlockStateToFreeAfterSVP( GetTaskId(), blockAddress );
    }

    /*!
       @brief          all memory resources are released and members set to their initial values
       @param          TaskId [in] task id
       @return         one
     */
    void Shutdown( const tsp00_TaskId TaskId );

    /*!
       @brief          Supplies a set of free and neighbouring blocks
       @param          taskId [in] task id
       @param          NumFreeBlocksWanted [in] wanted number of blocks
       @param          bReqSequential [in] request access to sequential volume
       @param          trError [out] errors state of the function. if everything worked fine it is e_ok
       @return         (IOMan_ClusterAddress) address of the supplied cluster

       - Supplies a set of free and neighbouring blocks and sets these blocks
      to the state 'occupied'
     */
    IOMan_ClusterAddress GetMultFreeBlocks(
        const tsp00_TaskId  taskId,
        const SAPDB_Int4    NumFreeBlocksWanted,
        const SAPDB_Bool    bReqSequential,
        tgg00_BasisError   &trError);

    /*!
       @brief          Supplies a free block
       @param          taskId [in] task id
       @param          bReqSequential [in] request access to sequential volume
       @return         (IOMan_BlockAddress) block address of the supplied block

       - Supplies a single free block and marks this block as 'occupied'
      if no free block could be found than an emergency shutdown will
    be executed.
     */
    IOMan_BlockAddress GetFreeBlock(
        const tsp00_TaskId  taskId ,
        const SAPDB_Bool    bReqSequential);

    /*!
       @brief          All Blocks marked as 'free after savepoint' are released
       @param          TaskId [in] task id
       @return         none
     */
    void SetAllBlocksMarkedAsFreeAfterSVPToFree( const tsp00_TaskId TaskId );

    /*!
       @brief          Removes for all blocks the flags indicating that this block must be saved
       @param          TaskId [in] task id
       @return         none

       - The original state of the blocks is restored.
     */
    void RestoreAllBlockStatesMarkedForBackup( const tsp00_TaskId TaskId );

	SAPDB_UInt CheckConverterFBM(
			const tsp00_TaskId taskId);
	
    /*!
       @brief          Removes the flags indicating that a block must be saved
       @param          TaskId [in] task id
       @param          BlockAddress [in] address of the block to restore
       @return         true if the block was really marked for back up

       - The original state of the block is restored.
       - If the state could not be restored successfully since the block to be restored
      was not at all marked for backup the kernel aborts
     */
    void RestoreBlockStateMarkedForBackup (
        const tsp00_TaskId        TaskId,
        const IOMan_BlockAddress &BlockAddress );

    /*!
      @brief          Removes the flags indicating that a block must be moved to compress the device
      @param          TaskId  [in] task id
      @param          DevNo   [in] address of the block to restore
      @param          BlockNo [in] address of the block to restore
      @param          bBlockInUse [in] undocumented
      @return         true if the block was really marked for back up

      - The original state of the block is restored.
      - If the state could not be restored successfully since the block to be restored
     was not at all marked for moving the kernel aborts
    */
    void RestoreBlockStateMarkedForCompression(
        const tsp00_TaskId  TaskId,
        const SAPDB_Int2    DevNo,
        const SAPDB_Int4    BlockNo,
        SAPDB_Bool          &bBlockInUse );

    /*!
       @brief          Returns the number of all blocks marked for back up
       @param          taskId [in] task id
       @return         number of all blocks marked for back up
     */
    SAPDB_Int4 NumBlocksMarkedForBackup( const tsp00_TaskId taskId ) const;

    /*!
       @brief          The iterator supplying the blocks marked for back up is set to the first block
       @param          TaskId [in] task id
       @return         none

       - This function has to be called prior to the call of the function
      GetNextBlocksForBackUp which returns each time it is executed another
    block marked for backup. The function BeginReadingBlocksMarkedForBackUp
    initializes this iterator and sets it onto the very first block marked for backup
     */
    void BeginReadingBlocksMarkedForBackUp( const tsp00_TaskId TaskId );

    /*!
       @brief          Returns the address of a set of neighbouring blocks marked for back up
       @param          taskId [in] task id
       @param          DevNo [in] number of the device from which the block is wanted
       @param          MaxNumBlocksWanted [in] maximum number of neighbouring blocks supplied 
                                       i.e. SuppliedNumBlocks &lt;= MaxNumBlocksWanted
       @param          SuppliedNumBlocks [out] number of adjacent marked blocks found
       @param          BlockNo [out] address of the first block of the supplied set
       @param          trError [out] errors state of the function. if everything worked 
                                       fine it is e_ok
       @return         none

       - Each time this function is called it returns another set of blocks marked for backup.
      The function  BeginReadingBlocksMarkedForBackUp initializes this iterator and sets it
    onto the very first block marked for backup. After the initialization this function
    returns by each call an other block untill all marked blocks where returned. That there
    are no more blocks left is displayed by a return value 0 for the parameter SuppliedNumBlocks 
     */
    void  GetNextBlocksForBackUp (
        const tsp00_TaskId taskId,
        const SAPDB_Int2   DevNo,
        const SAPDB_Int4   MaxNumBlocksWanted,
        SAPDB_Int4        &SuppliedNumBlocks,
        SAPDB_Int4        &BlockNo,
        tgg00_BasisError  &trError);

    /*!
       @brief          inserts all important memory structures of the FBM into the 
                       kernel dump file
       @param          taskId [in] identification of the calling task
       @param          dump [in/out] kernel dump file
       @return         none
     */
    void Dump(
        const tsp00_TaskId  taskId,
        Kernel_Dump         &dump ) const;

    /*!
       @brief          Returns the number of all used blocks on a device
       @param          DevNo [in] number of the device for which the number of used blocks is requested
       @return         number of all blocks used

       - Returns the number of all used blocks on a device. A block is considered to be used if it is not in the state free.
     */
    SAPDB_Int4 NumBlocksUsed( const SAPDB_Int2 DevNo ) const
    {
        SAPDBERR_ASSERT_ARGUMENT ((DevNo >=FirstDevNo()) && (DevNo <= LastDevNo()));

        return (m_DataVolume [DevNo].GetNumBlocksUsed());
    }

    /*!
        @brief	Returns the number of blocks in state FreeAfterSavepoint
        @return  int4
      */
    SAPDB_Int4 GetNumberOfFreeAfterSVPBlocks() const{
        return m_TotalNumBlocksFreeAfterSVP;
    }

    /*!
       @brief          Returns the volume mode of a device
       @param          DevNo [in] number of the device for which the volume mode is requested
       @return         volume mode

       - Returns the volume mode of a device.
     */
    RTE_VolumeAccessMode GetVolMode( const SAPDB_Int2 DevNo ) const
    {
        SAPDBERR_ASSERT_ARGUMENT ((DevNo >=FirstDevNo()) && (DevNo <= LastDevNo()));

        return (m_DataVolume [DevNo].VolMode());
    }

    /*!
       @brief          Returns the number of blocks in state free. Note that the
                       blocks in state free after savepoint are treated as blocks
                       in state occupied, because the are not available at this 
                       moment.
       @return         (SAPDB_Int4) Number of free blocks
     */
    SAPDB_Int4 GetNumberOfFreeBlocks() const
    {
        return( m_TotalNumBlocksFree );
    }

    /*!
       @brief          Returns the number of all used blocks. Used means all blocks
                       not in state free. Note that blocks in state free after savepoint 
                       are handled as occupied blocks because there are not available
                       at this moment.
       @return         (SAPDB_Int4) Number of used blocks
     */
    SAPDB_Int4 GetNumberOfUsedBlocks() const
    {
        return( m_TotalNumBlocks - GetNumberOfFreeBlocks() );
    }

    /*!
       @brief          Checks if the given number of blocks is storable within the
                       data volumes.
       @param          numBlocksRequested [in] number of blocks to be written into
                       the data volumes.
       @return         true is given number of blocks is storable within data
     */
    bool IsSpaceAvailable( const SAPDB_Int4 numBlocksRequested ) const
    {
        return(( numBlocksRequested + GetNumberOfUsedBlocks() ) < m_SecuritySpaceLimit );
    }

    /*!
       @brief          If the data volume filling is about 90 percent and the fbm
                       is in online mode, this method returns true to signalize that 
                       garbage collector support is needed.
       @param          numChangedPages [in] number of changed pages within data cache
                       and converter
       @return         true if garbage collector support is needed; else false
     */
    bool GarbageCollectionNeeded( const SAPDB_Int4 numChangedPages ) const
    {
        if( ! m_Active )
            return( false );

        if( m_SavepointIsRequested )
            return( false );

        // data base filling is under 90 percent inclusive the changed pages of the data cache
        if(( GetNumberOfUsedBlocks() + numChangedPages ) < ( m_TotalNumBlocks * 0.9 ))
            return( false );

        return( true );
    }

    /*!
       @brief          If the number of blocks in state free after savepoint is greater
                       than the number of blocks in state free a savepoint is needed.
       @return         true if a savepoint is needed; else false
     */
    bool SavepointNeeded() const
    {
        if( !m_Active )
            return( false );

        if( m_SavepointIsRequested )
            return( false );

        if( m_TotalNumBlocksFree > m_TotalNumBlocksFreeAfterSVP )
            return( false );

        return( true );
    }

    /*!
        @brief  Returns true if the number of blocks in state free after savepoint is greater than zero.
        @return (SAPDB_Bool) 
     */

    SAPDB_Bool FreeAfterSVPExist() const
    {
        return( m_TotalNumBlocksFreeAfterSVP );
    }

private:

    FBM_Manager(SAPDBMem_IRawAllocator &Allocator);

    SAPDB_Int4 FirstDevNo() const
    {
        return 1;
    }

    SAPDB_Int4 MaxDevNo() const
    {
        return (FirstDevNo() + m_MaxNumDev - 1);
    }

    SAPDB_Int4 LastDevNo() const
    {
        return (FirstDevNo() + m_NumDev - 1);
    }

    SAPDB_Int4 GetFreeVolumeInGroup(const SAPDB_UInt CurrentVolumeGroup,
                                    const SAPDB_Int2 NumRequestedBlocks);

    SAPDB_Int2 GetDeviceWithFreeBlocks (const SAPDB_Int2 NumRequestedBlocks,
                                        const SAPDB_Bool bReqSequential);

    SAPDB_Int4 OptimumNumUsedBlocksPerDevice ();

    void ChangeDeviceUsagePreferences();

    void ResetMembersToNullAndZero();

    void TriggerSavepoint( const tsp00_TaskId taskId );

    /*!
       @brief          Sets the state of block to 'occupied' - simple without locking/synchronizing
       @param          BlockAddress [in] I/O adress of the block to change
       @param          bAbortIfErrorOccured [in] terminate kernel if an error occure.
                                        default is set to no. Therefore
                                        the calling method has to make the
                                        error handlig.
       @return         (SAPDB_Bool) SAPDB_TRUE if operation was executed successful

       - If the state could not be changed successfully the kernel aborts if
      the given parameter bAbortIfErrorOccured is set to SAPDB_TRUE.
     */
    SAPDB_Bool SetBlockStateToOccupiedUnsynched(
        const IOMan_BlockAddress &BlockAddress,
        const SAPDB_Bool          bAbortIfErrorOccured = ! FBM_ABORT_IF_ERROR);

    SAPDB_Bool SetBlockStateToBackupUnsynched(
        const IOMan_BlockAddress &BlockAddress,
        const SAPDB_Bool          bAbortIfErrorOccured = ! FBM_ABORT_IF_ERROR);


private:

    tsp00_TaskId GetTaskId()
    {
        tsp00_TaskId taskId;
        vgetpid( taskId );
        return( taskId );
    }

    class FBM_SynchObject
    {
    private:
        tsp00_TaskId m_TaskId;
    public:
        FBM_SynchObject(tsp00_TaskId TaskId) :m_TaskId(TaskId)
        {
            vbegexcl (m_TaskId, g08fbm);
        };

        ~FBM_SynchObject()
        {
            vendexcl (m_TaskId, g08fbm);
        };
    };

private:

    RTEMem_AllocatorWrapper     m_Allocator;
    SAPDB_Int4                  m_MaxNumDev;
    SAPDB_Int4                  m_SecuritySpaceLimit;
    SAPDB_Int4                  m_TotalNumBlocksFreeAfterSVP;
    SAPDB_Int4                  m_TotalNumBlocksFree;
    SAPDB_Int4                  m_TotalNumBlocks;
    SAPDB_Int4                  m_NumDev;
    SAPDB_Int4                  m_NextDevToUseForNewBlock;
    SAPDB_Int4                  m_NextDevToUseForFragBlock;
    SAPDB_Int4                  m_LastArchiveVolumeUsed;
    SAPDB_Int4                  m_ParallelWritingArchiveVolumes;
    FBM_DataVolumeArray         m_DataVolume;
    static FBM_Manager          *m_Instance;
    SAPDB_Bool                  m_SavepointIsRequested;
    SAPDB_Bool                  m_Active;
    IOMan_ReservedBlockAddress  m_ReservedBlocks;
    Container_Vector<SAPDB_UInt> m_ArchiveVolumes;
    Container_Vector<SAPDB_UInt> m_ArchiveVolumeGroups;
    SAPDB_UInt                  m_CurArchiveVolumeGroup;
    SAPDB_UInt                  m_NumberVolumeGroups;


};


#endif //FBM_MANAGER_HPP
