//
//  Copyright (c) 2001, Reson, Inc. All Rights Reserved.
//
//  Filename:   Disk.cpp
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implementation file for the CDisk class.
//
//  Notes:
//

#include "StdAfx.h"
#include "Disk.h"
#include "RegistryKeys.h"

#include "..\Utils\NetUtils\7kFile.h"
#include "..\Utils\NetUtils\7kFileHeader.h"

#include <atlbase.h>

namespace                   // Begin unnamed namespace.
{

    const unsigned long     ulMegaBytesToBytes_c              =   1UL * 1024UL * 1024UL;

}                           // End unnamed namespace.

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// CDisk class implementation.

using namespace N6046RegistryKeys;

CDisk::CDisk(   LPCTSTR             lpszPath,
                LPCTSTR             lpszFileName,
                const bool          &rbEnable,
                const bool          &rbAutoSwitch,
                const unsigned long &rulMaxFileSize )

      :m_ulMinFileSizeInBytes( 512UL * 1024UL ),        // Arbitrarily 512 kBytes (0.5 M Bytes) for now.
       m_ulMaxRecordsBeforeFlush( 100UL ),
       m_ulMaxPossibleOverlap( 1000UL ),
       m_unFileHeaderVersion( 1 )
{
    m_bAutoSwitch               = rbAutoSwitch;
    m_ulMaxFileSizeInBytes      = rulMaxFileSize;

    m_bUsingDefaultFolder       = false;

    m_ulMaxRecordOverlapCount   = 100UL;

    m_ulFlushCount              = 0UL;
    m_ulOverlapCount            = 0UL;

    m_ulBytesLogged             = 0UL;
    m_ulSensorsInTable          = 0UL;

    m_ulSessionId               = 0UL;

    memset( &m_szProgramVersion[ 0 ], 0x00,  16 * sizeof( TCHAR ) ); 
    memset( &m_szUserName[ 0 ],       0x00,  64 * sizeof( TCHAR ) ); 
    memset( &m_szUserNotes[ 0 ],      0x00, 128 * sizeof( TCHAR ) ); 

    m_pPrimaryFile              = NULL;
    m_pSecondaryFile            = NULL;
    m_pSensorIndexTable         = NULL;

    m_Path.Empty();
    m_FileName.Empty();
    m_FullFileName.Empty();

    ReadPathFromRegistry();

    ReadFileSizeFromRegistry();

    ReadRecordOverlapFromRegistry();

    SetFileName( lpszPath, lpszFileName, false );

    if ( rbEnable )
    {
        if ( ! Switch( m_Path, m_FileName ) )
        {
            TRACE( _T( "CDisk::CDisk(), Can't activate file%s%s\n" ), (LPCTSTR) m_Path, (LPCTSTR) m_FileName );
        }
    }
}

CDisk::~CDisk( void )
{
    __TRY
    {
        if ( ! WritePathToRegistry() )
        {
            TRACE( _T( "CDisk::~CDisk(), WritePathToRegistry() failed\n" ) );
        }

        if ( ! WriteFileSizeToRegistry() )
        {
            TRACE( _T( "CDisk::~CDisk(), WriteFileSizeToRegistry() failed\n" ) );
        }

        if ( ! WriteRecordOverlapToRegistry() )
        {
            TRACE( _T( "CDisk::~CDisk(), WriteRecordOverlapToRegistry() failed\n" ) );
        }
    }
    __FINALLY
    {
        if ( m_pPrimaryFile != NULL )
        {
            delete m_pPrimaryFile;
            m_pPrimaryFile = NULL;
        }

        if ( m_pSecondaryFile != NULL )
        {
            delete m_pSecondaryFile;
            m_pSecondaryFile = NULL;
        }

        if ( m_pSensorIndexTable != NULL )
        {
            delete [] m_pSensorIndexTable;
            m_pSensorIndexTable = NULL;

            m_ulSensorsInTable = 0UL;
        }
    }
    __ENDFINALLY
}

