/******************************************************************************/
/*
 * File name: FdkOffsetGainMinMaxMain.cpp
 * Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\solios\C++\SoliosFDK
 *             \OffsetGainMinMax
 *
 * Synopsis:  This example shows how to use a custom FPGA accelerated
 *            processing function to execute parallel primitives using
 *            OffsetGain and MinMax.
 */

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

#define BUFFERING_SIZE_MAX 22

/* 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  MilImageOffset;
   MIL_ID  MilImageGain;
   MIL_ID  MilImageDisp;
   MIL_INT DivisionFactor;
   MIL_INT ProcessedImageCount;
   } HookDataStruct;

/* Master MIL functions declarations. */
void FPGAOffsetGainMinMax(MIL_ID SrcImageId, MIL_ID OffsetImageId,
                          MIL_ID GainImageId, MIL_INT Src4, MIL_ID DstImageId,
                          MIL_UINT32* pMinValue, MIL_UINT32* pMaxValue);

/* 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     MilImageGain  ;
   MIL_ID     MilImageOffset;
   MIL_ID     MilImageDisp  ;
   MIL_INT    SizeX, SizeY, BoardType, n, NbFrames = 0;
   MIL_INT    ProcessFrameCount  = 0;
   MIL_DOUBLE ProcessFrameRate = 0;
   HookDataStruct UserHookData;

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

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

   /* Allocate buffers. */
   /* For FPGA acceleration, all source buffers must be in FPGA memory.*/
   SizeX = MdigInquire(MilDigitizer, M_SIZE_X, M_NULL);
   SizeY = MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL);
   
   /* Allocate the grab buffers and clear them. */
   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);

   /* Free buffers to leave space for possible temporary buffers. */
   for (n=0; n<2 && NbFrames; n++)
      {
      MilGrabBufferListSize--;
      MbufFree(MilGrabBufferList[MilGrabBufferListSize]);
      }

   MbufAlloc2d(MilSystem,
               SizeX,
               SizeY,
               8+M_UNSIGNED,
               M_IMAGE+M_PROC+M_FPGA_ACCESSIBLE,
               &MilImageGain);
   MbufAlloc2d(MilSystem,
               SizeX,
               SizeY,
               8+M_UNSIGNED,
               M_IMAGE+M_PROC+M_FPGA_ACCESSIBLE,
               &MilImageOffset);

   /* Clear the Gain and Offset buffers to some constants. These will be used
       on a per-pixel basis by the GainOffset processing unit. */
   MbufClear(MilImageGain, 16);
   MbufClear(MilImageOffset, 2);

   /* For FPGA acceleration, destination buffers can be in host non paged
      memory or in FPGA memory. */
   MbufAlloc2d(MilSystem,
               SizeX,
               SizeY,
               8+M_UNSIGNED,
               M_IMAGE+M_DISP+M_GRAB+M_HOST_MEMORY+M_FPGA_ACCESSIBLE,
               &MilImageDisp);
   MbufClear(MilImageDisp, 0);
   MdispSelect(MilDisplay, MilImageDisp);
 
   /* Start continuous grab and wait for a key. */
   MdigGrabContinuous(MilDigitizer, MilImageDisp);
   MosPrintf(MIL_TEXT("This example demonstrates how to use the Mfpga API to program\n"));
   MosPrintf(MIL_TEXT("a parallel OffsetGain+MinMax primitive. Press <Enter> to continue.\n"));
   MosGetchar();

   MdigHalt(MilDigitizer);
   
   /* Initialize the User's processing function data structure. */
   UserHookData.MilImageDisp        = MilImageDisp;
   UserHookData.MilImageOffset      = MilImageOffset;
   UserHookData.MilImageGain        = MilImageGain;
   UserHookData.DivisionFactor      = 8;
   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%ld frames grabbed at %.1f frames/sec (%.1f ms/frame).\n"),
                          ProcessFrameCount, ProcessFrameRate, 1000.0/ProcessFrameRate);
   MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
   MosGetch();

   /* Free the MIL objects.*/
   /* Free the grab buffers. */
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);

   MbufFree(MilImageGain);
   MbufFree(MilImageOffset);
   MbufFree(MilImageDisp);
   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, M_NULL);

   return 0;
   }

/* User's 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_UINT32 MinValue, MaxValue;
   MIL_ID ModifiedBufferId;

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

   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;

   /* Perform the processing and update the display. */
   FPGAOffsetGainMinMax(ModifiedBufferId, UserHookDataPtr->MilImageOffset,
      UserHookDataPtr->MilImageGain, 4, UserHookDataPtr->MilImageDisp,
      &MinValue, &MaxValue);

   MosPrintf(MIL_TEXT("Processing frame #%d:\t MinValue: %d MaxValue: %d\r"),
      UserHookDataPtr->ProcessedImageCount, MinValue, MaxValue);
      
   return 0;
   }