//***************************************************************************************
//
// File name: exampleinterface.cpp
// Location: See Matrox Example Launcher in the MIL Control Center
// 
//
// Synopsis:  Implementation for CExampleInterface (implementation common to subclasses).
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
// All Rights Reserved

#include <mil.h>
#include "exampleinterface.h"

//*****************************************************************************
// Constants.
//*****************************************************************************

// Set this to 0 if you wish to remove the images pasted in the overlay.
#define ENABLE_IMAGES_IN_OVERLAY 1

// File names of images that will be pasted in the overlay.
static MIL_CONST_TEXT_PTR const OVERLAY_IMAGE_FILES[] =
   {
   EXAMPLE_IMAGE_PATH MIL_TEXT("CalibrateCameraImage.mim"),    // corresponds to CExampleInterface::eCalibrateCameraImage
   EXAMPLE_IMAGE_PATH MIL_TEXT("AdjustMinIntensityImage.mim"), // corresponds to CExampleInterface::eAdjustMinContrastImage
   EXAMPLE_IMAGE_PATH MIL_TEXT("CalibrateLaserImage.mim")      // corresponds to CExampleInterface::eCalibrateLaserImage
   };

// File name of an image used to frame the images to be pasted in the overlay.
static MIL_CONST_TEXT_PTR const FRAME_IMAGE_FILE =
   EXAMPLE_IMAGE_PATH MIL_TEXT("frame.mim");

// Transparency color of the overlay.
static const MIL_DOUBLE DEFAULT_TRANSPARENT_COLOR = M_RGB888(16,0,0);

// Transparency color of the frame image.
static const MIL_DOUBLE FRAME_TRANSPARENT_COLOR   = M_RGB888(227,0,227);

// Size of the border in the camera image (used when pasting in the overlay).
static const MIL_INT FRAME_BORDER = 5; // in pixels

// Maximum ratio of picture to paste in the overlay with respect to camera image size.
static const MIL_DOUBLE MAX_PICTURE_RATIO = 0.25;

//*****************************************************************************
// Constructor. Allocates MIL application, system and display.
//*****************************************************************************
CExampleInterface::CExampleInterface(MIL_CONST_TEXT_PTR SystemDescriptor, MIL_INT64 BufAttr)
: m_BufferAttribute(BufAttr),
  m_SizeX          (   0   ),
  m_SizeY          (   0   ),
  m_OverlayOffsetX (   0   ),
  m_OverlayOffsetY (   0   ),
  m_CalibrationMode(   0   ),
  m_MilApplication (M_NULL ),
  m_MilSystem      (M_NULL ),
  m_MilDisplay     (M_NULL ),
  m_MilDisplayImage(M_NULL ),
  m_MilOverlayImage(M_NULL )
   {
   // Allocate application.
   MappAlloc(M_NULL, M_DEFAULT, &m_MilApplication);

   // Allocate system (SystemDescriptor depends on subclass).
   MsysAlloc(M_DEFAULT, SystemDescriptor, M_DEFAULT, M_DEFAULT, &m_MilSystem);

   // Allocate display.
   MdispAlloc(m_MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_WINDOWED, &m_MilDisplay);
   for (MIL_INT i = 0 ; i < eNbOverlayImages; i++)
      {
      m_MilOverlayPictures[i] = M_NULL;
      }
   }

//*****************************************************************************
// Destructor, free all MIL objects.
//*****************************************************************************
CExampleInterface::~CExampleInterface()
   {
   for (MIL_INT i = 0; i < eNbOverlayImages; ++i)
      {
      if (m_MilOverlayPictures[i] != M_NULL)
         MbufFree(m_MilOverlayPictures[i]);
      }
   if (m_MilDisplayImage != M_NULL)
      MbufFree(m_MilDisplayImage);
   if (m_MilDisplay != M_NULL)
      MdispFree(m_MilDisplay);
   if (m_MilSystem != M_NULL)
      MsysFree(m_MilSystem);
   if (m_MilApplication != M_NULL)
      MappFree(m_MilApplication);
   }

