/*************************************************************************/
/*
* File name: FdkOffsetGainMinMaxPrimitive.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\solios\C++\SoliosFDK
 *             \OffsetGainMinMax
*
* Synopsis:  This example shows how to use the Mfpga API to create
*            parallel primitives using OffsetGain and MinMax.
 */

/* Headers. */
#include <mil.h>
#include <milfpga.h>
#include <fpga_gainoffset.h>
#include <fpga_minmax.h>
#include <fpga_splitter.h>

/* Function definition. */
#define FUNCTION_NB_PARAM_OFFSET_GAIN_MIN_MAX      7
#define FUNCTION_OPCODE_FPGA_OFFSET_GAIN_MIN_MAX   7

/* Error codes */
#define FUNCTION_PARAMETER_ERROR_CODE              1
#define FUNCTION_WRONG_FPGA_ERROR_CODE             2

/* Master, Slave and Primitive 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);
void MFTYPE FPGAOffsetGainMinMaxSlave(MIL_ID Func);
void FPGAOffsetGainMinMaxPrimitive(MIL_ID Func, MIL_ID  MilSourceImage,
           MIL_ID  MilOffsetImage, MIL_ID  MilGainImage, MIL_INT Src4,
           MIL_ID  MilImageDst, MIL_UINT32* pMinValue, MIL_UINT32* pMaxValue);
void FPGAInitializeShadowRegisters(MIL_ID Func, FPGA_GAINOFFSET_USER_ST* pOGShadow,
           FPGA_MINMAX_USER_ST* pMMShadow, FPGA_SPLITTER_USER_ST* pSplitShadow,
           MIL_BUFFER_INFO Src1, MIL_BUFFER_INFO Src2, MIL_BUFFER_INFO Src3,
           MIL_INT Src4, MIL_BUFFER_INFO Dst);

/******************************************************************************
**                                            _______
**                             SrcImage1---->| gain  |
**                _______      SrcImage2---->| offset|-----> DstImage
**               | spli- |   |-SrcImage0---->|_______|
** SrcImage0---->| tter  |___|                _______
**               |_______|   |               | min   |
**                           |-SrcImage0---->| max   |
**                                           |_______|
**
** DstImage = (((SrcImage0-SrcImage1)*SrcImage3)/Src4);
** Min      = Min(SrcImage0)
** Max      = Max(SrcImage0)
**
******************************************************************************/

/* MIL Primitive function definition. */
/* ---------------------------------- */

