/*************************************************************************/
/*
* File name: enumfeatures.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\gigevision\C++\enumfeatures
 *             
*
* Synopsis:  This example shows how to use MIL in order to enumerate
*            all the features in you GenICam(c) compliant device.
*/                                                                   
#include <mil.h>
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>

using namespace std;
/* define a STL compatible string that maps to MIL strings. */
typedef basic_string<MIL_TEXT_CHAR, char_traits<MIL_TEXT_CHAR>, allocator<MIL_TEXT_CHAR> >
   milstring;
typedef basic_ostringstream<MIL_TEXT_CHAR, char_traits<MIL_TEXT_CHAR>, allocator<MIL_TEXT_CHAR> >
   omilstringstream;

/* String vector containing the feature names. */
vector<milstring> FeatureList;

/* Routine used to retrieve various info related to a feature. */
void GetFeatureProperties(MIL_ID MilDigitizer, MIL_CONST_TEXT_PTR FeatureName);

/* Routine used to enumerate the device's GenICam(c) node tree. */
void EnumerateGenICamNodeTree(MIL_ID MilDigitizer, MIL_CONST_TEXT_PTR Node, MIL_INT RecurseCount);

/* Print everything that is Guru level or less. */
#define VISIBILITY_LEVEL       M_FEATURE_VISIBILITY_GURU