bool CDisk::IsRecording( void ) const
{
    bool bRecording = false;

    if ( m_pPrimaryFile != NULL )
    {
        bRecording = m_pPrimaryFile->IsFileOpen();
    }

    return bRecording;
}

bool CDisk::Close( void )
{
    if ( m_pPrimaryFile != NULL )
    {
        m_pPrimaryFile->Close();
    }

    if ( m_pSecondaryFile != NULL )
    {
        m_pSecondaryFile->Close();
    }

    return true;
}

bool CDisk::Path( LPCTSTR lpszPath )
{
    if ( IsRecording() )
    {
        TRACE( _T( "CDisk::Path(), Can't change path while recording\n" ) );
    }
    else if ( ( lpszPath != NULL ) && ( ::strlen( lpszPath ) > 0 ) )
    {
        m_Path = _T( lpszPath );

        TRACE( _T( "CDisk::SetPath(), path changed to \"%s\"\n" ), static_cast<LPCTSTR>( m_Path ) );

        if ( ! WritePathToRegistry() )
        {
            TRACE( _T( "CDisk::SetPath(), WritePathToRegistry() failed\n" ) );
        }

        // Indicate path was chaned regarless of whether it was written to the registry.

        return true;
    }

    return false;
}

bool CDisk::UsingDefaultFolder( void ) const
{
    return m_bUsingDefaultFolder;
}

CString CDisk::Path( void ) const
{
    return m_Path;
}

void CDisk::SetAutoSwitch( const bool &rbEnable )
{
    m_bAutoSwitch = rbEnable;
}

bool CDisk::MaxFileSize( const unsigned long &rulMaxFileSizeInMBytes )
{
    const unsigned long ulMaxFileSizeInBytes = rulMaxFileSizeInMBytes * ulMegaBytesToBytes_c;

    if ( ulMaxFileSizeInBytes >= m_ulMinFileSizeInBytes )
    {
        m_ulMaxFileSizeInBytes = ulMaxFileSizeInBytes;
        return true;
    }

    return false;
}

unsigned long CDisk::MaxFileSize( void ) const
{
    return ( m_ulMaxFileSizeInBytes / ulMegaBytesToBytes_c );
}

bool CDisk::RecordOverlap( const unsigned long &rulRecordOverlap )
{
    if ( rulRecordOverlap < m_ulMaxPossibleOverlap )
    {
        m_ulMaxRecordOverlapCount = rulRecordOverlap;
        
        if ( IsRecording() )
        {
            MaintainOverlap( false );
        }

        return true;
    }

    return false;
}

unsigned long CDisk::RecordOverlap( void ) const
{
    return m_ulMaxRecordOverlapCount;
}

bool CDisk::Set7kFileHeaderInfo( const unsigned long    &rulNumberOfSensors,
                                 const PSUBSYSTEMENTRY   psSensorTable,
                                 const unsigned long    &rulSessionId,
                                 LPCTSTR                 lpszProgramVersion,
                                 LPCTSTR                 lpszUserName,
                                 LPCTSTR                 lpszUserNotes )
{
    // Store the sensor table and misc. header info in preparation for fromatting and writing 
    // the 7k file header (record 7200) -- see also CDisk::OpenFile().

    bool bSuccess = false;      // Assume failure for now.

    // Destroy the old sensor table first, note that consequently, calling this member with zero sensors 
    // will delete the existing sensor table.

    if ( m_pSensorIndexTable != NULL )
    {
        delete [] m_pSensorIndexTable;
        m_pSensorIndexTable = NULL;
        m_ulSensorsInTable = 0UL;
    }

    // Reallocate and populate the new sensor table in preparation of writing the 7k file header.

    if ( rulNumberOfSensors > 0UL )
    {
        m_ulSensorsInTable = rulNumberOfSensors;
        m_pSensorIndexTable = new SUBSYSTEMENTRY [ rulNumberOfSensors ];
        ASSERT( m_pSensorIndexTable != NULL );

        ASSERT( psSensorTable != NULL );

        if ( ( m_pSensorIndexTable != NULL ) && ( psSensorTable != NULL ) )
        {
            for ( unsigned long ulEntry = 0UL; ulEntry < rulNumberOfSensors; ulEntry++ )
            {
                ::memcpy( &m_pSensorIndexTable[ ulEntry ], &psSensorTable[ ulEntry ], SUBSYSTEMENTRY::Size() );
            }

            bSuccess = true;
        }
    }
    else
    {
        bSuccess = true;
    }

    m_ulSessionId = rulSessionId;

    C7kFileHeader::SetString( &m_szProgramVersion[ 0 ], lpszProgramVersion, 16  );
    C7kFileHeader::SetString( &m_szUserName [ 0 ],      lpszUserName,       64  );
    C7kFileHeader::SetString( &m_szUserNotes[ 0 ],      lpszUserNotes,      128 );

    return bSuccess;
}