void FPGAOffsetGainMinMaxPrimitive(MIL_ID  Func, 
                                   MIL_ID  MilSourceImage,
                                   MIL_ID  MilOffsetImage,
                                   MIL_ID  MilGainImage,
                                   MIL_INT Src4,
                                   MIL_ID  MilImageDst,
                                   MIL_UINT32* pMinValue,
                                   MIL_UINT32* pMaxValue)
   {
   MIL_BUFFER_INFO Src, Offset, Gain, Dst;
   MIL_FPGA_CONTEXT OffsetGainContext, MinMaxContext, SplitterContext;
   FPGA_GAINOFFSET_USER_ST GOShadow;
   FPGA_MINMAX_USER_ST MMShadow;
   FPGA_SPLITTER_USER_ST SPltShadow;
   memset(&GOShadow, 0, sizeof(GOShadow));
   memset(&MMShadow, 0, sizeof(MMShadow));
   memset(&SPltShadow, 0, sizeof(SPltShadow));

   /* Inquire the buffer info object of all the buffers. */
   MfuncInquire(MilSourceImage, M_BUFFER_INFO, &Src);
   MfuncInquire(MilOffsetImage, M_BUFFER_INFO, &Offset);
   MfuncInquire(MilGainImage, M_BUFFER_INFO, &Gain);
   MfuncInquire(MilImageDst, M_BUFFER_INFO, &Dst);

   /* Allocate a synchronous command context on the OffsetGain processing unit. */
   if(MfpgaCommandAlloc(MfuncBufOwnerSystemId(Src), M_DEV0, FPGA_GAINOFFSET_FID,
         M_DEFAULT, M_DEV0, M_SYNCHRONOUS, M_DEFAULT, &OffsetGainContext))
      {
      /* Allocate a synchronous command context on the MinMax processing unit. 
         This is important because this processing unit returns a result through
         a register read.
       */
      if(MfpgaCommandAlloc(MfuncBufOwnerSystemId(Src), M_DEV0, FPGA_MINMAX_FID,
            M_DEFAULT, M_DEV0, M_SYNCHRONOUS, M_DEFAULT, &MinMaxContext))
         {
         /* Allocate a synchronous command context on the Splitter processing unit. */
         if(MfpgaCommandAlloc(MfuncBufOwnerSystemId(Src), M_DEV0, FPGA_SPLITTER_FID,
               M_DEFAULT, M_DEV0, M_SYNCHRONOUS, M_DEFAULT, &SplitterContext))
            {
            /* Initialize the shadow register structure. */
            FPGAInitializeShadowRegisters(Func, &GOShadow, &MMShadow, &SPltShadow,
               Src, Offset, Gain, Src4, Dst);

            /* Input0 of the Splitter processing unit is connected to the Src buffer. */
            MfpgaSetSource(SplitterContext, Src, M_INPUT0, M_DEFAULT);

            /* Output0 of the Splitter processing unit is connected to Input0
               of the OffsetGain processing unit. */
            MfpgaSetLink(SplitterContext, M_OUTPUT0, OffsetGainContext,
               M_INPUT0, M_DEFAULT);

            /* Output1 of the Splitter processing unit is connected to Input0
               of the MinMax processing unit. */
            MfpgaSetLink(SplitterContext, M_OUTPUT1, MinMaxContext, M_INPUT0,
               M_DEFAULT);

            /* Pass the initialized shadow register structure, it will be
               programmed when the processing operation is dispatched
               (before processing starts). */
            MfpgaSetRegister(SplitterContext, M_USER, 0, sizeof(SPltShadow),
               (void*)(&SPltShadow), M_WHEN_DISPATCHED);

            /* Input1 of the OffsetGain processing unit is connected to the Offset buffer. */
            MfpgaSetSource(OffsetGainContext, Offset, M_INPUT1, M_DEFAULT);

            /* Input2 of the OffsetGain processing unit is connected to the  Offset buffer. */
            MfpgaSetSource(OffsetGainContext, Gain, M_INPUT2, M_DEFAULT);

            /* Output0 of the OffsetGain processing unit is connected to the Dst buffer. */
            MfpgaSetDestination(OffsetGainContext, Dst, M_OUTPUT0, M_DEFAULT);

            /* Pass the initialized shadow register structure, it will be programmed when 
               the processing operation is dispatched (before processing starts). */
            MfpgaSetRegister(OffsetGainContext, M_USER, 0, sizeof(GOShadow),
               (void*)(&GOShadow), M_WHEN_DISPATCHED);

            /* Pass the initialized shadow register structure, it will be programmed when 
               the processing operation is dispatched(before processing starts). */
            MfpgaSetRegister(MinMaxContext, M_USER, 0, sizeof(MMShadow),
                             (void*)(&MMShadow), M_WHEN_DISPATCHED);

            /* Pass the initialized shadow register structure again, it will
               be read back when the processing operation is completed. */
            MfpgaGetRegister(MinMaxContext, M_USER, 0, sizeof(MMShadow),
                                  (void*)(&MMShadow), M_WHEN_COMPLETED);

            /* Queue the processing operation, we use the M_WAIT flag because
               other MfpgaCommandQueue follows This lets the software know that
               it must wait for other MfpgaCommandQueue operations. */
            MfpgaCommandQueue(OffsetGainContext,M_DEFAULT, M_WAIT);
            MfpgaCommandQueue(MinMaxContext,    M_DEFAULT, M_WAIT);

            /* Queue the processing operation, we use the M_DEFAULT flag */
            /* This lets the software know that it can issue the processing
               operation to the FPGA. */
            MfpgaCommandQueue(SplitterContext,  M_DEFAULT, M_DEFAULT);

            /* Retrieve the result from the FPGA. */
            *pMinValue = (MIL_UINT32)MMShadow.min.f.min;
            *pMaxValue = (MIL_UINT32)MMShadow.max.f.max;

            /* Free the allocated context. */
            MfpgaCommandFree(OffsetGainContext, M_DEFAULT);
            MfpgaCommandFree(MinMaxContext, M_DEFAULT);
            MfpgaCommandFree(SplitterContext, M_DEFAULT);
            }
         else
            {
            /* Report a MIL error. */
            MfuncErrorReport( 
                Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
                MIL_TEXT("No Splitter acceleration supported by this FPGA configuration."),
                MIL_TEXT("Please select the proper FPGA configuration file."),
                MIL_TEXT("See the processing FPGA section of the milconfig utility."),
                M_NULL
                );
            }
         }
      else
         {
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
             MIL_TEXT("No MinMax acceleration supported by this FPGA configuration."),
             MIL_TEXT("Please select the proper FPGA configuration file."),
             MIL_TEXT("See the processing FPGA section of the milconfig utility."),
             M_NULL
             );
         }
      }
   else
      {
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
          MIL_TEXT("No Gain&Offset acceleration supported by this FPGA configuration."),
          MIL_TEXT("Please select the proper FPGA configuration file."),
          MIL_TEXT("See the processing FPGA section of the milconfig utility."),
          M_NULL
          );
      }
   }

