/*
* File name: MbufSharedMemory.cpp
* Location: See Matrox Example Launcher in the MIL Control Center
* 
*/
// MbufSharedMemory.cpp : Defines the exported functions for the DLL application.
//
//            The functions defined here can be used to create MIL buffer mapping on
//            interprocess shared memory. This can be used to share data quickly between 
//            a 32 and a 64 bit process.
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
// All Rights Reserved


#include <windows.h>
#include "mil.h"
#include "../MbufSharedMemory.h"

#ifdef __cplusplus
extern "C"
{
#endif

void MFTYPE SlaveAllocSharedMemory(MIL_ID Func);
void * MFTYPE SharedMemoryOperation(MIL_INT ControlFlag, MIL_CONST_TEXT_PTR SharedBufferName, MIL_INT64 SharedBufferSize, 
                                    MIL_UINT64 *PtrToSharedBufferHandle, char **PtrToSharedBufferPtr);
void MFTYPE SlaveFreeSharedMemory(MIL_ID Func);
#ifdef __cplusplus
}
#endif

// internal functions to abstract the shared memory functions of the operating system
static MIL_UINT64 CreateSharedMemory(MIL_CONST_TEXT_PTR SharedBufferName, MIL_INT64  SharedBufferSize, void ** MapPtr);
static MIL_UINT64 OpenSharedMemory(MIL_CONST_TEXT_PTR SharedBufferName, MIL_INT64  SharedBufferSize, void ** MapPtr);
static void       FreeSharedMemory(MIL_UINT64 SharedBufferHandle, void* MapPtr);

/* --------------------------------------------------------------------------------------- */
/* Functions to handle MIL buffers residing in a chunk of sharable inter-process memory.   */
/* --------------------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------------------- */
/* Allocate or Create a MIL buffer residing in a chunk of sharable inter-process memory.   */

// Slave function.
void MFTYPE SlaveAllocSharedMemory(MIL_ID Func)
   {
   MIL_ID MilSystem;
   MIL_INT SizeBand, SizeX, SizeY, Type, DataFormat, ControlFlag, PitchByte;
   MIL_INT SharedBufferNameLength;
   MIL_CONST_TEXT_PTR SharedBufferName;
   MIL_UINT64 *SharedBufferHandlePtr;
   MIL_ID  *SharedBufferMilIdPtr;
   MIL_INT SharedBufferBandSize;
   char *SharedBufferPtr, *SharedBufferBandPtr[3];
   
   /* Read the parameters including pointer to the Return Values data. */
   MfuncParamValueMilId(Func, 1, &MilSystem);
   MfuncParamValueMilInt(Func, 2, &SizeBand);
   MfuncParamValueMilInt(Func, 3, &SizeX);
   MfuncParamValueMilInt(Func, 4, &SizeY);
   MfuncParamValueMilInt(Func, 5, &Type);
   MfuncParamValueMilInt(Func, 6, &DataFormat); 
   MfuncParamValueMilInt(Func, 7, &ControlFlag);
   MfuncParamValueMilInt(Func, 8, &PitchByte);
   MfuncParamValueMilInt(Func, 9, &SharedBufferNameLength); 
   MfuncParamValueConstMilText(Func, 10, &SharedBufferName); 
   MfuncParamValueArrayMilUint64(Func, 11, &SharedBufferHandlePtr); 
   MfuncParamValueArrayMilId(Func, 12, &SharedBufferMilIdPtr); 

   /* Do the Operation */
   if ((ControlFlag == SHARED_MEMORY_ALLOCATE) || (ControlFlag == SHARED_MEMORY_CREATE))
      {
      // Allocate or Map the shared memory. 
      SharedMemoryOperation(ControlFlag, SharedBufferName, SizeBand*PitchByte*SizeY, 
                                                           SharedBufferHandlePtr, &SharedBufferPtr);

      // Create a MIL buffer on it if memory map file allocation or mapping was successful.
      if (SharedBufferPtr != M_NULL)
         {
         if (DataFormat & M_PLANAR) // If Planar = multiples pointers.
            {
            SharedBufferBandSize = PitchByte*SizeY;
            for (int n=0; n<SizeBand; n++)
                SharedBufferBandPtr[n] =  SharedBufferPtr+(n*SharedBufferBandSize);
            }
         else // Monochrome or Packed = One pointer.
            {
            SharedBufferBandPtr[0] =  SharedBufferPtr;
            }
         MbufCreateColor(MilSystem, SizeBand, SizeX, SizeY, Type, DataFormat, 
                         M_HOST_ADDRESS + M_PITCH_BYTE, PitchByte, (void **)&SharedBufferBandPtr, SharedBufferMilIdPtr);
         }
      else
         {  
         /* Report a MIL error. */
         MfuncErrorReport(Func,M_FUNC_ERROR+ALLOC_SHARED_MEMORY_MAPPING_FILE_ERROR_CODE,
                          MIL_TEXT("Shared Memory Operation: Could not create file mapping."), M_NULL, M_NULL, M_NULL);
         }
      }
   }