bool CDisk::Switch( LPCTSTR lpszPath, LPCTSTR lpszFileName )
{
    bool bSuccess = false;

    try
    {
        if ( ! SetFileName( lpszPath, lpszFileName, false ) )
        {
            ThrowMessage_m( "Can't set valid file or path name" );
        }

        if ( IsAutoSwitchMode() )
        {
            if ( ( m_pPrimaryFile == NULL ) || 
                 ( ( m_pPrimaryFile != NULL ) && ( ! m_pPrimaryFile->IsFileOpen() ) ) )
            {
                if ( m_pPrimaryFile == NULL )
                {
                    if ( ( m_pPrimaryFile = new C7kFile() ) == NULL )
                    {
                        ThrowMessage_m( "Can't construct primary log file object" )
                    }
                }

                OpenFile( fileTypePrimary );
            }
            else
            {
                if ( m_pSecondaryFile == NULL )
                {
                    if ( ( m_pSecondaryFile = new C7kFile() ) == NULL )
                    {
                        ThrowMessage_m( "Can't construct secondary log file object" );
                    }

                    OpenFile( fileTypeSecondary );
                }
            }
        }
        else
        {
            if ( m_pPrimaryFile == NULL )
            {
                if ( ( m_pPrimaryFile = new C7kFile() ) == NULL )
                {
                    ThrowMessage_m( "Can't construct primary log file object." );
                }
            }
            else
            {
                m_pPrimaryFile->Close();
            }

            OpenFile( fileTypePrimary );
        }

        m_ulBytesLogged = 0UL;

        bSuccess = true;
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "CDisk::Switch(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CDisk::Switch(), unspecified exception.\n" ) );
    }
    
    return bSuccess;
}

bool CDisk::Write(  BYTE                   *pbyRecord,
                    const unsigned long    &rulBytes )
{
    bool bSuccess = false;

    try
    {
        if ( rulBytes == 0 )
        {
            bSuccess = true;        // Nothing to do.
        }
        else if ( pbyRecord == NULL )
        {
            ThrowMessage_m( "pbyRecord is NULL\n" );
        }
        else if ( MaintainOverlap( true ) == fileModeOverlapped )
        {
            bSuccess = true;

            //TRACE( _T( "CDisk::Write(), Writing primary\n" ) );

            if ( ! WritePrimaryFile( pbyRecord, rulBytes ) )
            {
                bSuccess = false;
            }

            //TRACE( _T( "CDisk::Write(), Writing secondary\n" ) );

            if ( ! WriteSecondaryFile( pbyRecord, rulBytes ) )
            {
                bSuccess = false;
            }
        }
        else
        {
            bSuccess = WritePrimaryFile( pbyRecord, rulBytes );
        }
    }
    catch ( LPCTSTR lpszErrorMessage )
    {
        TRACE( _T( "CDisk::Write(), %s\n" ), lpszErrorMessage );
        bSuccess = false;
    }
    catch ( ... )
    {
        TRACE( _T( "CDisk::Write(), Unspecified exception caught\n" ) );
        bSuccess = false;
    }

    return bSuccess;
}