/* Helper function used to convert a feature type into its string representation. */
milstring TypeToString(MIL_INT64 Type);
milstring VisibilityToString(MIL_INT64 Visibility);
milstring RepresentationToString(MIL_INT64 Representation);
/* Main function. */
int MosMain(void)
   { 
   MIL_ID   MilApplication;
   MIL_ID   MilSystem     ;
   MIL_ID   MilDigitizer  ;
   MIL_INT SystemType = M_NULL;

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

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

   if(SystemType != M_SYSTEM_GIGE_VISION_TYPE   &&
      SystemType != M_SYSTEM_USB3_VISION_TYPE   &&
      SystemType != M_SYSTEM_RADIENTCXP_TYPE    )
      {
      /* Print error message. */
      MosPrintf(MIL_TEXT("This example program can only be used with the Matrox Driver for ")
         MIL_TEXT("GenICam.\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, M_NULL, MilDigitizer, M_NULL);
      return 1;
      }

   MosPrintf(MIL_TEXT("This example shows how query various feature properties.\n"));
   MosPrintf(MIL_TEXT("It also shows how to enumerate all the features present in your\n")
      MIL_TEXT("GenICam compliant device.\n\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   MosPrintf(MIL_TEXT("Getting feature properties and value.\n\n"));
   /* Retrieve feature info related to the PixelFormat feature. */
   GetFeatureProperties(MilDigitizer, MIL_TEXT("PixelFormat"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue and enumerate all GenICam features.\n\n"));
   MosGetch();

   /* Enumerate all features under the root node. */
   EnumerateGenICamNodeTree(MilDigitizer, MIL_TEXT("Root"), 0);

   MosPrintf(MIL_TEXT("\nFinished enumeration.\n\n"));
   MosPrintf(MIL_TEXT("Note: due to console width constraints, some strings printed might have been\nclipped.\n\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

#if M_MIL_USE_WINDOWS
   MosPrintf(MIL_TEXT("Starting the MIL Feature Browser\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to quit.\n\n"));
   MdigControl(MilDigitizer, M_GC_FEATURE_BROWSER, M_OPEN+M_ASYNCHRONOUS);
   MosGetch();
#endif

   MappFreeDefault(MilApplication, MilSystem, M_NULL, MilDigitizer, M_NULL);

   return 0;
   }

/* Routine used to retrieve various info related to a feature. */
void GetFeatureProperties(MIL_ID MilDigitizer, MIL_CONST_TEXT_PTR FeatureName)
   {
   MIL_INT Len = 0;
   MIL_TEXT_PTR Str = NULL;

   /* Inquire the feature's name. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_NAME+M_STRING_SIZE, FeatureName, M_DEFAULT, &Len);
   if(Len)
      {
      Str = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_NAME, FeatureName, M_DEFAULT, Str);
      MosPrintf(MIL_TEXT("FeatureName:        %s\n"), Str);
      delete [] Str; Str = NULL;
      }

   /* Inquire the feature's display name. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_DISPLAY_NAME+M_STRING_SIZE, FeatureName, M_DEFAULT, &Len);
   if(Len)
      {
      Str = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_DISPLAY_NAME, FeatureName, M_DEFAULT, Str);
      MosPrintf(MIL_TEXT("FeatureDisplayName: %s\n"), Str);
      delete [] Str; Str = NULL;
      }

   /* Inquire the feature's tool tip string. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_TOOLTIP+M_STRING_SIZE, FeatureName, M_DEFAULT, &Len);
   if(Len)
      {
      Str = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_TOOLTIP, FeatureName, M_DEFAULT, Str);
      MosPrintf(MIL_TEXT("FeatureTooltip:     %s\n"), Str);
      delete [] Str; Str = NULL;
      }

   /* Inquire the feature's description string. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_DESCRIPTION+M_STRING_SIZE, FeatureName, M_DEFAULT, &Len);
   if(Len)
      {
      Str = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_DESCRIPTION, FeatureName, M_DEFAULT, Str);
      MosPrintf(MIL_TEXT("FeatureDescription: %s\n"), Str);
      delete [] Str; Str = NULL;
      }

   MIL_INT64 FeatureType = 0;
   /* Inquire the feature's native data type. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_TYPE, FeatureName, M_DEFAULT, &FeatureType);
   MIL_INT FeatureSize = 0;
   /* Inquire the feature's native data size in bytes. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_SIZE, FeatureName, M_DEFAULT, &FeatureSize);
   MIL_INT64 FeatureAccessMode = 0;
   /* Inquire the feature's access mode. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_ACCESS_MODE, FeatureName, M_DEFAULT, &FeatureAccessMode);
   MIL_INT64 FeatureVisibility = 0;
   /* Inquire the feature's visibility attribute. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_VISIBILITY, FeatureName, M_DEFAULT, &FeatureVisibility);
   MIL_INT64 FeatureCachingMode = 0;
   /* Inquire the feature's caching mode attribute. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_CACHING_MODE, FeatureName, M_DEFAULT, &FeatureCachingMode);
   MIL_INT64 FeatureStreamable = 0;
   /* Inquire the feature's streamable attribute. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_STREAMABLE, FeatureName, M_DEFAULT, &FeatureStreamable);
   MIL_INT64 FeatureDeprecated  = 0;
   /* Inquire the feature's deprecated attribute. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_DEPRECATED, FeatureName, M_DEFAULT, &FeatureDeprecated);

   MosPrintf(MIL_TEXT("\nType:               %s\n"), TypeToString(FeatureType).c_str());
   MosPrintf(MIL_TEXT("Size:               %lld bytes\n"), FeatureSize);
   MosPrintf(MIL_TEXT("Readable:           %s\n"), M_FEATURE_IS_READABLE(FeatureAccessMode)?MIL_TEXT("true"):MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("Writable:           %s\n"), M_FEATURE_IS_WRITABLE(FeatureAccessMode)?MIL_TEXT("true"):MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("Visibility:         %s\n"), VisibilityToString(FeatureVisibility).c_str());
   MosPrintf(MIL_TEXT("Available:          %s\n"), M_FEATURE_IS_AVAILABLE(FeatureAccessMode)?MIL_TEXT("true"):MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("Implemented:        %s\n"), M_FEATURE_IS_IMPLEMENTED(FeatureAccessMode)?MIL_TEXT("true"):MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("Cachable:           %s\n"), M_FEATURE_IS_CACHABLE(FeatureCachingMode)?MIL_TEXT("true"):MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("Streamable:         %s\n"), FeatureStreamable?MIL_TEXT("true"):MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("Deprecated:         %s\n"), FeatureDeprecated?MIL_TEXT("true"):MIL_TEXT("false"));


   MIL_INT64 IntVal = 0;
   MIL_DOUBLE DoubleVal = 0;
   MIL_BOOL BoolVal = 0;
   MIL_TEXT_PTR StrVal = NULL;
   MIL_TEXT_PTR ValueAsStrVal = NULL;
   MIL_UINT8* RegVal = NULL;
   MIL_INT64 RegLen = 0;

   /* Inquire the feature's value. */
   if(M_FEATURE_IS_READABLE(FeatureAccessMode))
      {
      /* For floating point type features inquire the feature's value range. */
      if(FeatureType == M_TYPE_DOUBLE)
         {
         MIL_DOUBLE Min = 0, Max = 0;
         MIL_INT64 Representation = 0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_MIN, FeatureName, FeatureType, &Min);
         MdigInquireFeature(MilDigitizer, M_FEATURE_MAX, FeatureName, FeatureType, &Max);
         MdigInquireFeature(MilDigitizer, M_FEATURE_REPRESENTATION, FeatureName, M_DEFAULT, &Representation);
         MosPrintf(MIL_TEXT("Min:                %f\n"), Min);
         MosPrintf(MIL_TEXT("Max:                %f\n"), Max);
         MosPrintf(MIL_TEXT("Representation:     %s\n"), RepresentationToString(Representation).c_str());
         }
      /* For integer type features inquire the feature's value range. */
      else if(FeatureType == M_TYPE_INT64)
         {
         MIL_INT64 Min = 0, Max = 0, Inc = 0, Representation = 0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_MIN, FeatureName, FeatureType, &Min);
         MdigInquireFeature(MilDigitizer, M_FEATURE_MAX, FeatureName, FeatureType, &Max);
         MdigInquireFeature(MilDigitizer, M_FEATURE_INCREMENT, FeatureName, FeatureType, &Inc);
         MdigInquireFeature(MilDigitizer, M_FEATURE_REPRESENTATION, FeatureName, M_DEFAULT, &Representation);
         MosPrintf(MIL_TEXT("Min:                %d\n"), Min);
         MosPrintf(MIL_TEXT("Max:                %d\n"), Max);
         MosPrintf(MIL_TEXT("Inc:                %d\n"), Inc);
         MosPrintf(MIL_TEXT("Representation:     %s\n"), RepresentationToString(Representation).c_str());
         }

      /* Inquire the feature's value as a sting. */
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, FeatureName, M_DEFAULT, &Len);
      if(Len)
         {
         ValueAsStrVal = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, FeatureName, M_DEFAULT, ValueAsStrVal);
         MosPrintf(MIL_TEXT("Value as string:    %s\n"), ValueAsStrVal);
         }

      /* Inquire the feature's value using it's native data type. */
      switch(FeatureType)
         {
         case M_TYPE_INT64:
            {
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, FeatureName, FeatureType, &IntVal);
            MosPrintf(MIL_TEXT("Value:              %lld (0x%llx)\n"), IntVal, IntVal);
            }
            break;
         case M_TYPE_DOUBLE:
            {
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, FeatureName, FeatureType, &DoubleVal);
            MosPrintf(MIL_TEXT("Value:              %f\n"), DoubleVal);
            }
            break;
         case M_TYPE_STRING:
            {
            MIL_INT Size = 0;
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, FeatureName, FeatureType, &Size);
            StrVal = new MIL_TEXT_CHAR[Size];
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, FeatureName, FeatureType, StrVal);
            MosPrintf(MIL_TEXT("Value:              %s\n"), StrVal);
            }
            break;
         case M_TYPE_BOOLEAN:
            {
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, FeatureName, FeatureType, &BoolVal);
            MosPrintf(MIL_TEXT("Value:              %d\n"), BoolVal);
            }
            break;
         case M_TYPE_ENUMERATION:
            {
            MIL_INT Size = 0;
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, FeatureName, FeatureType, &IntVal);
            MosPrintf(MIL_TEXT("Value:              %lld (0x%llx)\n"), IntVal, IntVal);
            }
            break;
         case M_TYPE_REGISTER:
            {
            MdigInquireFeature(MilDigitizer, M_FEATURE_SIZE, FeatureName, M_DEFAULT, &RegLen);
            RegVal = new MIL_UINT8[(MIL_INT)RegLen];
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, FeatureName, FeatureType, RegVal);
            MosPrintf(MIL_TEXT("Value: "));
            for(int i=0; i<RegLen; i++)
               MosPrintf(MIL_TEXT(" %d "), RegVal[i]);
            }
            break;

         case M_TYPE_COMMAND:
         case M_TYPE_CATEGORY:
         default:
            /* Command and category features types do not have feature values. */
            break;
         }
      }

   if(StrVal)
      delete [] StrVal;
   if(ValueAsStrVal)
      delete [] ValueAsStrVal;
   if(RegVal)
      delete [] RegVal;

   MosPrintf(MIL_TEXT("\n\n"));
   }

