/*
* File name: FilterGraphWrapper.cpp
* Location: See Matrox Example Launcher in the MIL Control Center
* 
*/
#include "FilterGraphWrapper.h"

/////////////////////////////////////////////////////////////////
//
// Name      : SelectCaptureFilter
//
// Access    : public 
//
// Synopsis  : Lists the different filters from a category that are available on the system.
//             Waits for a user answer and return the selected filter's IBaseFilter interface.
//
// Parameter :
//             GUID CategoryList :
//             IBaseFilter * * ppFilter :
//             LPCWSTR * pSelectedName :
//
// Returns   : BOOL
//
// Comments  : The returned IBaseFilter interface must be released 
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
// All Rights Reserved
/////////////////////////////////////////////////////////////////
BOOL SelectFilterFromCategory(GUID CategorytoList, IBaseFilter** ppFilter, LPCWSTR pSelectedName)
   {
   BOOL bCaptureSelected = FALSE;
   HRESULT hr = E_FAIL;
   VARIANT varFiltername;
   int UserChoice = 0;

   (*ppFilter) = NULL;

   /* Loop until a valid choice has been made. */
   while (UserChoice == 0)
      {
      CComPtr<ICreateDevEnum> pDevEnum = NULL;
      CComPtr<IEnumMoniker> pEnumCapture = NULL;

      /* Create the System Device Enumerator. */
      hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
      if (SUCCEEDED(hr))
         {
         /* Create an enumerator for the category. */
         hr = pDevEnum->CreateClassEnumerator(CategorytoList, &pEnumCapture, 0);
         }
      pDevEnum = NULL;

      if (hr == S_OK)
         {
         char UserInput[32] = {0};
         int devCounter = 0;

         /* Walk the enumerator to list all filters available. */
         CComPtr<IMoniker> pFilterMoniker = NULL;
         while (pEnumCapture->Next(1, &pFilterMoniker, NULL) == S_OK)
            {
            devCounter++;
            CComPtr<IPropertyBag> pPropBag;
            HRESULT hr = pFilterMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
            if (SUCCEEDED(hr))
               {
               VariantInit(&varFiltername);

               /* Get filter friendly name. */
               hr = pPropBag->Read(L"FriendlyName", &varFiltername, 0);
               if (SUCCEEDED(hr))
                  {
                  MosPrintf(MIL_TEXT(" %d: %s\n"), devCounter, varFiltername.bstrVal);
                  VariantClear(&varFiltername);
                  }
               }
            pFilterMoniker = NULL;
            }

         MosPrintf(MIL_TEXT("\nEnter the number corresponding to the desired filter:\n"));
         MosPrintf(MIL_TEXT("Valid entries are from 1 to %d, (Q)uit: "), (devCounter));

         /* Get the user input to select the correct source filter. */
         scanf_s("%s", UserInput, (int)_countof(UserInput));
         sscanf_s(UserInput, "%d", &UserChoice);
         if (UserChoice < 1 || UserChoice >(int)devCounter)
            {
            char inputChar;
            sscanf_s(UserInput, "%c", &inputChar, 1);
            if (inputChar == 'q' ||
                  inputChar == 'Q')
               {
               UserChoice = -1;
               }
            else
               {
               MosPrintf(MIL_TEXT("Invalid selection. Select another filter.\n"));
               UserChoice = 0;
               }
            }
         else
            {
            bCaptureSelected = TRUE;
            }

         if (UserChoice > 0)
            {
            /* Walk enumerator to get the interface from the selected filter Moniker. */
            pEnumCapture->Reset();
            devCounter = 0;
            while (pEnumCapture->Next(1, &pFilterMoniker, NULL) == S_OK)
               {
               devCounter++;
               if (devCounter == UserChoice)
                  {
                  CComPtr<IPropertyBag> pPropBag;
                  HRESULT hr = pFilterMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
                  if (SUCCEEDED(hr))
                     {
                     VariantInit(&varFiltername);

                     /* Get filter friendly name. */
                     hr = pPropBag->Read(L"FriendlyName", &varFiltername, 0);
                     if (SUCCEEDED(hr))
                        {
                        MosPrintf(MIL_TEXT("The selected filter is: %s\n"), varFiltername.bstrVal);
                        size_t filterNameLen = MosStrlen(varFiltername.bstrVal) + 2;
                        MosSprintf(((wchar_t*)pSelectedName), filterNameLen, MIL_TEXT("%s"), varFiltername.bstrVal);
                        }
                     }
                  pFilterMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)ppFilter);
                  break;
                  }
               pFilterMoniker = NULL;
               }
            }
         VariantClear(&varFiltername);
         pEnumCapture = NULL;
         }
      else
         {
         UserChoice = -1;
         MosPrintf(MIL_TEXT("No filter available in this category.\n"));
         }
      }
      MosPrintf(MIL_TEXT("\n"));
   return bCaptureSelected;
   }

