/*!
    @file       IOMan_DataArea.cpp
    @author     TorstenS
    @ingroup    IOManagement
    @brief      This module is used to manage the data area.

\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
*/


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

#include "heo79_1.h"  // RTE: vxparam_save_good_config

#include "IOManager/IOMan_DataArea.hpp"
#include "IOManager/IOMan_Exception.hpp"
#include "IOManager/IOMan_Messages.hpp"
#include "Converter/Converter_IManager.hpp"
#include "KernelCommon/Kernel_IPage.hpp"
#include "RunTime/RTE_Crash.hpp"
#include "RunTime/RTE_Message.hpp"


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


/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/



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



/*===========================================================================*
 *  STATIC/INLINE FUNCTION PROTOTYPES                                        *
 *===========================================================================*/



/*===========================================================================*
 *  METHODS                                                                  *
 *===========================================================================*/

SAPDB_Bool
IOMan_DataArea::AddVolume(
    const tsp00_TaskId      taskId,
    const SAPDB_Bool        bIsCold,
    tsp00_VFilename         &devName,
    const IOMan_BlockCount  devSize,
    const IOMan_DeviceNo    devNo )
{
    SAPDBTRACE_ROUTINE_DEBUG( "IOMan_DataArea::AddVolume", IOMan_Trace, 6 );

    SAPDBTRACE_WRITELN( IOMan_Trace, 6, "Volume: " << devName <<
                        " VolSize: " << devSize << " DevNo: " << devNo );

    if( ! IsDataVolumeNoValid( devNo ) )
    {
        IOMan_Exception errMsg( __CONTEXT__, IOMAN_ERR_DATA_DEVNO_OUT_OF_RANGE,
                                SAPDB_ToString( devNo, _T_d ),
                                SAPDB_ToString( MaxVolumes(), _T_d ));
        RTE_Message( errMsg );
        return( SAPDB_FALSE );
    }

    IOMan_DataVolume  &volume      = Get( devNo );
    IOMan_DataVolume  *pPrevVolume = NULL;
    IOMan_DataVolume  *pNextVolume = NULL;

    // Add data volume to the control structures of the RTE, because at the
    // time of kernel restart this volume was not stored within data base
    // configuration file. The volume was made persistent by the dbmsrv after
    // starting the db kernel, therefore vcold couldn't find the volume!

    if(! volume.Add( devName, devSize ))
        return( SAPDB_FALSE );

    const SAPDB_Byte *pDBIdentifier = DetermineLinkage( volume, &pPrevVolume, &pNextVolume );

    if( NULL == pDBIdentifier )
        return( SAPDB_FALSE );

    if( IOMan_Okay == volume.Create( taskId, pDBIdentifier ))
    {
        if( IOMan_Okay == volume.Open( taskId ))
        {
            if( bIsCold || Converter_IManager::Instance().Expand( taskId, devNo, devSize,
                    volume.GetAccessMode(), volume.GetCapacity() ))
            {
                if( NULL != pPrevVolume )
                    CreateLinkage( taskId, *pPrevVolume, volume );

                if( NULL != pNextVolume )
                    CreateLinkage( taskId, volume, *pNextVolume );

                m_TotalDataPages    += volume.GetCapacity();
                m_UsedVolumes       +=1;
                m_ConfiguredVolumes +=1;
                {
                    IOMan_Exception errMsg( __CONTEXT__, IOMAN_INFO_ADD_VOLUME,
                                            volume.GetTypeName(),
                                            SAPDB_ToString( devNo, _T_d ),
                                            SAPDB_ToString( devSize, _T_d ));
                    RTE_Message( errMsg );

                    tsp00_ErrText       errText;
                    tsp00_XpReturnCode  retCode;
                    const SAPDB_Bool    bIsInRestartPhase = SAPDB_TRUE;

                    vxparam_save_good_config( ! bIsInRestartPhase, errText, retCode  );
                    if( xp_ok != retCode )
                    {
                        IOMan_Exception errMsg( __CONTEXT__, IOMAN_WRN_SAVE_PARAM_FAILED,
                            SAPDB_ToString( errText, errText.length(), errText.length()));
                        RTE_Message( errMsg );
                    }
                }
                return( SAPDB_TRUE );
            }
        }
    }
    volume.Close( taskId );
    volume.Del();
    return( SAPDB_FALSE );
}

