/*************************************************************************************/
/*
 * File name: chunkmode.cpp
 * Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\gigevision\C++\chunkmode
 *             
 *
 * Synopsis:  This program shows the use of the MdigProcess() function to do perform
 *            real-time acquisition. It also enables your GenICam(c) device in chunk
 *            mode (if supported).
 *
 *      Note: The chunks used in the example might not be present in your GigE Vision
 *            device or they might have different names. If this is the case then you
 *            will have to change the example to use the chunk names that matches
 *            your GigE Vision device.
 *
 * Copyright (C) Matrox Electronic Systems Ltd., 1992-2015.
 * All Rights Reserved
 */
#include <mil.h>

 /* Number of images in the buffering grab queue.
    Generally, increasing this number gives better real-time grab.
  */
#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  MilDigitizer;
   MIL_ID  MilImageDisp;
   MIL_INT ProcessedImageCount;
   MIL_TEXT_PTR ChunkDataName;
   } HookDataStruct;


/* Main function. */
/* ---------------*/

int MosMain(void)
{
   MIL_ID MilApplication;
   MIL_ID MilSystem     ;
   MIL_ID MilDigitizer  ;
   MIL_ID MilDisplay    ;
   MIL_ID MilImageDisp  ;
   MIL_ID MilGrabBufferList[BUFFERING_SIZE_MAX] = { 0 };
   MIL_INT MilGrabBufferListSize;
   MIL_INT ProcessFrameCount  = 0;
   MIL_INT NbFrames           = 0, n=0;
   MIL_INT BoardType;
   MIL_DOUBLE ProcessFrameRate= 0;
   MIL_BOOL ChunkModeActive = M_FALSE;
   HookDataStruct UserHookData;

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

   /* Get information on the system we are using and print a welcome message to the console. */
   MsysInquire(MilSystem, M_BOARD_TYPE, &BoardType);

   if(((BoardType & M_BOARD_TYPE_MASK) != M_GIGE_VISION)&&((BoardType & M_BOARD_TYPE_MASK) != M_USB3_VISION))
      {
      MosPrintf(MIL_TEXT("This example program can only be used with the Matrox Driver for ")
         MIL_TEXT("GigE Vision or USB3 Vision.\n"));
      MosPrintf(MIL_TEXT("Please ensure that the default system type is set accordingly in ")
         MIL_TEXT("MIL Config.\n"));
      MosPrintf(MIL_TEXT("-------------------------------------------------------------\n\n"));
      MosPrintf(MIL_TEXT("Press <enter> to quit.\n"));
      MosGetch();
      MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp);
      return 1;
      }

   /* Validate that Chunk Mode is supported by the GigE Vision device. */
   MappControl(M_ERROR, M_PRINT_DISABLE);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ChunkModeActive"), M_TYPE_BOOLEAN, &ChunkModeActive);
   if(MappGetError(M_CURRENT, M_NULL) != M_NULL_ERROR)
      {
      MosPrintf(MIL_TEXT("Your GigE/USB3 Vision device does not support chunk mode.\n\n"));
      MosPrintf(MIL_TEXT("Press <enter> to quit.\n"));
      MosGetch();
      MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp);
      return 1;
      }
   MappControl(M_ERROR, M_PRINT_ENABLE);

   /* Allocate the grab buffers and clear them. */
   MappControl(M_ERROR, M_PRINT_DISABLE);
   for(MilGrabBufferListSize = 0; 
            MilGrabBufferListSize<BUFFERING_SIZE_MAX; MilGrabBufferListSize++)
      {
      MbufAlloc2d(MilSystem,
                  MdigInquire(MilDigitizer, M_SIZE_X, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL),
                  8+M_UNSIGNED,
                  M_IMAGE+M_GRAB+M_PROC,
                  &MilGrabBufferList[MilGrabBufferListSize]);

      if (MilGrabBufferList[MilGrabBufferListSize])
         {
         MbufClear(MilGrabBufferList[MilGrabBufferListSize], 0xFF);
         }
      else
         break;
      }
   MappControl(M_ERROR, M_PRINT_ENABLE);

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

   /* Initialize the User's processing function data structure. */
   UserHookData.MilDigitizer = MilDigitizer;
   UserHookData.MilImageDisp = MilImageDisp;
   UserHookData.ProcessedImageCount = 0;
   UserHookData.ChunkDataName = M_NULL;

   /* Enable chunk mode. */
   MosPrintf(MIL_TEXT("\nEnabling chunk mode.\n"));
   MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("ChunkModeActive"), M_DEFAULT, MIL_TEXT("1"));
   
   /* Enable timestamp chunks. */
   MosPrintf(MIL_TEXT("Please select a chunk data to enable.\n"));
   MIL_INT ChunkCount = 0;
   MIL_TEXT_PTR* ChunkNames = 0;
   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("ChunkSelector"), M_DEFAULT, &ChunkCount);
   if (ChunkCount)
      {
      ChunkNames = new MIL_TEXT_PTR[ChunkCount];
      for (MIL_INT i = 0; i < ChunkCount; i++)
         {
         MIL_INT Len = 0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME + M_STRING_SIZE + i, MIL_TEXT("ChunkSelector"), M_DEFAULT, &Len);
         ChunkNames[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME + i, MIL_TEXT("ChunkSelector"), M_DEFAULT, ChunkNames[i]);
         MosPrintf(MIL_TEXT("%20s (%d) %s\n"), MIL_TEXT(""), i, ChunkNames[i]);
         }
      bool Done = false;
      MIL_INT Selection = 0;
      do
         {
         MosPrintf(MIL_TEXT("\nPlease select the event you wish to hook a function to: "));
#if M_MIL_USE_WINDOWS
         scanf_s("%ld", &Selection);
#else
         scanf("%ld", &Selection);
#endif
         if ((Selection >= 0) && (Selection < ChunkCount))
            Done = 1;
         else
            MosPrintf(MIL_TEXT("\nInvalid selection"));
         } while (!Done);

         MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("ChunkSelector"), M_DEFAULT, ChunkNames[Selection]);
         MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("ChunkEnable"), M_DEFAULT, MIL_TEXT("1"));
         
         size_t ChunkDataNameLen = MosStrlen(ChunkNames[Selection]) + 6;
         UserHookData.ChunkDataName = new MIL_TEXT_CHAR[ChunkDataNameLen];
         MosSprintf(UserHookData.ChunkDataName, ChunkDataNameLen,MIL_TEXT("Chunk%s"), ChunkNames[Selection]);
      }

   /* 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 grab buffers. */
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);

   /* Release defaults. */
   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp);

   if (ChunkNames)
      {
      for (MIL_INT i = 0; i < ChunkCount; i++)
         {
         delete ChunkNames[i];
         }
      delete ChunkNames;
      delete UserHookData.ChunkDataName;
      }

   return 0;
}


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

/* Local defines. */
#define STRING_LENGTH_MAX  20
#define STRING_POS_X       20
#define STRING_POS_Y       20

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, 
                                                    void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId;
   MIL_TEXT_CHAR Text[STRING_LENGTH_MAX]= {MIL_TEXT('\0'),};

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

   /* Read the Chunk Data */
   MIL_INT64 ChunkData = 0;
   MdigInquireFeature(UserHookDataPtr->MilDigitizer, M_FEATURE_VALUE, UserHookDataPtr->ChunkDataName, M_TYPE_INT64, &ChunkData);
   MosPrintf(MIL_TEXT("Chunk data: %I64d \r"), ChunkData);

   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;
   MosSprintf(Text, STRING_LENGTH_MAX, MIL_TEXT("%ld"), 
                                       UserHookDataPtr->ProcessedImageCount);
   MgraText(M_DEFAULT, ModifiedBufferId, STRING_POS_X, STRING_POS_Y, Text);

   /* Perform the processing and update the display. */
   MbufCopy(ModifiedBufferId, UserHookDataPtr->MilImageDisp);
   
   return 0;
   }