CString CDisk::FullFileName( void )
{
    return m_FullFileName;
}

double CDisk::FileSize( void ) const
{
    return ( ( m_pPrimaryFile == NULL ) ? 0.0 : m_pPrimaryFile->FileSize() );
}

///////////////////////////////////////////////////////////////////////////////
// Private services.

inline
bool CDisk::WritePrimaryFile( const BYTE *pbyRecord, const unsigned long &rulBytes )
{
    ASSERT( pbyRecord != NULL );
    ASSERT( rulBytes  >  0UL );
    ASSERT( m_pPrimaryFile != NULL );

    bool bSuccess = false;

    if ( rulBytes == 0UL )
    {
        bSuccess = true;
    }
    else if ( ( m_pPrimaryFile != NULL ) && ( pbyRecord != NULL ) )
    {
        bSuccess = m_pPrimaryFile->Write( const_cast<BYTE *>( pbyRecord ), rulBytes );

        m_ulBytesLogged += rulBytes;

        if ( m_ulFlushCount++ >= m_ulMaxRecordsBeforeFlush )
        {
            m_pPrimaryFile->Flush();
            m_ulFlushCount = 0UL;
        }
    }

    return bSuccess;
}

inline
bool CDisk::WriteSecondaryFile( const BYTE *pbyRecord, const unsigned long &rulBytes )
{
    ASSERT( pbyRecord        != NULL );
    ASSERT( rulBytes         >  0UL );
    ASSERT( m_pSecondaryFile != NULL );

    bool bSuccess = false;

    if ( rulBytes == 0UL )
    {
        bSuccess = true;
    }
    else if ( ( m_pSecondaryFile != NULL ) && ( pbyRecord != NULL ) )
    {
        bSuccess = m_pSecondaryFile->Write( const_cast<BYTE *>( pbyRecord ), rulBytes );
    }

    return bSuccess;
}

inline
bool CDisk::ReadPathFromRegistry( void )
{
    CRegKey Key;

    bool    bSuccess   = false;
    DWORD   dwCount = 0;
    long    lResult = 0;

    char    pValue[ _MAX_PATH + 1 ];
        
    Key.Attach( hKey6046_c );

    lResult = Key.Open( hKey6046_c, lpsz6046RegistryKey_c );

    if ( lResult != ERROR_SUCCESS )
    {
        Key.Create( hKey6046_c, lpsz6046RegistryKey_c );
        Key.Close();
    }

    // Data path.

    Key.Open( hKey6046_c, lpsz6046RegistryKey_c );

    dwCount = _MAX_PATH;
    lResult = Key.QueryValue( pValue, lpsz6046DataPathRegistryKey_c, &dwCount );

    bool bUseDefault = false;

    if ( lResult == ERROR_SUCCESS )
    {
        pValue[ dwCount ] = '\0';
        m_Path = pValue;

        if ( m_Path.IsEmpty() )
        {
            bUseDefault = true;
        }
        else if ( ! PathFileExists( m_Path ) )
        {
            // The path doesn't exist so we're going to try to create it. If that fails, use the default folder.

            const int iPathLength = m_Path.GetLength();

            if ( ( iPathLength > 0 ) && ( iPathLength < MAX_PATH ) )
            {
                if ( ! CreateDirectory( m_Path, NULL ) )
                {
                    TRACE( _T( "CDisk::ReadPathFromRegistry(), Can't create path %s." ), static_cast<LPCTSTR>( m_Path ) );
                    bUseDefault = true;
                }
                else if ( ! PathFileExists( m_Path ) )
                {
                    TRACE( _T( "CDisk::ReadPathFromRegistry(), Path %s was not created\n." ), static_cast<LPCTSTR>( m_Path ) );
                    bUseDefault = true;
                }
            }
            else
            {
                bUseDefault = true;
            }
        }
        else
        {
            bSuccess = true;
        }
    }
    else
    {
        bUseDefault = true;
    }

    // If the path selection failed, set the default path and try to update (and create it first, if
    // necessary) its value in the registry.

    if ( bUseDefault )
    {
        m_bUsingDefaultFolder = true;

        m_Path = _T( "c:\\" );

        if ( Key.SetValue( m_Path, lpsz6046DataPathRegistryKey_c ) != ERROR_SUCCESS )
        {
            if ( Key.Create( hKey6046_c, lpsz6046DataPathRegistryKey_c ) == ERROR_SUCCESS )
            {
                if ( Key.SetValue( m_Path, lpsz6046DataPathRegistryKey_c ) == ERROR_SUCCESS )
                {
                    bSuccess = true;
                }
                else
                {
                    TRACE( _T( "CDisk::ReadPathFromRegistry(), Can't set data path key.\n" ) );
                }
            }
            else
            {
                TRACE( _T( "CDisk::ReadPathFromRegistry(), Can't create data path key.\n" ) );
            }
        }
    }

    Key.Close();

    return bSuccess;
}