MIL_INT FilterFormCategoryCount(GUID CategorytoList)
   {
   HRESULT hr = E_FAIL;
   MIL_INT devCounter = 0;

   CComPtr<ICreateDevEnum> pDevEnum = NULL;
   CComPtr<IEnumMoniker> pEnumCapture = NULL;

   /* Create the System Device Enumerator. */
   hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
   if (SUCCEEDED(hr))
      {
      /* Create an enumerator for the category. */
      hr = pDevEnum->CreateClassEnumerator(CategorytoList, &pEnumCapture, 0);
      }
   pDevEnum = NULL;

   if (hr == S_OK)
      {
      /* Walk the enumerator to list all filters available. */
      CComPtr<IMoniker> pFilterMoniker = NULL;
      while (pEnumCapture->Next(1, &pFilterMoniker, NULL) == S_OK)
         {
         devCounter++;
         pFilterMoniker = NULL;
         }

      }
   return devCounter;
   }

FilterGraphWrapper::FilterGraphWrapper()
   {

   }

FilterGraphWrapper::~FilterGraphWrapper()
   {

   }

HRESULT FilterGraphWrapper::InitGraph(const CLSID RendererGUID/*=NULL*/)
   {
   HRESULT hr = E_FAIL;
   hr = CoInitialize(NULL);

   if (SUCCEEDED(hr))
      {
      /* Allocate a Graph Builder.*/
      hr = CoCreateInstance(CLSID_FilterGraph,
                            NULL,
                            CLSCTX_INPROC_SERVER,
                            IID_IGraphBuilder,
                            (void**)&m_pGraphBuilder);
      }
   else
      {
      MosPrintf(MIL_TEXT("\nERROR\n\nFailed to initialize Directshow Graph Builder\n"));
      }

   if (m_pGraphBuilder)
      {
#if REGISTER_GRAPH
      /* Register the Graph to allow remote connection. */
      AddToRot(m_pGraphBuilder, &m_dwGraphRegister);
#endif // REGISTER_GRAPH

      /* Gets Graph Media Control Interface.*/
      hr = m_pGraphBuilder->QueryInterface(IID_IMediaControl, (void**)&m_pMediaControl);
      if (FAILED(hr))
         {
         MosPrintf(MIL_TEXT("\nERROR\n\nFailed to initialize IMediaControl Interface\n"));
         }
      }

   if (SUCCEEDED(hr) && RendererGUID != CLSID_NULL)
      {
      CComPtr<IBaseFilter> pRendererfilter;
      /* Try allocating the desired Renderer. */
      hr = CoCreateInstance(RendererGUID,
                            NULL,
                            CLSCTX_INPROC_SERVER,
                            IID_IBaseFilter,
                            (void**)&pRendererfilter);

      if (SUCCEEDED(hr))
         {
         /* Adds filter to the graph. */
         m_pGraphBuilder->AddFilter(pRendererfilter, L"Renderer");
         }
      pRendererfilter = NULL;
      }

   if (SUCCEEDED(hr))
      {
      m_bGraphInitialized = true;
      }

   return hr;
   }