/* ----------------------------------------------------------------------- */
/* Free a MIL buffer residing in a chunk of sharable inter-process memory. */


// Slave function.
void MFTYPE SlaveFreeSharedMemory(MIL_ID Func)
   {
   MIL_UINT64 SharedBufferHandle;
   MIL_ID  SharedBufferMilId;
   char   *SharedBufferPtr;
   
  /* Read the parameters including pointer to the Return Values data. */
  MfuncParamValueMilUint64(Func, 1, &SharedBufferHandle); 
  MfuncParamValueMilId(Func, 2, &SharedBufferMilId); 

  // Read the memory pointer.
  MbufInquire(SharedBufferMilId, M_HOST_ADDRESS, &SharedBufferPtr);

  // Free the MIL buffer.
  MbufFree(SharedBufferMilId);
  
  // Free the existing shared memory. 
  SharedMemoryOperation(SHARED_MEMORY_FREE, NULL, NULL, &SharedBufferHandle, &SharedBufferPtr);
  }


/* ----------------------------------------------------------------------------------- */
/* Function to handle inter-process memory located in a maps to MemoryMapped file.     */
/* This can be used to exchange data between 2 MIL processes or between a DMIL Master  */
/* it's Slave if they resides in the same physical PC and use a common memory.         */
/* ------------------------------------------------------------------------------------*/

/* ---------------------------------------------------------------------------------------------- */
/* Allocate, Map or Free an array of inter-process shared memory located in a Memory Mapped file. */

void * MFTYPE SharedMemoryOperation(MIL_INT  ControlFlag, // ControlFlag: SHARED_MEMORY_ALLOCATE, SHARED_MEMORY_CREATE or SHARED_MEMORY_FREE.
                             MIL_CONST_TEXT_PTR SharedBufferName, // Name of the Memory Map File object.
                             MIL_INT64  SharedBufferSize,         // Size of Memory Map File object.
                             MIL_UINT64 *PtrToSharedBufferHandle,    // Pointer to store the Memory Map File object Handle.
                             char   **PtrToSharedBufferPtr)       // Pointer to store the Memory Map File memory pointer.
   {
   // Clear Handle and Pointer.
   *PtrToSharedBufferHandle = NULL;
   *PtrToSharedBufferPtr    = NULL;

   // Do the ControlFlag.

   if ((ControlFlag == SHARED_MEMORY_ALLOCATE) || (ControlFlag == SHARED_MEMORY_CREATE))
      {
      if (ControlFlag == SHARED_MEMORY_ALLOCATE)
         {
         /* Create the memory Mapped file */
         *PtrToSharedBufferHandle = CreateSharedMemory(SharedBufferName, SharedBufferSize, (void**) PtrToSharedBufferPtr);
         }
      else if (ControlFlag == SHARED_MEMORY_CREATE)
         {
         /* Open the memory Mapped file */
         *PtrToSharedBufferHandle = OpenSharedMemory(SharedBufferName, SharedBufferSize, (void**)PtrToSharedBufferPtr);
         }
      if (*PtrToSharedBufferHandle == M_NULL)
         {
         /* Report a MIL error. */
         MIL_TEXT_CHAR ErrCode[32];
         MosSprintf(ErrCode, 32, MIL_TEXT("%lu"), GetLastError());
         MfuncErrorReport(M_DEFAULT, M_FUNC_ERROR + 1,
                          MIL_TEXT("Shared Memory Operation: Could not create file mapping object."), ErrCode, M_NULL, M_NULL);
         // MosPrintf(TEXT("Shared Memory Operation: Could not create file mapping object (%d).\n"), GetLastError());
         return M_NULL;
         }

      if (*PtrToSharedBufferPtr == NULL)
         {
         MIL_TEXT_CHAR ErrCode[32];
         MosSprintf(ErrCode, 32, MIL_TEXT("%lu"), GetLastError());
         /* Report a MIL error. */
         MfuncErrorReport(M_DEFAULT, M_FUNC_ERROR + 2,
                          MIL_TEXT("Shared Memory Operation: Could not map view of file."), ErrCode, M_NULL, M_NULL);
         //MosPrintf(TEXT("Shared Memory Operation: Could not map view of file (%d).\n"), GetLastError());
         FreeSharedMemory(*PtrToSharedBufferHandle, M_NULL);
         return NULL;
         }
      }
   else if(ControlFlag == SHARED_MEMORY_FREE)
      {
      FreeSharedMemory(*PtrToSharedBufferHandle, *PtrToSharedBufferPtr);
      *PtrToSharedBufferPtr = NULL;
      *PtrToSharedBufferHandle = NULL;
      }

   return *PtrToSharedBufferPtr;
   }

