//
//  Copyright © 2001 - 2002, RESON Inc. All Rights Reserved.
//
//  No part of this file may be reproduced or transmitted in any form or by
//  any means, electronic or mechanical, including photocopy, recording, or
//  information storage or retrieval system, without permission in writing
//  from RESON Inc.
///
//  Filename:   MemMap.cpp
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    Defines and implements the template class CMemoryMappedFile for access to memory mapped data.
//
//  Notes:      1)  This class provides access to the MMF data. It does access in a non-thread safe manner.
//                  Clients are therefore responsible for thread safe access.
//

#if !defined(AFX_MEMMAP_H__3AEDE2CC_4B33_4549_A4A0_A4A878536A78__INCLUDED_)
#define AFX_MEMMAP_H__3AEDE2CC_4B33_4549_A4A0_A4A878536A78__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

//////////////////////////////////////////////////////////////////////
// CMemoryMappedFile class definition.

#include "Critical.h"
#include "Swmrg.h"

#include <string.h>

template <class tData>
class CMemoryMappedFile
{
public:

    ///////////////
    // Services.

    // Construction, destruction and assignment.

                                CMemoryMappedFile       (   LPCTSTR                         lpszMapName     = _T( "RESON_6046_MEMMAP" ),
                                                            bool                            bUseDiskFile    = false );

                                CMemoryMappedFile       (   const CMemoryMappedFile<tData>  &rRhs );

    virtual                    ~CMemoryMappedFile       (   void );

    CMemoryMappedFile<tData> &  operator =              (   const CMemoryMappedFile<tData>  &rRhs );

    // General public access members.

    bool                        IsInitialized           (   void )  const;

    bool                        Map                     (   LPCTSTR                         lpszMapName,            // Maps the MMF - used when default constructor is used.
                                                            bool                            bUseDiskFile );

    bool                        Unmap                   (   void );

    tData *                     MappedData              (   void )  const;                                          // MMF data access pointer.

                                operator tData *        (   void )  const;                                          // MMF data access as a cast operation on this objects of this class.

    const char  *               MappingName             (   void )  const;

private:

    ///////////////
    // Attributes.

    const HANDLE                m_hMemoryFile;                                                                      // Handle to use if MMF is to reside in system swap space only.
    const DWORD                 m_dwSizeOfMMF;                                                                      // Size of the MMF (from template injected type).

    tData                      *m_pMMFData;                                                                         // Pointer to the MMF area of generic type.

    bool                        m_bUseDiskFile;                                                                     // Flag indicating whether data is to be memory or disk resident.
    bool                        m_bInitialized;

    HANDLE                      m_hFile;                                                                            // MMF file mapping.
    HANDLE                      m_hFileMapping;                                                                     // MMF mapping handle.

    char                        m_szMappingName     [ MAX_PATH ];                                                   // Mapping name.
    char                        m_szMappingFileName [ MAX_PATH ];                                                   // MMF file name.

};

//////////////////////////////////////////////////////////////////////////////
// CMemoryMappedFile class implementation.

template <class tData>
CMemoryMappedFile<tData>::CMemoryMappedFile(    LPCTSTR lpszMapName,
                                                bool    bUseDiskFile )

                         :m_hMemoryFile( INVALID_HANDLE_VALUE ),
                          m_dwSizeOfMMF( sizeof( tData ) )
{
    m_bInitialized           = false;
    m_bUseDiskFile           = false;
                            
    m_hFile                  = NULL;
    m_hFileMapping           = NULL;

    m_szMappingName[ 0 ]     = '\0';
    m_szMappingFileName[ 0 ] = '\0';

    if ( ! Map( lpszMapName, bUseDiskFile ) )
    {
        TRACE( "CMemoryMappedFile<tData>::CMemoryMappedFile(), Can't create MMF mapping.\n" );
        ASSERT( false );
        return;
    }

    m_bInitialized           = true;
}

template <class tData>
CMemoryMappedFile<tData>::CMemoryMappedFile( const CMemoryMappedFile<tData> &rRhs )

                         :m_hMemoryFile( INVALID_HANDLE_VALUE ),
                          m_dwSizeOfMMF( sizeof( tData ) )
{
    m_bInitialized           = false;
    m_bUseDiskFile           = false;

    m_hFile                  = NULL;
    m_hFileMapping           = NULL;

    m_szMappingName[ 0 ]     = '\0';
    m_szMappingFileName[ 0 ] = '\0';

    if ( ! Map( rRhs.m_lpszMapName, rRhs.m_bUseDiskFile ) )
    {
        TRACE( "CMemoryMappedFile<tData>::CMemoryMappedFile(), Can't create MMF mapping.\n" );
        ASSERT( false );
        return;
    }

    m_bInitialized  = true;
}