/* MIL PU Register initialization function definition. */
/* --------------------------------------------------- */

void FPGAInitializeShadowRegisters( MIL_ID Func,  
                                    FPGA_GAINOFFSET_USER_ST* pOGShadow,
                                    FPGA_MINMAX_USER_ST* pMMShadow,
                                    FPGA_SPLITTER_USER_ST* pSplitShadow,
                                    MIL_BUFFER_INFO Src1,
                                    MIL_BUFFER_INFO Src2,
                                    MIL_BUFFER_INFO Src3,
                                    MIL_INT Src4,
                                    MIL_BUFFER_INFO Dst)
   {
   MIL_UINT lMaxValue, Src1Type, Src2Type, Src3Type, DstType, i, j;
   lMaxValue = (MIL_UINT)MfuncBufMaxValue(Src1);
   Src1Type = MfuncBufType(Src1);
   Src2Type = MfuncBufType(Src2);
   Src3Type = MfuncBufType(Src3);
   DstType = MfuncBufType(Dst);

   /*****************************
   ** Set OffsetGain registers
   *****************************/

   /*Set in-out types. */
   switch(Src1Type)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.srctype = 0x1; break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.srctype = 0x3; break;
      default:
         MfuncErrorReport( 
            Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
            MIL_TEXT("Invalid source buffer type."),
            M_NULL, M_NULL, M_NULL
            );
         break;
      }

   switch(Src2Type)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.offtype = 0x1; break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.offtype = 0x3; break;
      default:
         MfuncErrorReport( 
            Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
            MIL_TEXT("Invalid offset buffer type."),
            M_NULL, M_NULL, M_NULL
            );
         break;
      }

   switch(Src3Type)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.gaintype = 0x1;   break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.gaintype = 0x3;   break;
      default:
         MfuncErrorReport( 
            Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
            MIL_TEXT("Invalid gain buffer type."),
            M_NULL, M_NULL, M_NULL
            );
         break;
      }

   switch(DstType)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.dsttype = 0x1;   break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.dsttype = 0x3;   break;
      default:
         MfuncErrorReport( 
            Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
            MIL_TEXT("Invalid destination buffer type."),
            M_NULL, M_NULL, M_NULL
            );
         break;
      }

   /* Set the value we want to clip to. */
   pOGShadow->clipval.f.clipval  = lMaxValue;

   /* Compute the division factor. */
   j=1;
   for(i=0; i<32; i++)
      {
      if(Src4 & j)
         {
         pOGShadow->ctrl.f.shift = i;
         break;
         }
      j <<= 1;
      }

   /*****************************
   ** Set MinMax registers
   *****************************/

   /*Set the shadow min and max values to extremes. */
   pMMShadow->max.f.max = 0;
   pMMShadow->min.f.min = 0xffffffff;

   /* Program the source buffer type. */
   switch(Src1Type)
      {
      case 8+M_SIGNED:
         pMMShadow->ctrl.f.srctype = 0;   break;
      case 8+M_UNSIGNED:
         pMMShadow->ctrl.f.srctype = 1;   break;
      case 16+M_SIGNED:
         pMMShadow->ctrl.f.srctype = 2;   break;
      case 16+M_UNSIGNED:
         pMMShadow->ctrl.f.srctype = 3;   break;
      case 32+M_SIGNED:
         pMMShadow->ctrl.f.srctype = 4;   break;
      case 32+M_UNSIGNED:
         pMMShadow->ctrl.f.srctype = 5;   break;
      default:
         MfuncErrorReport( 
            Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
            MIL_TEXT("Invalid source buffer type."),
            M_NULL, M_NULL, M_NULL
            );
         break;
      }

   /*****************************
   ** Set Splitter registers
   *****************************/

   /* Enable Output0 and Output 1 of splitter processing unit. */
   pSplitShadow->ctrl.f.outen = 0x3;
   }


