//
//  Copyright © 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:   7kSharedMemory.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus -- re-worked version of classes supplied by P. Moberg.
//
//  Purpose:    
//
//  Notes:      
//

#include "StdAfx.h"
#include "7kSharedMemory.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// C7kSharedMemory class implementation.

C7kSharedMemory::C7kSharedMemory    ( const DWORD &rdwTotalMMFSize )
                :m_dwTotalMMFSize   ( rdwTotalMMFSize ),
                 m_dwHeaderSize     ( static_cast<DWORD>( sizeof( MMFHEADER7KSONAR ) ) )
{
    __TRY
    {
        m_Critical.Enter();

        m_hMMFile             = NULL;
        m_pbyMMFMemory        = NULL;
        m_psMMFHeader         = NULL;
        m_hEvent              = NULL;
        m_hMutex              = NULL;
        m_ullReadPos.QuadPart = 0;

        // Create an unnamed, non-signaled manual reset event to allow the wait for mutex operation to be 
        // terminated, it is an infinite duration event.

        m_hTerminateMutexWaitEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

        if ( m_hTerminateMutexWaitEvent == NULL )
        {
            TRACE( _T( "C7kSharedMemory::C7kSharedMemory(), m_hTerminateMutexWaitEvent is NULL, error code: %lu\n" ), GetLastError() );
            ASSERT( false );
        }
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY
}

C7kSharedMemory::~C7kSharedMemory( void )
{
    if ( ! Close() )
    {
        TRACE( _T( "C7kSharedMemory::~C7kSharedMemory(), Close() failed\n" ) );
    }

    __TRY
    {
        m_Critical.Enter();

        if ( m_hTerminateMutexWaitEvent != NULL )
        {
            CloseHandle( m_hTerminateMutexWaitEvent );
            m_hTerminateMutexWaitEvent = NULL;
        }
    }
    __FINALLY
    {
        ASSERT( m_hTerminateMutexWaitEvent == NULL );

        m_hTerminateMutexWaitEvent = NULL;
        m_hMMFile                  = NULL;
        m_pbyMMFMemory             = NULL;
        m_psMMFHeader              = NULL;
        m_hEvent                   = NULL;
        m_hMutex                   = NULL;
        m_ullReadPos.QuadPart      = 0;

        m_Critical.Leave();
    }
    __ENDFINALLY
}

bool C7kSharedMemory::Close( void )
{
    __TRY
    {
        m_Critical.Enter();

        TerminateWaitingMutex();

        if ( m_pbyMMFMemory != NULL )
        {
            UnmapViewOfFile( m_pbyMMFMemory );
            m_pbyMMFMemory = NULL;
            m_psMMFHeader  = NULL;
        }

        if ( m_hMMFile != NULL )
        {
            CloseHandle( m_hMMFile );
            m_hMMFile = NULL;
        }

        if ( m_hEvent )
        {
            CloseHandle( m_hEvent );
            m_hEvent = NULL;
        }
    }
    __FINALLY
    {
        m_pbyMMFMemory = NULL;
        m_psMMFHeader  = NULL;
        m_hMMFile      = NULL;
        m_hEvent       = NULL;

        m_Critical.Leave();
    }
    __ENDFINALLY

    __TRY
    {
        if ( m_hMutex != NULL )
        {
            if ( WaitForSingleObject( m_hMutex, 1000 ) == WAIT_OBJECT_0 )
            {
                ReleaseMutex( m_hMutex );
            }
        }
    }
    __FINALLY
    {
        if ( m_hMutex != NULL )
        {
            if ( ! CloseHandle( m_hMutex ) )
            {
                TRACE( _T( "C7kSharedMemory::Close(), CloseHandle() failed.\n" ) );
            }
            m_hMutex = NULL;
        }
    }
    __ENDFINALLY

    return true;
}

bool C7kSharedMemory::Open( LPCTSTR     lpsz7kMMFName,
                            LPCTSTR     lpsz7kMutexName,
                            LPCTSTR     lpsz7kEventName,
                            BOOL        bManualResetEvent )
{
    bool bSuccess  = false;     // Assume failure for now.
    bool bCreation = false;
    bool bMemValid = false;

    __TRY
    {
        m_Critical.Enter();

        bMemValid = ( m_pbyMMFMemory != NULL );
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY

    if ( bMemValid )
    {
        bSuccess = true;
    }
    else
    {
        try
        {
            m_Critical.Enter();

            if ( ! IsNameValid( lpsz7kMMFName ) )
            {
                throw failureModeInvalidMMFName;
            }

            if ( ! IsNameValid( lpsz7kMutexName ) )
            {
                throw failureModeInvalidMutexName;
            }

            if ( ! IsNameValid( lpsz7kEventName ) )
            {
                throw failureModeInvalidEventName;
            }

            if ( ( m_hMMFile = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, lpsz7kMMFName ) ) != NULL )
            {
                if ( ( m_hMutex = OpenMutex( MUTEX_ALL_ACCESS | SYNCHRONIZE, TRUE, lpsz7kMutexName ) ) == NULL )
                {
                    throw failureModeMutex;
                }

                if ( ( m_hEvent = OpenEvent( EVENT_ALL_ACCESS, TRUE, lpsz7kEventName ) ) == NULL)
                {
                    throw failureModeEvent;
                }
            }
            else 
            {
                if ( ( m_hMMFile = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, m_dwTotalMMFSize, lpsz7kMMFName ) ) == NULL )
                {
                    throw failureModeFileMapping;
                }

                if ( ( m_hMutex = CreateMutex( NULL, FALSE, lpsz7kMutexName ) ) == NULL )
                {
                    throw failureModeMutex;
                }
    
                if ( ( m_hEvent = CreateEvent( NULL, bManualResetEvent, FALSE, lpsz7kEventName ) ) == NULL )
                {
                    throw failureModeEvent;
                }

                bCreation = true;
            }
  
            if ( ( m_pbyMMFMemory = reinterpret_cast<BYTE *>( ::MapViewOfFile( m_hMMFile, FILE_MAP_WRITE, 0, 0, 0 )) ) == NULL )
            {
                throw failureModeMapView;
            }

            m_psMMFHeader = reinterpret_cast<MMFHEADER7KSONAR *>( m_pbyMMFMemory );

            bSuccess = true;
        }
        catch ( const EFAILUREMODE &reError )
        {
            bSuccess = false;
            TRACE( _T( "C7kSharedMemory::Open() failed, code %lu, type %d\n" ), ::GetLastError(), reError );
        }
        catch ( ... )
        {
            bSuccess = false;
            TRACE( _T( "C7kSharedMemory::Open() failed, unspecified exception caught!\n" ) );
        }

        m_Critical.Leave();
    }

    if ( bSuccess )
    {
        __TRY
        {
            m_Critical.Enter();

            ASSERT( m_psMMFHeader != NULL );

            if ( bCreation )
            {
                m_psMMFHeader->dwWrite               = m_dwHeaderSize;       // Write position in Bytes.
                m_psMMFHeader->uliTotalSize.QuadPart = 0;                    // Initialize to 0.
                m_psMMFHeader->dwWrap                = m_dwTotalMMFSize;     // Wrap position in Bytes.
                m_psMMFHeader->dwSize                = m_dwTotalMMFSize;     // Size of memory in Bytes.
            }
        }
        __FINALLY
        {
            m_Critical.Leave();
        }
        __ENDFINALLY
    }
    else
    {
        Close();
        TRACE( _T( "C7kSharedMemory::Open(), failure detected... object closed\n" ) );
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Protected members.
// NB: Don't protect these members with the critical section as drived class will typically 
// invoke within the critical section externally thus resulting in a deadlock.

void C7kSharedMemory::TerminateWaitingMutex( void )
{
    if ( m_hTerminateMutexWaitEvent != NULL )
    {
        SetEvent( m_hTerminateMutexWaitEvent );
    }
}

bool C7kSharedMemory::GetPositions( DWORD *pdwWritePoint, DWORD *pdwWrapPoint, unsigned __int64 *pui64TotalSize ) const
{
    bool bSuccess = false;

    ASSERT( pdwWritePoint  != NULL );
    ASSERT( pdwWrapPoint   != NULL );
    ASSERT( pui64TotalSize != NULL );

    ASSERT( m_psMMFHeader  != NULL );

    if ( m_psMMFHeader != NULL )
    {
        *pdwWritePoint  = m_psMMFHeader->dwWrite - m_dwHeaderSize;
        *pdwWrapPoint   = m_psMMFHeader->dwWrap  - m_dwHeaderSize;
        *pui64TotalSize = (unsigned __int64) (m_psMMFHeader->uliTotalSize.QuadPart);

        bSuccess = true;
    }

    return bSuccess;
}

DWORD C7kSharedMemory::GetMemorySize( void ) const
{
    DWORD dwSize = 0UL;

    ASSERT( m_psMMFHeader != NULL );

    if ( m_psMMFHeader != NULL )
    {
        dwSize = m_psMMFHeader->dwSize - m_dwHeaderSize;
    }

    return dwSize;
}

bool C7kSharedMemory::CopyData( BYTE *pbyData, const DWORD &rdwSize, const DWORD &rdwOffset )
{
    bool bSuccess = false;      // Assume failure for now.

    if ( rdwSize == 0 )
    {
        bSuccess = true;
    }
    else if ( pbyData == NULL )
    {
        bSuccess = false;
    }
    else if ( m_psMMFHeader != NULL )
    {
        if ( ( rdwSize + rdwOffset ) <= ( m_psMMFHeader->dwSize - m_dwHeaderSize ) )
        {
            memcpy( pbyData, &m_pbyMMFMemory[ m_dwHeaderSize + rdwOffset ], rdwSize );
            bSuccess = true;
        }
    }

    return bSuccess;
}

///////////////////////////////////////////////////////////////////////////////
// Private members.

inline
bool C7kSharedMemory::IsNameValid( LPCTSTR lpszName ) const
{
    return ( ( lpszName != NULL ) && ( ::lstrlen( lpszName ) > 0 ) );
}