template <class tData>
CMemoryMappedFile<tData> & CMemoryMappedFile<tData>::operator = ( const CMemoryMappedFile<tData> &rRhs )
{
    m_bInitialized           = false;
    m_bUseDiskFile           = false;

    m_hFile                  = NULL;
    m_hFileMapping           = NULL;

    m_szMappingName[ 0 ]     = '\0';
    m_szMappingFileName[ 0 ] = '\0';

    if ( ! Map( rRhs.m_lpszMapName, rRhs.m_bUseDiskFile ) )
    {
        TRACE( "CMemoryMappedFile<tData>::CMemoryMappedFile(), Can't create MMF mapping.\n" );
        ASSERT( false );
        return;
    }

    m_bInitialized = true;

    return *this;
}

template <class tData>
CMemoryMappedFile<tData>::~CMemoryMappedFile( void )
{
    Unmap();
}

template <class tData>
bool CMemoryMappedFile<tData>::IsInitialized( void ) const
{
    return m_bInitialized;
}

template <class tData>
bool CMemoryMappedFile<tData>::Map( LPCTSTR lpszMapName,
                                    bool    bUseDiskFile )
{
    // First, check to see if this MMF object already exists. If so, map a view of it, otherwise
    // we'll try to create it.

    m_bUseDiskFile = bUseDiskFile;

    if ( lpszMapName == NULL )
    {
        lpszMapName = _T( "RESON_6046_MEMMAP" );
    }

    strcpy( m_szMappingName, lpszMapName );

    if ( ( m_hFileMapping = ::OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, m_szMappingName ) ) != NULL )
    {
        TRACE( _T( "CMemoryMappedFile<tData>::Map(), Using existing MMF\n" ) );
    }
    else
    {
        if ( m_bUseDiskFile )
        {
            ::sprintf( m_szMappingFileName, "c:\\%s.bin", lpszMapName );
    
            if ( m_hFile != NULL )
            {
                ::CloseHandle( m_hFile );
                m_hFile = NULL;
            }
       
            if ( ( m_hFile = ::CreateFile(  m_szMappingFileName,
                                            GENERIC_READ | GENERIC_WRITE,
                                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                                            NULL,
                                            OPEN_ALWAYS,
                                            FILE_ATTRIBUTE_NORMAL,
                                            NULL ) )                   == INVALID_HANDLE_VALUE )
            {
                m_hFile = NULL;
                TRACE( _T( "CMemoryMappedFile<tData>::Map(), Can't create memory mapped file\n" ) );
            }
        }
        else
        {
            m_hFile = m_hMemoryFile;
        }
    
        if ( ( m_hFileMapping = ::CreateFileMapping( m_hFile, NULL, PAGE_READWRITE, 0, m_dwSizeOfMMF, m_szMappingName ) ) == NULL )
        {
            TRACE( _T( "CMemoryMappedFile<tData>::Map(), Cannot create file mapping\n" ) );
        }
    }

    // Map the entire file.

    if ( m_hFileMapping != NULL )
    {
        m_pMMFData = reinterpret_cast<tData *>( ::MapViewOfFile( m_hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ) );
    }

    return ( ( m_hFileMapping != NULL ) && ( m_pMMFData != NULL ) );
}

template <class tData>
bool CMemoryMappedFile<tData>::Unmap( void )
{
    if ( m_hFileMapping != NULL )
    {
        ::UnmapViewOfFile( m_pMMFData );
        ::CloseHandle( m_hFileMapping );

        m_pMMFData     = NULL;
        m_hFileMapping = NULL;
    }

    if ( ( ! m_bUseDiskFile ) && ( m_hFile != NULL ) )
    {
        ::CloseHandle( m_hFile );
        m_hFile = NULL;
    }

    return ( m_hFileMapping == NULL );
}

template< class tData >
tData * CMemoryMappedFile<tData>::MappedData( void ) const
{
    return m_pMMFData;
}

template< class tData >
CMemoryMappedFile<tData>::operator tData * ( void ) const
{
    return static_cast<tData *>( m_pMMFData );
}

template< class tData >
const char * CMemoryMappedFile<tData>::MappingName( void ) const
{
    return m_szMappingName;
}

#endif // !defined(AFX_MEMMAP_H__3AEDE2CC_4B33_4549_A4A0_A4A878536A78__INCLUDED_)