/* MIL Master function definition. */
/* ------------------------------- */
void FPGAOffsetGainMinMax(MIL_ID SrcImageId, MIL_ID OffsetImageId,
                         MIL_ID GainImageId, MIL_INT Src4, MIL_ID DstImageId,
                         MIL_UINT32* pMinValue, MIL_UINT32* pMaxValue)
   {
   MIL_ID   Func;

   /* Allocate a MIL function context that will be used to call a target 
      Slave function locally on the Host to do the processing.
   */
   MfuncAlloc(MIL_TEXT("FPGAOffsetGainMinMax"), 
              FUNCTION_NB_PARAM_OFFSET_GAIN_MIN_MAX,
              FPGAOffsetGainMinMaxSlave, M_NULL, M_NULL, 
              M_USER_MODULE_1+FUNCTION_OPCODE_FPGA_OFFSET_GAIN_MIN_MAX, 
              M_DEFAULT, 
              &Func
             );

   /* Register the parameters. */
   MfuncParamMilId      (Func, 1, SrcImageId,    M_IMAGE, M_IN);
   MfuncParamMilId      (Func, 2, OffsetImageId, M_IMAGE, M_IN);
   MfuncParamMilId      (Func, 3, GainImageId,   M_IMAGE, M_IN);
   MfuncParamMilInt     (Func, 4, Src4);
   MfuncParamMilId      (Func, 5, DstImageId,    M_IMAGE, M_OUT);
   MfuncParamDataPointer(Func, 6, pMinValue, sizeof(MIL_UINT32), M_OUT);
   MfuncParamDataPointer(Func, 7, pMaxValue, sizeof(MIL_UINT32), M_OUT);

   /* Call the target Slave function. */
   MfuncCall(Func);

   /* Free the MIL function context. */
   MfuncFree(Func);
   }

/* MIL Slave function definition. */
/* ------------------------------ */
void MFTYPE FPGAOffsetGainMinMaxSlave(MIL_ID Func)
   {
   MIL_ID      SrcImageId, OffsetImageId, GainImageId, DstImageId;
   MIL_INT     Src4;
   MIL_UINT32  *pMinValue, *pMaxValue;

   /* Read the parameters. */
   MfuncParamValue(Func, 1, &SrcImageId);
   MfuncParamValue(Func, 2, &OffsetImageId); 
   MfuncParamValue(Func, 3, &GainImageId);
   MfuncParamValue(Func, 4, &Src4);
   MfuncParamValue(Func, 5, &DstImageId);
   MfuncParamValue(Func, 6, &pMinValue);
   MfuncParamValue(Func, 7, &pMaxValue);
    
   /* Call the primitive controling the processing FPGA using the Mfpga API. */
   FPGAOffsetGainMinMaxPrimitive(Func, SrcImageId, OffsetImageId,
                          GainImageId, Src4, DstImageId, pMinValue, pMaxValue);
   }