//
//  Copyright © 2004, 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:   7kInBoundMemory.cpp
//
//  Project:    6046
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:      
//

#include "StdAfx.h"
#include "RESON7kSonar.h"
#include "7kInBoundMemory.h"

#include "..\..\..\Utils\NetUtils\7kProtocolVersion.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Module constants and definitions.

namespace                                                                       // Begin anonymous namespace.
{
#pragma pack( push, PACK_7KINBOUNDMEMORY_CPP, 1 )

    typedef struct tagSOURCEINFO
    {
        DWORD   dwAddress;
        WORD    wPort;
        WORD    wTransmissionId;
        DWORD   dwType;
        SOCKET  hSocket;
    }
    SOURCEINFO, *PSOURCEINFO;

#pragma pack( pop, PACK_7KINBOUNDMEMORY_CPP )

    const unsigned long ul7kMMFInSize_c          = 8UL * 1024UL * 1024UL;       // 8 MBytes.

    LPCTSTR             lpsz7kInBoundMMFName_c   = _T( "7kInBoundMemory" );
    LPCTSTR             lpsz7kInBoundMutexName_c = _T( "7kInBoundMutex"  );
    LPCTSTR             lpsz7kInBoundEventName_c = _T( "7kInBoundEvent"  );

}                                                                               // End anonymous namespace.

//////////////////////////////////////////////////////////////////////
// C7kInBoundMemory class implementation.

C7kInBoundMemory::C7kInBoundMemory( void )
                 :C7kSharedMemory( ul7kMMFInSize_c )
{
}

C7kInBoundMemory::~C7kInBoundMemory( void )
{
}

bool C7kInBoundMemory::Open( void )
{
    return C7kSharedMemory::Open( lpsz7kInBoundMMFName_c, lpsz7kInBoundMutexName_c, lpsz7kInBoundEventName_c, TRUE );
}