MIL_UINT64 CreateSharedMemory(MIL_CONST_TEXT_PTR SharedBufferName, MIL_INT64  SharedBufferSize, void ** MapPtr)
   {
   // We initialize a SECURITY_ATTRIBUTES to an empty security
   // This permit to run the DMIL server and the client application
   // in any user account on the local machine.
   // This is of use if the server is running as a service
   // or if one of the process is run as administrator
   SECURITY_ATTRIBUTES  sa;
   SECURITY_DESCRIPTOR  sd;

   InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
   SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);

   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   sa.lpSecurityDescriptor = &sd;
   sa.bInheritHandle = TRUE;

   HANDLE SharedMemHandle = M_NULL;

   MIL_INT NameLength = MosStrlen(SharedBufferName);
   MIL_TEXT_PTR TempName = new MIL_TEXT_CHAR[NameLength + 10];

   // let's try the global namespace first
   MosSprintf(TempName, NameLength + 10, MIL_TEXT("Global\\%s"), SharedBufferName);
   SharedMemHandle = CreateFileMapping(
      INVALID_HANDLE_VALUE,            // use paging file
      &sa,                             // default security
      PAGE_READWRITE,                  // read/write access
      (long) (SharedBufferSize >> 32), // maximum object size (high-order DWORD)
      (long) SharedBufferSize,         // maximum object size (low-order DWORD)
      TempName);                       // name of mapping object

   if (SharedMemHandle == M_NULL)
      {
      // Global name not found, let's try local
      MosSprintf(TempName, NameLength + 10, MIL_TEXT("Local\\%s"), SharedBufferName);
      SharedMemHandle = CreateFileMapping(
         INVALID_HANDLE_VALUE,            // use paging file
         &sa,                             // default security
         PAGE_READWRITE,                  // read/write access
         (long) (SharedBufferSize >> 32), // maximum object size (high-order DWORD)
         (long) SharedBufferSize,         // maximum object size (low-order DWORD)
         TempName);                       // name of mapping object
      }
   delete [] TempName;
   TempName = M_NULL;

   if (MapPtr != M_NULL)
      {
      *MapPtr = M_NULL;
      if (SharedMemHandle != M_NULL)
         {
         *MapPtr = MapViewOfFile(SharedMemHandle,
            FILE_MAP_ALL_ACCESS, // read+write
            0, 0, (size_t) SharedBufferSize);
         }
      }

   return (MIL_UINT) SharedMemHandle;
   }

MIL_UINT64 OpenSharedMemory(MIL_CONST_TEXT_PTR SharedBufferName, MIL_INT64  SharedBufferSize, void ** MapPtr)
   {
   HANDLE SharedMemHandle = M_NULL;

   MIL_INT NameLength = MosStrlen(SharedBufferName);
   MIL_TEXT_PTR TempName = new MIL_TEXT_CHAR[NameLength + 10];

   // let's try the global namespace first
   MosSprintf(TempName, NameLength + 10, MIL_TEXT("Global\\%s"), SharedBufferName);

   SharedMemHandle =  OpenFileMapping(FILE_MAP_ALL_ACCESS, // read+write
                                      FALSE,               // do not inherit the name
                                      TempName);           // name of mapping object

   if (SharedMemHandle == M_NULL)
      {
      // Global name not found, let's try local
      MosSprintf(TempName, NameLength + 10, MIL_TEXT("Local\\%s"), SharedBufferName);
      SharedMemHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, // read+write
                                        FALSE,               // do not inherit the name
                                        TempName);           // name of mapping object
      }

   delete [] TempName;
   TempName = M_NULL;

   if (MapPtr != M_NULL)
      {
      *MapPtr = M_NULL;
      if (SharedMemHandle != M_NULL)
         {
         *MapPtr = MapViewOfFile(SharedMemHandle,
                                 FILE_MAP_ALL_ACCESS, // read+write
                                 0, 0, (size_t) SharedBufferSize);
         }
      }
   return (MIL_UINT) SharedMemHandle;
   }

void FreeSharedMemory(MIL_UINT64 SharedBufferHandle, void* MapPtr)
   {
   if (MapPtr != M_NULL)
      UnmapViewOfFile(MapPtr);
   if (SharedBufferHandle != M_NULL)
      CloseHandle((HANDLE) SharedBufferHandle);
   }