//*****************************************************************************
// Prompts the user for the example type to use (will determine which subclass
// will be instantiated in main code).
//*****************************************************************************
bool CExampleInterface::AskInteractiveExample()
   {
   MosPrintf(MIL_TEXT("You can run this example with images:\n")
             MIL_TEXT("   1. Reloaded from disk     (STAND-ALONE)\n")
             MIL_TEXT("   2. Grabbed using a camera (INTERACTIVE)\n\n")
             MIL_TEXT("Your choice (1 or 2): "));

   MIL_INT Choice = 0;
   while (Choice == 0)
      {
      switch (MosGetch())
         {
         case MIL_TEXT('1'):
            Choice = 1;
            MosPrintf(MIL_TEXT("1. STAND-ALONE\n\n"));
            break;

         case MIL_TEXT('2'):
            Choice = 2;
            MosPrintf(MIL_TEXT("2. INTERACTIVE\n\n"));
            break;
         }
      }

   bool IsInteractive = (Choice == 2);
   return IsInteractive;
   }

//*****************************************************************************
// Prompts the user for the 3d reconstruction context type to use.
//*****************************************************************************
MIL_INT CExampleInterface::AskCalibrationMode()
   {
   MosPrintf(MIL_TEXT("Choose a calibration mode:\n")
             MIL_TEXT("   1. M_DEPTH_CORRECTION\n")
             MIL_TEXT("   2. M_CALIBRATED_CAMERA_LINEAR_MOTION\n\n")
             MIL_TEXT("Your choice (1 or 2): "));

   MIL_INT CalibrationMode = M_NULL;
   while (CalibrationMode == M_NULL)
      {
      switch (MosGetch())
         {
         case MIL_TEXT('1'):
            CalibrationMode = M_DEPTH_CORRECTION;
            MosPrintf(MIL_TEXT("1. M_DEPTH_CORRECTION\n\n")

                      MIL_TEXT("For this calibration mode, the following steps will be executed:\n")
                      MIL_TEXT("   - Setting up the laser line extraction parameters.\n")
                      MIL_TEXT("   - Calibrating the 3d reconstruction setup.\n\n"));
            break;

         case MIL_TEXT('2'):
            CalibrationMode = M_CALIBRATED_CAMERA_LINEAR_MOTION;
            MosPrintf(MIL_TEXT("2. M_CALIBRATED_CAMERA_LINEAR_MOTION\n\n")

                      MIL_TEXT("For this calibration mode, the following steps will be executed:\n")
                      MIL_TEXT("   - Calibrating the camera.\n")
                      MIL_TEXT("   - Setting up the laser line extraction parameters.\n")
                      MIL_TEXT("   - Calibrating the 3d reconstruction setup.\n\n"));
            break;
         }
      }

   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   m_CalibrationMode = CalibrationMode;
   return CalibrationMode;
   }

//*****************************************************************************
// Show the display window and enables its overlay. If it is the first time the
// function is called, the display image will also be allocated. Also, all images
// that will be pasted in the overlay are loaded from disk and "framed".
//*****************************************************************************
void CExampleInterface::ShowDisplay()
   {
   // Only do this on the first call.
   if (m_MilDisplayImage == M_NULL)
      {
      // Allocate display image (m_BufferAttribute - M_NULL or M_GRAB - depends on subclass).
      MbufAlloc2d(m_MilSystem, m_SizeX, m_SizeY, 8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP+m_BufferAttribute, &m_MilDisplayImage);
      MbufClear(m_MilDisplayImage, 0.0);

      // Set overlay transparent color.
      MdispControl(m_MilDisplay, M_TRANSPARENT_COLOR, DEFAULT_TRANSPARENT_COLOR);

      // Set up images to be pasted in the overlay.
      // Determine size of the images.
      MIL_INT PictureSizeX, PictureSizeY;
      DeterminePictureSize(&PictureSizeX, &PictureSizeY);

      // Load frame image for overlay and resize it to picture size.
      MIL_ID MilFrameOriginalImage = MbufRestore(FRAME_IMAGE_FILE, m_MilSystem, M_NULL);
      MIL_ID MilFrameImage = MbufAllocColor(m_MilSystem, 3, PictureSizeX, PictureSizeY, 8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, M_NULL);
      MimResize(MilFrameOriginalImage, MilFrameImage, M_FILL_DESTINATION, M_FILL_DESTINATION, M_NEAREST_NEIGHBOR);
      MbufFree(MilFrameOriginalImage);

      // Load overlay images that will be used later, resize them and frame them.
      for (MIL_INT i = 0; i < eNbOverlayImages; ++i)
         {
         // Load and resize image.
         MIL_ID MilOriginalImage = MbufRestore(OVERLAY_IMAGE_FILES[i], m_MilSystem, M_NULL);
         MbufAllocColor(m_MilSystem, 3, PictureSizeX, PictureSizeY, 8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, &m_MilOverlayPictures[i]);
         MimResize(MilOriginalImage, m_MilOverlayPictures[i], M_FILL_DESTINATION, M_FILL_DESTINATION, M_BICUBIC);
         MbufFree(MilOriginalImage);

         // Adds the frame to the overlay images. Only pixels that are not transparent are copied.
         MbufCopyCond(MilFrameImage, m_MilOverlayPictures[i], MilFrameImage, M_NOT_EQUAL, FRAME_TRANSPARENT_COLOR);
         }

      // Free the frame image.
      MbufFree(MilFrameImage);

      // Precompute position to copy the overlay images (top-right corner of overlay).
      m_OverlayOffsetX = m_SizeX - PictureSizeX - FRAME_BORDER;
      m_OverlayOffsetY = FRAME_BORDER;
      }

   // Select image to display.
   MdispSelect(m_MilDisplay, m_MilDisplayImage);

   // Prepare for overlay annotations.
   MdispControl(m_MilDisplay, M_OVERLAY, M_ENABLE);
   MdispInquire(m_MilDisplay, M_OVERLAY_ID, &m_MilOverlayImage);
   }