bool C7kInBoundMemory::Write(   const BYTE     *pbyData,
                                const DWORD    &rdwSize,
                                const DWORD    &rdwAddress,
                                const WORD     &rwPort,
                                const WORD     &rwTransmissionId,
                                const DWORD    &rdwType,
                                const SOCKET   &rhSocket )
{
    bool bSuccess = false;

    __TRY
    {
        m_Critical.Enter();

        ASSERT( m_hMutex       != NULL );
        ASSERT( m_hEvent       != NULL );
        ASSERT( m_pbyMMFMemory != NULL );
        ASSERT( m_psMMFHeader  != NULL );

        if ( m_hMutex == NULL )
        {
            bSuccess = false;
            TRACE( _T( "C7kInBoundMemory::Write(), m_hMutex is NULL.\n" ) );
        }
        else if ( m_hEvent == NULL )
        {
            bSuccess = false;
            TRACE( _T( "C7kInBoundMemory::Write(), m_hEvent is NULL\n" ) );
        }
        else if ( m_pbyMMFMemory == NULL )
        {
            bSuccess = false;
            TRACE( _T( "C7kInBoundMemory::Write(), m_pbyMMFMemory is NULL\n" ) );
        }
        else if ( m_psMMFHeader == NULL )
        {
            bSuccess = false;
            TRACE( _T( "C7kInBoundMemory::Write(), m_psMMFHeader is NULL\n" ) );
        }
        else if ( rdwSize >= ( m_dwTotalMMFSize - m_dwHeaderSize ) )
        {
            bSuccess = false;
            TRACE( _T( "C7kInBoundMemory::Write(), Total record size is greater that MMF size\n" ) );
        }
        else
        {
            ASSERT( m_hMutex != NULL );
            ASSERT( m_hTerminateMutexWaitEvent != NULL );

            HANDLE ahWaitObjects[] = { m_hMutex, m_hTerminateMutexWaitEvent };
            DWORD  dwEventCount    = DimOf_m( ahWaitObjects );

            ASSERT( dwEventCount == 2UL );

            if ( WaitForMultipleObjects( dwEventCount, &ahWaitObjects[ 0 ], FALSE, INFINITE ) == WAIT_OBJECT_0 )
            {
                __TRY
                {
                    const DWORD dwSourceInfoSize    = static_cast<DWORD>( sizeof( SOURCEINFO ) );
                    const DWORD dwNetworkFrameSize  = static_cast<DWORD>( sizeof( NETWORKFRAMEHEADER ) );

                    DWORD dwWritePosition           = m_psMMFHeader->dwWrite;

                    // Memory wrap?

                    if ( ( m_psMMFHeader->dwWrite + rdwSize + dwSourceInfoSize + dwNetworkFrameSize ) >= m_psMMFHeader->dwSize )
                    {
                        m_psMMFHeader->dwWrap = m_psMMFHeader->dwWrite;
                        dwWritePosition       = m_dwHeaderSize;
                    }

                    // Set the source info and network frame (prefixes to the 7k record).

                    SOURCEINFO *psSourceInfo = reinterpret_cast<SOURCEINFO *>( &m_pbyMMFMemory[ dwWritePosition ] );

                    psSourceInfo->dwAddress             = rdwAddress;
                    psSourceInfo->wPort                 = rwPort;
                    psSourceInfo->wTransmissionId       = rwTransmissionId;
                    psSourceInfo->dwType                = rdwType;
                    psSourceInfo->hSocket               = rhSocket;

                    // Now the network header frame.

                    NETWORKFRAMEHEADER * const psNetworkFrame = reinterpret_cast<NETWORKFRAMEHEADER *>( &m_pbyMMFMemory[ dwWritePosition + dwSourceInfoSize ] );

                    memset( psNetworkFrame, 0x00, sizeof( NETWORKFRAMEHEADER ) );

                    psNetworkFrame->m_unVersion         = NF_VERSION_7K;
                    psNetworkFrame->m_unOffset          = sizeof( NETWORKFRAMEHEADER );             // Offset, in bytes, to the start of data from the start of this packet.
                    psNetworkFrame->m_ulTotalPackets    = 1UL;                                      // Number of network packets for set of records transmitted
                    psNetworkFrame->m_unTotalRecords    = 1;                                        // Total number of records in network packets transmitted (helper field for parsing data). Max 128 records per transmission.
                    psNetworkFrame->m_ulPacketSize      = sizeof( NETWORKFRAMEHEADER ) + rdwSize;   // Size in bytes of this packet including the header and appended data.
                    psNetworkFrame->m_ulTotalSize       = psNetworkFrame->m_ulPacketSize;           // Total size in bytes of all packets in transmission excluding network frame(s).

                    // Copy the record data to the MMF.

                    memcpy( &m_pbyMMFMemory[ dwWritePosition + dwSourceInfoSize + dwNetworkFrameSize ], pbyData, rdwSize );
  
                    // Set write pointer and total size.

                    m_psMMFHeader->dwWrite                = dwWritePosition + dwSourceInfoSize + dwNetworkFrameSize + rdwSize;
                    m_psMMFHeader->uliTotalSize.QuadPart += ( dwSourceInfoSize + dwNetworkFrameSize + rdwSize );

                    bSuccess = true;
                }
                __FINALLY
                {
                    // Release ownership of the mutex.

                    if ( ! ReleaseMutex( m_hMutex ) )
                    {
                        bSuccess = false;
                        TRACE( _T( "C7kInBoundMemory::Write(), ::ReleaseMutex() failed with code: %lu\n" ), GetLastError() );
                    }
                }
                __ENDFINALLY

                if ( bSuccess )
                {
                    // Set the 7k sonar's inbound MMF event indicating new data is available.

                    if ( ! SetEvent( m_hEvent ) )       // if ( ! PulseEvent( m_hEvent ) )
                    {
                        bSuccess = false;
                        TRACE( _T( "C7kInBoundMemory::Write(), ::SetEvent() failed with code: %lu\n" ), GetLastError() );
                    }
                }
            }
        }
    }
    __FINALLY
    {
        m_Critical.Leave();
    }
    __ENDFINALLY
    
    return bSuccess;
}