/*---------------------------------------------------------------------------*/

void
IOMan_DataArea::CloseAll( const tsp00_TaskId taskId )
{
    SAPDBTRACE_ROUTINE_DEBUG( "IOMan_DataArea::CloseAll", IOMan_Trace, 6 );

    ConstIterator   endIter = End();

    for( Iterator iter = Begin(); iter != endIter; ++iter )
        ( *iter ).Close( taskId );

    m_TotalDataPages = 0;
    m_UsedVolumes    = 0;
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
IOMan_DataArea::CreateAll(
    const tsp00_TaskId taskId,
    const SAPDB_Byte   *pDBIdentifier )
{
    SAPDBTRACE_ROUTINE_DEBUG( "IOMan_DataArea::CreateAll", IOMan_Trace, 6 );

    if( 0 != m_TotalDataPages )
    {
        IOMan_Exception errMsg( __CONTEXT__, IOMAN_ERR_DATA_AREA_ALREADY_ONLINE );
        RTE_Crash( errMsg );
    }

    Iterator        prevIter = End();
    ConstIterator   endIter  = End();

    for( Iterator iter = Begin(); iter != endIter; ++iter )
    {
        IOMan_ReturnCode retCode = ( *iter ).Create( taskId, pDBIdentifier );

        if( IOMan_Okay == retCode )
        {
            if( IOMan_Okay != (( *iter ).Open( taskId )))
                return( SAPDB_FALSE );
            else
            {
                if( prevIter != endIter )
                    CreateLinkage( taskId, *prevIter, *iter );

                prevIter          = iter;
                m_TotalDataPages += ( *iter ).GetCapacity();
                m_UsedVolumes    +=1;
            }
        }
        else if( IOMan_NotConfigured == retCode )
            continue;
        else
            return( SAPDB_FALSE );
    }
    SAPDBERR_ASSERT_STATE( m_ConfiguredVolumes == m_UsedVolumes );
    return( SAPDB_TRUE );
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
IOMan_DataArea::GetVolumeNo(
    tsp00_VFilename  &devName,
    IOMan_DeviceNo   &devNo ) const
{
    SAPDBTRACE_ROUTINE_DEBUG( "IOMan_DataArea::GetVolumeNo", IOMan_Trace, 6 );

    tsp00_VFilename     auxName;
    ConstIterator       endIter = End();

    for( ConstIterator iter = Begin(); iter != endIter; ++iter )
        if(( *iter ).GetName( auxName ))
            if( 0 == memcmp( devName, auxName, sizeof( devName )))
            {
                devNo = (*iter).GetLogicalDevNo();
                return( SAPDB_TRUE );
            };
    return( SAPDB_FALSE );
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
IOMan_DataArea::OpenAll( const tsp00_TaskId taskId )
{
    SAPDBTRACE_ROUTINE_DEBUG( "IOMan_DataArea::OpenAll", IOMan_Trace, 6 );

    IOMan_BlockCount    totalDataPages = 0;
    SAPDB_UInt          usedVolumes    = 0;
    Iterator            prevIter       = End();
    ConstIterator       endIter        = End();

    for( Iterator iter = Begin(); iter != endIter; ++iter )
    {
        IOMan_ReturnCode retCode = ( *iter ).Open( taskId );

        if( IOMan_Okay == retCode )
        {
            if( prevIter == endIter )
            {
                // iter points to the first volume within configuration file
                if( ( *iter ).IsPrevLinkageValid())
                {
                    IOMan_Exception errMsg( __CONTEXT__, IOMAN_ERR_MISSING_PREDECESSOR,
                                            ( *iter ).GetTypeName(),
                                            SAPDB_ToString( ( *iter ).GetLogicalDevNo(), _T_d ),
                                            SAPDB_ToString( ( *iter ).GetPrevLogicalDevNo(), _T_d ));
                    RTE_Message( errMsg );
                    return( SAPDB_FALSE );
                }
            }
            else
            {
                if( !( *prevIter ).IsLinkageValid( taskId, *iter ))
                    return( SAPDB_FALSE );

                if( !( *prevIter ).IsDBIdentifierValid( taskId, *iter ))
                    return( SAPDB_FALSE );
            }
            prevIter        = iter;
            totalDataPages += ( *iter ).GetCapacity();
            usedVolumes    +=1;
        }
        else if( IOMan_NotConfigured == retCode )
            continue;
        else
            return( SAPDB_FALSE );
    }
    if( ( *prevIter ).IsNextLinkageValid())
    {
        IOMan_Exception errMsg( __CONTEXT__, IOMAN_ERR_MISSING_SUCCESSOR,
                                ( *prevIter ).GetTypeName(),
                                SAPDB_ToString( ( *prevIter ).GetLogicalDevNo(), _T_d ),
                                SAPDB_ToString( ( *prevIter ).GetNextLogicalDevNo(), _T_d ));
        RTE_Message( errMsg );
        return( SAPDB_FALSE);
    }

    SAPDBERR_ASSERT_STATE( m_ConfiguredVolumes == usedVolumes );

    // Set the data area capacity after opening all volumes
    m_TotalDataPages = totalDataPages;
    m_UsedVolumes    = usedVolumes;

    return( SAPDB_TRUE );
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
IOMan_DataArea::Initialize(
    const tsp00_TaskId    taskId,
    const SAPDB_Int       maxDataVolumes )
{
    SAPDBTRACE_ROUTINE_DEBUG( "IOMan_DataArea::Initialize", IOMan_Trace, 6 );

    if(! m_Volumes.Resize( maxDataVolumes ))
        return( SAPDB_FALSE );

    IOMan_DeviceNo  devNo   = IOMan_DataArea::GetFirstDataVolumeNo();
    Iterator        iter    = Begin();
    ConstIterator   endIter = End();

    while( iter != endIter )
    {
        Kernel_IPage::PageFrame frame = m_PageAllocator.New( taskId );

        if( ! frame.IsAssigned() )
            return( SAPDB_FALSE );

        ( *iter ).Initialize( devNo, frame );

        // Determine the number of configured data volumes at this point
        // because same compontents are interested in this information
        // before the corresponding data volumes are started.

        if( ( *iter ).IsConfigured() )
            ++m_ConfiguredVolumes;
        ++iter;
        ++devNo;
    }
    return( SAPDB_TRUE );
}

/*----------------------------- Private methods -----------------------------*/

void
IOMan_DataArea::CreateLinkage(
    const tsp00_TaskId  taskId,
    IOMan_DataVolume    &prevVolume,
    IOMan_DataVolume    &volume )
{
    if( prevVolume.SetNextLinkage( taskId, volume ))
        if( volume.SetPrevLinkage( taskId, prevVolume ))
            return;

    IOMan_Exception errMsg( __CONTEXT__, IOMAN_ERR_CREATE_LINKAGE,
                            volume.GetTypeName(),
                            SAPDB_ToString( prevVolume.GetLogicalDevNo(), _T_d ),
                            SAPDB_ToString( volume.GetLogicalDevNo(), _T_d ));
    RTE_Crash( errMsg );
}

/*---------------------------------------------------------------------------*/

const SAPDB_Byte*
IOMan_DataArea::DetermineLinkage(
    const IOMan_DataVolume    &volume,
    IOMan_DataVolume          **pPrevVolume,
    IOMan_DataVolume          **pNextVolume )
{
    ConstIterator   endIter = End();

    for( Iterator iter = Begin(); iter != endIter; ++iter )
    {
        if(( *iter ).IsOnline())
        {
            if(( *iter ).GetLogicalDevNo() < volume.GetLogicalDevNo())
                *pPrevVolume = iter;
            else
                if(( *iter ).GetLogicalDevNo() > volume.GetLogicalDevNo())
                {
                    *pNextVolume = iter;
                    break;
                }
        }
    }
    if( NULL != *pPrevVolume )
        return(( **pPrevVolume ).GetDBIdentifier());
    else if( NULL != *pNextVolume )
        return(( **pNextVolume ).GetDBIdentifier());
    else
        return( NULL );
}

/*===========================================================================*
*  END OF CODE                                                              *
*===========================================================================*/