inline
bool CDisk::WritePathToRegistry( void )
{
    bool bSuccess = false;

    try
    {
        CRegKey Key;
    
        Key.Attach( hKey6046_c );

        if ( Key.Open( hKey6046_c, lpsz6046RegistryKey_c ) == ERROR_SUCCESS )
        {
            bSuccess = ( Key.SetValue( m_Path, lpsz6046DataPathRegistryKey_c ) == ERROR_SUCCESS );
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }
  
    return bSuccess;
}

inline
bool CDisk::IsAutoSwitchMode( void ) const
{
    return m_bAutoSwitch;
}

inline
bool CDisk::SetFileName(    LPCTSTR    lpszPath,
                            LPCTSTR    lpszFileName,
                            const bool &rbUseDefault )
{
    if ( lpszPath != NULL )
    {
        m_Path = lpszPath;
    }

    if ( rbUseDefault )
    {
        m_FileName = C7kFile::BuildDefaultFileName();
    }
    else if ( lpszFileName == NULL )
    {
        m_FileName = C7kFile::BuildDefaultFileName();
    }
    else
    {
        m_FileName = lpszFileName;
    }

    if ( m_Path.IsEmpty() )
    {
        m_FileName = C7kFile::BuildDefaultFileName();
    }

    if ( m_Path[ m_Path.GetLength() - 1 ] != '\\' )
    {
        m_Path += _T( "\\" );
    }

    m_FullFileName = m_Path + m_FileName;

    return true;
}

inline
bool CDisk::OpenFile( const EFILETYPE &reFileType )
{
    // Opens the 7k data file and writes the file header (record #7200) immediately thereafter.
    // Note that the file header is created on a per file basis (since the 7k header timestamp etc will change) 
    // and therefore it is constructed and destroyed within this routine even though the sensor table 
    // will infrequently change.

    bool bSuccess = false;              // Assume failure for now.

    try
    {
        bool          bUseHeader = false;
        C7kFileHeader FileHeader;

        BYTE  const * pbyFileHeaderRecord = NULL;
        unsigned long ulFileHeaderSize    = 0UL;

        try
        {
            if ( FileHeader.Encode( m_ulSensorsInTable,
                                        reinterpret_cast<C7kFileHeader::SUBSYSTEMENTRY *>( m_pSensorIndexTable ),
                                            false,
                                                m_unFileHeaderVersion,
                                                    m_ulSessionId,
                                                        _T( "RESON 6046 Payload Controller" ),
                                                            &m_szProgramVersion[ 0 ],
                                                                &m_szUserName[ 0 ],
                                                                    &m_szUserNotes [ 0 ] ) )

            {
                pbyFileHeaderRecord = static_cast<BYTE *>( FileHeader );
                ulFileHeaderSize    = static_cast<unsigned long>( FileHeader );
                bUseHeader          = ( ( pbyFileHeaderRecord != NULL ) && ( ulFileHeaderSize > 0UL ) );
            }
        }
        catch ( ... )
        {
            bUseHeader          = false;
            pbyFileHeaderRecord = NULL;
            ulFileHeaderSize    = 0UL;
        }

        switch ( reFileType )
        {
            case fileTypePrimary:

                ASSERT( m_pPrimaryFile != NULL );

                if ( m_pPrimaryFile->Open( m_FullFileName, C7kFile::accessModeWrite ) )
                {
                    if ( bUseHeader )
                    {
                        if ( ! WritePrimaryFile( pbyFileHeaderRecord, ulFileHeaderSize ) )
                        {
                            ThrowMessage_m( "Can't write record header to primary file" );
                        }

                    }

                    bSuccess = true;
                }
                else
                {
                    ThrowMessage_m( "Can't open primary file" );
                }

                break;

            case fileTypeSecondary:

                ASSERT( m_pSecondaryFile != NULL );

                if ( m_pSecondaryFile->Open( m_FullFileName, C7kFile::accessModeWrite ) )
                {
                    if ( bUseHeader )
                    {
                        if ( ! WriteSecondaryFile( pbyFileHeaderRecord, ulFileHeaderSize ) )
                        {
                            ThrowMessage_m( "Can't write record header to secondary file" );
                        }
                    }

                    bSuccess = true;
                }
                else
                {
                    ThrowMessage_m( "Can't open secondary file" );
                }
    
                break;

            default:

                ThrowMessage_m( "Unrecognized file type specified" );
                break;
        }
    }
    catch ( LPCTSTR lpszMessage )
    {
        bSuccess = false;
        TRACE( _T( "CDisk::OpenFile(), %s\n" ), lpszMessage );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CDisk::OpenFile(), unspecified exception.\n" ) );
    }
 
    return bSuccess;
}

inline
CDisk::EFILEMODE CDisk::MaintainOverlap( const bool &rbNewRecord )
{
    EFILEMODE eSwitchMode = fileModeNormal;

    if ( IsAutoSwitchMode() && ( m_ulBytesLogged > m_ulMaxFileSizeInBytes ) )
    {
        if ( m_ulOverlapCount <= m_ulMaxRecordOverlapCount )
        {
            eSwitchMode = fileModeOverlapped;

            if ( m_ulOverlapCount == 0 )
            {
                if ( m_pSecondaryFile == NULL )
                {
                    try
                    {
                        if ( ! SetFileName( NULL, NULL, false ) )
                        {
                            ThrowMessage_m( "Can't set valid file or path name" );
                        }

                        if ( ( m_pSecondaryFile = new C7kFile() ) == NULL )
                        {
                            ThrowMessage_m( "Can't construct secondary log file object" );
                        }

                        if ( ! OpenFile( fileTypeSecondary ) )
                        {
                            ThrowMessage_m( "Can't open seconday log file object" );
                        }
                    }
                    catch ( LPCTSTR lpszMessage )
                    {
                        TRACE( _T( "CDisk::MaintainOverlap(), %s.\n" ), lpszMessage );
                    }
                    catch ( ... )
                    {
                        TRACE( _T( "CDisk::MaintainOverlap(), Can't construct or open seondary logging file.\n" ) );
                    }
                }

                ASSERT( m_pSecondaryFile != NULL );
            }

            if ( rbNewRecord )
            {
                ++m_ulOverlapCount;
            }
        }
        else
        {
            __TRY
            {
                if ( m_pPrimaryFile != NULL )
                {
                    delete m_pPrimaryFile;
                    m_pPrimaryFile = NULL;
                }
            }
            __FINALLY
            {
                ASSERT( m_pPrimaryFile == NULL );

                eSwitchMode      = fileModeNormal;
                m_ulOverlapCount = 0UL;
                m_ulBytesLogged  = 0UL;

                m_pPrimaryFile   = m_pSecondaryFile;
                m_pSecondaryFile = NULL;
            }
            __ENDFINALLY
        }
    }

    return eSwitchMode;
}


inline
bool CDisk::ReadRecordOverlapFromRegistry( void )
{
    bool bSuccess = false;

    CRegKey Key;

    Key.Attach( hKey6046_c );

    if ( Key.Open( hKey6046_c, lpsz6046RegistryKey_c ) == ERROR_SUCCESS )
    {
        DWORD dwValue = 0UL;

        if ( Key.QueryValue( dwValue, lpsz6046LoggingRecordOverlapRegistryKey_c ) == ERROR_SUCCESS )
        {
            if ( ( dwValue >= 0 ) && ( dwValue < m_ulMaxPossibleOverlap ) )
            {
                m_ulMaxRecordOverlapCount = static_cast<unsigned long>( dwValue );
                bSuccess = true;
            }
        }

        Key.Close();
    }

    return bSuccess;
}

inline
bool CDisk::WriteRecordOverlapToRegistry( void )
{
    bool bSuccess = false;

    try
    {
        CRegKey Key;
    
        Key.Attach( hKey6046_c );

        if ( Key.Open( hKey6046_c, lpsz6046RegistryKey_c ) == ERROR_SUCCESS )
        {
            bSuccess = ( Key.SetValue( static_cast<DWORD>( m_ulMaxRecordOverlapCount ), lpsz6046LoggingRecordOverlapRegistryKey_c ) == ERROR_SUCCESS );
        }
    }
    catch ( ... )
    {
        bSuccess = false;
    }

    return bSuccess;
}

inline
bool CDisk::ReadFileSizeFromRegistry( void )
{
    bool bSuccess = false;

    CRegKey Key;

    Key.Attach( hKey6046_c );

    if ( Key.Open( hKey6046_c, lpsz6046RegistryKey_c ) == ERROR_SUCCESS )
    {
        DWORD dwValue = 0UL;

        if ( Key.QueryValue( dwValue, lpsz6046MaxFileSizeRegistryKey_c ) == ERROR_SUCCESS )
        {
            m_ulMaxFileSizeInBytes = ulMegaBytesToBytes_c * static_cast<unsigned long>( dwValue );
            bSuccess = true;
        }

        Key.Close();
    }

    return bSuccess;
}

inline
bool CDisk::WriteFileSizeToRegistry( void )
{
    bool bSuccess = false;

    CRegKey Key;

    Key.Attach( hKey6046_c );

    if ( Key.Open( hKey6046_c, lpsz6046RegistryKey_c ) == ERROR_SUCCESS )
    {
        const DWORD dwValue = static_cast<DWORD>( m_ulMaxFileSizeInBytes / ulMegaBytesToBytes_c );

        bSuccess = ( Key.SetValue( dwValue, lpsz6046MaxFileSizeRegistryKey_c ) == ERROR_SUCCESS );

        Key.Close();
    }

    return bSuccess;
}

float CDisk::PercentDiskSpaceFree( void ) const
{
    // Queries the available disk space as a percentage.

    float fAvailablePercent = 0.0f;

    ULARGE_INTEGER uliFreeBytesAvailable        = { 0 };    // Bytes available to caller.
    ULARGE_INTEGER uliTotalNumberOfBytes        = { 0 };    // Bytes on disk.
    ULARGE_INTEGER uliTotalNumberOfFreeBytes    = { 0 };    // Free bytes on disk.

    if ( ::GetDiskFreeSpaceEx( m_Path, &uliFreeBytesAvailable, &uliTotalNumberOfBytes, &uliTotalNumberOfFreeBytes ) )
    {
        __int64 i64TotalSpace    = (__int64) uliTotalNumberOfBytes.QuadPart;
        __int64 i64FreeAvailable = (__int64) uliFreeBytesAvailable.QuadPart;

        if ( i64TotalSpace > 0 )
        {
            fAvailablePercent = static_cast<float>( 100.0 * ((double) i64FreeAvailable) / ((double)(i64TotalSpace)) );
        }
    }
    else
    {
        TRACE( _T( "CDisk::PercentDiskSpaceFree(), GetDiskFreeSpaceEx() failed with error code: %lu\n" ), ::GetLastError() );
    }

    return fAvailablePercent;
}