/* Routine used to enumerate the device's GenICam(c) node tree. */
void EnumerateGenICamNodeTree(MIL_ID MilDigitizer, MIL_CONST_TEXT_PTR Node, MIL_INT RecurseCount)
   {
   MIL_INT NodeCount = 0, SubNodeCount = 0, Length = 0;
   MIL_TEXT_PTR FeatureName = NULL;
   MIL_TEXT_PTR FeatureValue = NULL;
   milstring Format;
   omilstringstream stream;

   if(RecurseCount == 0)
      {
      MosPrintf(MIL_TEXT("%-40.39s%-19.18s%20.19s\n"), MIL_TEXT("Feature Name"), MIL_TEXT("Feature Type"),
         MIL_TEXT("Feature Value"));
      MosPrintf(MIL_TEXT("--------------------------------------------------------------------------------\n\n"));
      }

   /* Prepare printf input format string. */
   for(MIL_INT i=0; i<RecurseCount; i++)
      stream << MIL_TEXT("   ");
   stream << MIL_TEXT("%-") << 40-(RecurseCount*3) << MIL_TEXT(".") << 39-(RecurseCount*3) <<
      MIL_TEXT("s%-19.18s%20.19s") << endl;
   Format = stream.str();

   /* Inquire the number of elements under this node. */
   MdigInquireFeature(MilDigitizer, M_SUBFEATURE_COUNT, Node, M_DEFAULT, &NodeCount);
   for(MIL_INT i=0; i<NodeCount; i++)
      {
      /* For each element under this node inquire it's string size. */
      MdigInquireFeature(MilDigitizer, M_SUBFEATURE_NAME+M_STRING_SIZE+i, Node, M_DEFAULT, &Length);
      if(Length)
         {
         MIL_INT64 Type = 0, AccessMode = 0, Visibility = 0;
         FeatureName = new MIL_TEXT_CHAR[Length];
         /* For each element under this node inquire it's string name. */
         MdigInquireFeature(MilDigitizer, M_SUBFEATURE_NAME+i, Node, M_DEFAULT, FeatureName);
         /* For each element under this node inquire it's type. */
         MdigInquireFeature(MilDigitizer, M_SUBFEATURE_TYPE+i, Node, M_DEFAULT, &Type);
         /* For each element under this node inquire it's read property. */
         MdigInquireFeature(MilDigitizer, M_FEATURE_ACCESS_MODE, FeatureName, M_DEFAULT, &AccessMode);
         /* For each element under this node inquire it's visibility property. */
         MdigInquireFeature(MilDigitizer, M_FEATURE_VISIBILITY, FeatureName, M_DEFAULT, &Visibility);

         /* Validate that the feature is actually implemented on this specific device. */
         if(M_FEATURE_IS_IMPLEMENTED(AccessMode))
            {
            /* Read the feature's value only if it's a value feature and is readable. */
            if((Type != M_TYPE_COMMAND && Type != M_TYPE_CATEGORY && Type != M_TYPE_REGISTER) && M_FEATURE_IS_READABLE(AccessMode))
               {
               MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, FeatureName, M_DEFAULT, &Length);
               FeatureValue = new MIL_TEXT_CHAR[Length];
               MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, FeatureName, M_DEFAULT, FeatureValue);
               }

            /* Features under a selector will appear twice. Filter out the double. */
            if(find(FeatureList.begin(), FeatureList.end(), FeatureName) == FeatureList.end())
               {
               /* Feature not in FeatureList, insert it at the end. */
               FeatureList.push_back(FeatureName);

               /* Inquire if this feature has child nodes. */
               MdigInquireFeature(MilDigitizer, M_SUBFEATURE_COUNT, FeatureName, M_DEFAULT, &SubNodeCount);

               /* Print the feature name if visibility level ok. */
               if(Visibility <= VISIBILITY_LEVEL)
                  MosPrintf(Format.c_str(), FeatureName, TypeToString(Type).c_str(), FeatureValue ? FeatureValue : MIL_TEXT(" "));

               /* If child nodes exist enumerate them. */
               if(SubNodeCount != 0)
                  EnumerateGenICamNodeTree(MilDigitizer, FeatureName, RecurseCount+1);
               }
            }

         /* Cleanup. */
         if(FeatureName)
            delete [] FeatureName;
         if(FeatureValue)
            delete [] FeatureValue;
         FeatureName = NULL;
         FeatureValue = NULL;
         }
      }
   }