//*****************************************************************************
// Disables the overlay and hide the display window.
//*****************************************************************************
void CExampleInterface::HideDisplay()
   {
   // Disable overlay.
   m_MilOverlayImage = M_NULL;
   MdispControl(m_MilDisplay, M_OVERLAY, M_DISABLE);

   // Hide display.
   MdispSelect(m_MilDisplay, M_NULL);
   }

//*****************************************************************************
// Used in main code to paste an image in the overlay according to the current
// action the user should be doing.
//*****************************************************************************
void CExampleInterface::CopyInOverlay(EOverlayImage eOverlayImage)
   {
#if ENABLE_IMAGES_IN_OVERLAY
   MbufCopyClip(m_MilOverlayPictures[eOverlayImage], m_MilOverlayImage, m_OverlayOffsetX, m_OverlayOffsetY);
#endif
   }

//*****************************************************************************
// Used by subclasses to set image size.
//*****************************************************************************
void CExampleInterface::SetImageSize(MIL_INT SizeX, MIL_INT SizeY)
   {
   m_SizeX = SizeX;
   m_SizeY = SizeY;
   }

//*****************************************************************************
// Returns true if MIL objects in the base class have been allocated successfully.
//*****************************************************************************
bool CExampleInterface::IsValidBase() const
   {
   bool IsValid = ( m_MilApplication != M_NULL &&
                    m_MilSystem      != M_NULL &&
                    m_MilDisplay     != M_NULL );
   return IsValid;
   }

//*****************************************************************************
// Chooses the size of the images to paste in the overlay according to the
// camera image size.
//*****************************************************************************
void CExampleInterface::DeterminePictureSize(MIL_INT* pPictureSizeX, MIL_INT* pPictureSizeY) const
   {
   // Initialize size with actual picture size.
   MIL_INT PictureSizeX = MbufDiskInquire(FRAME_IMAGE_FILE, M_SIZE_X, M_NULL);
   MIL_INT PictureSizeY = MbufDiskInquire(FRAME_IMAGE_FILE, M_SIZE_Y, M_NULL);

   MIL_DOUBLE ResizeFactor = 1.0;

   // Both PictureSizeX and PictureSizeY must be smaller than 1/4 the camera image size.
   if (PictureSizeX > m_SizeX * MAX_PICTURE_RATIO)
      {
      ResizeFactor = m_SizeX * MAX_PICTURE_RATIO / PictureSizeX;
      }
   if (PictureSizeY > m_SizeY * MAX_PICTURE_RATIO)
      {
      MIL_DOUBLE ResizeFactorY = m_SizeY * MAX_PICTURE_RATIO / PictureSizeY;
      if (ResizeFactorY < ResizeFactor)
         ResizeFactor = ResizeFactorY;
      }

   // Fill output variables.
   *pPictureSizeX = static_cast<MIL_INT>( PictureSizeX * ResizeFactor );
   *pPictureSizeY = static_cast<MIL_INT>( PictureSizeY * ResizeFactor );
   }