HRESULT FilterGraphWrapper::UninitGraph()
   {
#if REGISTER_GRAPH
   /* Unregister the graph. */
   RemoveFromRot(m_dwGraphRegister);
#endif // REGISTER_GRAPH

   /* Releasing the CComPtr to release the associated interfaces. */
   m_pMediaControl = NULL;
   m_pGraphBuilder = NULL;

   CoUninitialize();

   m_bGraphInitialized = false;
   return S_OK;
   }

HRESULT FilterGraphWrapper::InsertFilter(IBaseFilter* pFilter, LPCWSTR pFilterName, IPin** ppOutputPin)
   {
   HRESULT hr = E_FAIL;
   /* Adds the filter to the graph.*/
   if (pFilter != NULL)
      {
      hr = m_pGraphBuilder->AddFilter(pFilter, pFilterName);
      if (SUCCEEDED(hr) && ppOutputPin)
         hr = FindUnconnectedPin(pFilter, PINDIR_OUTPUT, ppOutputPin);
      }
   return hr;
   }

HRESULT FilterGraphWrapper::Connect(IPin* pOutputPin, IBaseFilter* pDownstreamFilter)
   {
   HRESULT hr = S_FALSE;
   if (pOutputPin && pDownstreamFilter)
      hr = ConnectFilters(m_pGraphBuilder, pOutputPin, pDownstreamFilter);
   return hr;
   }

HRESULT FilterGraphWrapper::Connect(IBaseFilter* pUpstreamFilter, IBaseFilter* pDownstreamFilter)
   {
   HRESULT hr = S_OK;
   CComPtr<IPin> pOutputPin = NULL;
   if (pUpstreamFilter != NULL)
      {
      hr = FindUnconnectedPin(pUpstreamFilter, PINDIR_OUTPUT, &pOutputPin);
      if (SUCCEEDED(hr) && pDownstreamFilter)
         {
         hr = Connect(pOutputPin, pDownstreamFilter);
         }
      }
   return hr;
   }

void FilterGraphWrapper::Render(IPin* pOutputPin)
   {
   if (pOutputPin)
      m_pGraphBuilder->Render(pOutputPin);
   }

void FilterGraphWrapper::Render(IBaseFilter* pFilter)
   {
   HRESULT hr = S_OK;
   CComPtr<IPin> pOutputPin = NULL;
   if (pFilter != NULL)
      {
      hr = FindUnconnectedPin(pFilter, PINDIR_OUTPUT, &pOutputPin);
      if (SUCCEEDED(hr) && pOutputPin)
         {
         Render(pOutputPin);
         }
      }
   }

HRESULT FilterGraphWrapper::StartGraph()
{
   HRESULT hr = S_OK;
   if (m_pMediaControl)
      hr = m_pMediaControl->Run();

   if (hr != S_OK)
      {
      OAFilterState filterGraphState = State_Stopped;
      hr = m_pMediaControl->GetState(5000, &filterGraphState);
      if (hr == VFW_S_STATE_INTERMEDIATE && filterGraphState == State_Running)
         {
         MosPrintf(MIL_TEXT("Some filters are still in an intermediate state.\n"));
         }
      else if(hr != S_OK || filterGraphState != State_Running)
         {
         MosPrintf(MIL_TEXT("ERROR\n\n"));
         MosPrintf(MIL_TEXT("Failed to start all the filter in the graph.\n"));
         }
      }
   return hr;
   }

HRESULT FilterGraphWrapper::StopGraph()
{
   HRESULT hr = S_OK;
   if (m_pMediaControl)
      hr = m_pMediaControl->Stop();
   return hr;
   }