/* Helper function used to convert a feature type into its string representation. */
milstring TypeToString(MIL_INT64 Type)
   {
   milstring sType;

   switch(Type)
      {
   case M_TYPE_INT64:
      sType = MIL_TEXT("M_TYPE_INT64");
      break;
   case M_TYPE_DOUBLE:
      sType = MIL_TEXT("M_TYPE_DOUBLE");
      break;
   case M_TYPE_BOOLEAN:
      sType = MIL_TEXT("M_TYPE_BOOLEAN");
      break;
   case M_TYPE_STRING:
      sType = MIL_TEXT("M_TYPE_STRING");
      break;
   case M_TYPE_ENUMERATION:
      sType = MIL_TEXT("M_TYPE_ENUMERATION");
      break;
   case M_TYPE_COMMAND:
      sType = MIL_TEXT("M_TYPE_COMMAND");
      break;
   case M_TYPE_REGISTER:
      sType = MIL_TEXT("M_TYPE_REGISTER");
      break;
   case M_TYPE_CATEGORY:
      sType = MIL_TEXT("M_TYPE_CATEGORY");
      break;
   default:
      sType = MIL_TEXT("M_NULL");
      break;
      }

   return sType;
   }

milstring VisibilityToString(MIL_INT64 Visibility)
   {
   milstring sVisibility;

   switch(Visibility)
      {
      case M_FEATURE_VISIBILITY_BEGINNER:
         sVisibility = MIL_TEXT("Beginner");
         break;
      case M_FEATURE_VISIBILITY_EXPERT:
         sVisibility = MIL_TEXT("Expert");
         break;
      case M_FEATURE_VISIBILITY_GURU:
         sVisibility = MIL_TEXT("Guru");
         break;
      case M_FEATURE_VISIBILITY_INVISIBLE:
         sVisibility = MIL_TEXT("Invisible");
         break;
      }

   return sVisibility;
   }

milstring RepresentationToString(MIL_INT64 Representation)
   {
   milstring sRepresentation;

   switch(Representation)
      {
      case M_FEATURE_REPRESENTATION_LINEAR:
         sRepresentation = MIL_TEXT("Linear");
         break;
      case M_FEATURE_REPRESENTATION_LOGARITHMIC:
         sRepresentation = MIL_TEXT("Logarithmic");
         break;
      case M_FEATURE_REPRESENTATION_BOOLEAN:
         sRepresentation = MIL_TEXT("Boolean");
         break;
      case M_FEATURE_REPRESENTATION_PURE_NUMBER:
         sRepresentation = MIL_TEXT("Pure number");
         break;
      case M_FEATURE_REPRESENTATION_HEX_NUMBER:
         sRepresentation = MIL_TEXT("Hex number");
         break;
      case M_FEATURE_REPRESENTATION_IPV4_ADDRESS:
         sRepresentation = MIL_TEXT("IPv4 address");
         break;
      case M_FEATURE_REPRESENTATION_MAC_ADDRESS:
         sRepresentation = MIL_TEXT("MAC address");
         break;
      }

   return sRepresentation;
   }