/******************************************************************************/
/*
 * File name: FdkLutMapMain.cpp
 * Location: See Matrox Example Launcher in the MIL Control Center
 * 
 *
 * Synopsis:  This example shows how to use a custom FPGA accelerated
 *            processing function: LutMap. It also use it to do real time 
 *            processing on the input from a camera.
 *
 * Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
 * All Rights Reserved
 */

/* Headers. */
#include <mil.h>

#define BUFFERING_SIZE_MAX 20

/* User's processing function prototype. */
MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr);

/* User's processing function hook data structure. */
typedef struct
   {
   MIL_ID  MilImageLut;
   MIL_ID  MilImageDisp;
   MIL_INT ProcessedImageCount;
   } HookDataStruct;

/* Master MIL functions declarations. */
void FPGALoadLut(MIL_ID LutId);
void FPGALutMap(MIL_ID  SrcImageId, MIL_ID  DstImageId, MIL_ID  LutId);

/* Main function. */
int MosMain(void)
   {
   MIL_ID   MilApplication;
   MIL_ID   MilSystem     ;
   MIL_ID   MilDisplay    ;
   MIL_ID   MilDigitizer  ;
   MIL_ID   MilGrabBufferList[BUFFERING_SIZE_MAX] = { 0 };
   MIL_INT  MilGrabBufferListSize;
   MIL_ID   MilLutBuffer  ;
   MIL_ID   MilImageDisp  ;
   MIL_INT  SizeX, SizeY, BoardType, NbFrames = 0;
   MIL_INT ProcessFrameCount  = 0;
   MIL_DOUBLE ProcessFrameRate = 0;
   HookDataStruct UserHookData;

   /* Allocate defaults. */
   MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, &MilDisplay, &MilDigitizer, M_NULL);

   /* Check if the processing FPGA is present. */
   MsysInquire(MilSystem, M_BOARD_TYPE, &BoardType);
   if(!(BoardType & M_PF))
      {
      MosPrintf(MIL_TEXT("Error, this example needs a processing FPGA installed on your\n"));
      MosPrintf(MIL_TEXT("board to continue.\n"));
      MosPrintf(MIL_TEXT("Press <Enter> to quit.\n"));
      MosGetch();
      MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, M_NULL);
      return 0;
      }

   /* Inquire the digitizer aquisition Size. */
   SizeX = MdigInquire(MilDigitizer, M_SIZE_X, M_NULL);
   SizeY = MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL);

   /* Allocate a display buffer. For FPGA acceleration, destination buffers 
      can be in host FPGA accessible memory or in FPGA memory. */
   MbufAlloc2d(MilSystem,
               SizeX,
               SizeY,
               8+M_UNSIGNED,
               M_IMAGE+M_GRAB+M_DISP+M_HOST_MEMORY+M_FPGA_ACCESSIBLE,
               &MilImageDisp);
   MbufClear(MilImageDisp, 0);
   MdispSelect(MilDisplay, MilImageDisp);

   /* Allocate a LUT buffer and initialize it. */
   MbufAlloc1d(MilSystem,
               256,
               8+M_UNSIGNED,
               M_LUT+M_FPGA_ACCESSIBLE,
               &MilLutBuffer);
   /* Generate an inverted ramp in the LUT buffer. */
   MgenLutRamp(MilLutBuffer, 0, 255, 255, 0);

   /* Start a continuous grab on the display. */
   MdigGrabContinuous(MilDigitizer, MilImageDisp);

   MosPrintf(MIL_TEXT("This example demonstrates how to use the Mfpga API to program\n"));
   MosPrintf(MIL_TEXT("a LutMap primitive and do live processing.\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetchar();

   /* Start the live processing. */
   MdigHalt(MilDigitizer);

   /* Allocate the grab buffers and clear them. For FPGA acceleration,
   all source buffers must be in FPGA accessible memory.
   */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   for (MilGrabBufferListSize = 0;
      MilGrabBufferListSize<BUFFERING_SIZE_MAX; MilGrabBufferListSize++)
      {
      MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + M_UNSIGNED,
         M_IMAGE + M_GRAB + M_PROC + M_FPGA_ACCESSIBLE,
         &MilGrabBufferList[MilGrabBufferListSize]);
      if (MilGrabBufferList[MilGrabBufferListSize])
         MbufClear(MilGrabBufferList[MilGrabBufferListSize], 0xFF);
      else
         break;
      }
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);


   /* Load the LUT buffer in the Lut PU. */
   FPGALoadLut(MilLutBuffer);
   
   /* Initialize the User's processing function data structure. */
   UserHookData.MilImageDisp        = MilImageDisp;
   UserHookData.MilImageLut         = MilLutBuffer;
   UserHookData.ProcessedImageCount = 0;

   /* Start the processing. The processing function is called for every frame grabbed. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
                             M_START, M_DEFAULT, ProcessingFunction, &UserHookData);

   /* NOTE: Now the main() is free to perform other tasks while the processing is executing. */
   /* -------------------------------------------------------------------------------------- */

   /* Print a message and wait for a key press after a minimum number of frames. */
   MosPrintf(MIL_TEXT("Press <Enter> to stop.\n\n"));
   MosGetch();

   /* Stop the processing. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
               M_STOP, M_DEFAULT, ProcessingFunction, &UserHookData);

   /* Print statistics. */
   MdigInquire(MilDigitizer, M_PROCESS_FRAME_COUNT,  &ProcessFrameCount);
   MdigInquire(MilDigitizer, M_PROCESS_FRAME_RATE,   &ProcessFrameRate);
   MosPrintf(MIL_TEXT("\n\n%lld frames grabbed at %.1f frames/sec (%.1f ms/frame).\n"),
                          (long long)ProcessFrameCount, ProcessFrameRate, 1000.0/ProcessFrameRate);
   MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
   MosGetch();

   /* Free the MIL objects.*/
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);
   MbufFree(MilLutBuffer);
   MbufFree(MilImageDisp);
   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, M_NULL);

   return 0;
   }

   
/* User's call back processing function called every time a grab buffer is modified. */
/* ----------------------------------------------------------------------------------*/

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId;

   /* Retrieve the MIL_ID of the grabbed buffer. */
   MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);

   /* Perform the processing and update the display. */
   FPGALutMap(ModifiedBufferId, UserHookDataPtr->MilImageDisp, UserHookDataPtr->MilImageLut);
   
   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;
   MosPrintf(MIL_TEXT("Processing frame #%lld.\r"), (long long)UserHookDataPtr->ProcessedImageCount);

   return 0;
   }