// DecalImpl.h
// Declaration of default implementations for Decal interfaces

#ifndef __DECALIMPL_H
#define __DECALIMPL_H

//C Library includes
#include <stdio.h>
#include <stdlib.h>

#include <Decal.h>
#import <msxml.dll>

#include "../Include/Helpers.h"

template< class ImplT >
class ATL_NO_VTABLE IDecalServiceImpl
: public IDecalService
{
public:
   CComPtr< IDecal > m_pDecal;

   HRESULT onInitialize()
   {
      return S_OK;
   }

   void onTerminate()
   {
   }

   STDMETHOD(Initialize)( IDecal *pDecal )
   {
      m_pDecal = pDecal;

      HRESULT hRes = static_cast< ImplT * >( this )->onInitialize();

      if( FAILED( hRes ) )
         m_pDecal.Release();

      return hRes;
   }

   STDMETHOD(Terminate)()
   {
      static_cast< ImplT * >( this )->onTerminate();
      m_pDecal.Release();

      return S_OK;
   }

   STDMETHOD(BeforePlugins)()
   {
      return S_OK;
   }

   STDMETHOD(AfterPlugins)()
   {
      return S_OK;
   }
};

class IDecalRenderImpl
: public IDecalRender
{
public:
   STDMETHOD(Render2D)()
   {
      return S_OK;
   }

   STDMETHOD(Render3D)()
   {
      return S_OK;
   }

   STDMETHOD(ChangeHWND)()
   {
      return S_OK;
   }

   STDMETHOD(ChangeDirectX)()
   {
      return S_OK;
   }
};

template< class ImplT, const CLSID *pClsid >
class IDecalFileSurrogateXMLImpl
: public IDecalFileSurrogate
{
public:
   static LPCTSTR getConfigGroup()
   {
      // This function must be overridden
      _ASSERT( FALSE );
      return _T( "Error: Bad config group" );
   }

   STDMETHOD(Register)(BSTR strFilename)
   {
      USES_CONVERSION;

      MSXML::IXMLDOMDocumentPtr pDoc;
      
      pDoc.CreateInstance( __uuidof( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER );

      if( !pDoc->load( strFilename ) )
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      MSXML::IXMLDOMElementPtr pRoot = pDoc->selectSingleNode( _T( "/*" ) );
      _variant_t vCLSID = pRoot->getAttribute( _T( "clsid" ) ),
         vProgID = pRoot->getAttribute( _T( "progid" ) ),
         vName = pRoot->getAttribute( _T( "name" ) );

      if( vCLSID.vt != VT_BSTR )
      {
         _ASSERT( FALSE );
         return E_FAIL;
      }

      // Convert the clsid
      CLSID clsid;
      HRESULT hRes = ::CLSIDFromString( vCLSID.bstrVal, &clsid );
      if( FAILED( hRes ) )
      {
         // Improperly formatted CLSID
         _ASSERT( FALSE );
         return hRes;
      }

      // Everything we need now, make the registry entries
      LPOLESTR strKey, strSurrogate, strUninstall;
      ::StringFromCLSID( clsid, &strKey );
      ::StringFromCLSID( *pClsid, &strSurrogate );
      ::StringFromCLSID( __uuidof( SurrogateRemove ), &strUninstall );

      LPTSTR szKey = OLE2T( strKey );

      RegKey rk;
      TCHAR szPluginKey[ 255 ];
      ::_stprintf( szPluginKey, _T( "Software\\Decal\\%s\\%s" ), static_cast< ImplT * >( this )->getConfigGroup(), szKey );

      // Open it up
      rk.Create( HKEY_LOCAL_MACHINE, szPluginKey );

      if( vName.vt == VT_BSTR )
         rk.SetStringValue (NULL, OLE2T( vName.bstrVal ));

      LPTSTR szSurrogate = OLE2T( strSurrogate );

	  rk.SetDWORDValue (_T("Enabled"), 1);
	  rk.SetStringValue (_T("Surrogate"), szSurrogate);

      rk.SetStringValue ( _T("Uninstall"), OLE2T( strUninstall ));
      rk.SetStringValue( _T("File"), OLE2T( strFilename ) );

      if( vProgID.vt == VT_BSTR )
      {
         LPTSTR szProgID = OLE2T( vProgID.bstrVal );
         RegKey rkProgID;
         rkProgID.Create( HKEY_CLASSES_ROOT, szProgID );

         rkProgID.SetKeyValue( _T( "CLSID" ), szKey );

         rk.SetStringValue( _T("ProgID"), szProgID );
      }

      ::CoTaskMemFree( strUninstall );
      ::CoTaskMemFree( strSurrogate );
      ::CoTaskMemFree( strKey );

      return S_OK;
   }
};

// Disp event dynamic is a hybrid that uses type info from a generated
// typelib (at runtime) to dispatch messages. Unlike IDispEventImpl which
// requires a registered typelib. Still, much code is copied from IDispEventImpl.
template< UINT nID, class T >
class ATL_NO_VTABLE IDispEventDynamicImpl
: public IDispEventSimpleImpl< nID, T, &GUID_NULL >
{
public:
   typedef IDispEventSimpleImpl< nID, T, &GUID_NULL > _base;

   CComPtr< ITypeInfo2 > m_pTI;

   HRESULT GetSourceInterfaceInfo( IUnknown *pUnk, ITypeInfo **ppTI )
   {
      // Get provide class info
      CComPtr< IProvideClassInfo > pPCI;
      HRESULT hRes = pUnk->QueryInterface( &pPCI );
      if( FAILED( hRes ) )
         // Does not support IProvideClassInfo, bad
         return hRes;

      CComPtr< ITypeInfo > pClassInfo;
      hRes = pPCI->GetClassInfo( &pClassInfo );

      if( FAILED( hRes ) )
         // Failed to return any class info
         return hRes;

      // Iterate through the Impl types to find the default source interface
      int nImplFlags;
      for( UINT i = 0; SUCCEEDED( pClassInfo->GetImplTypeFlags() ); ++ i )
      {
         if( nImplFlags == ( IMPLTYPEFLAG_FSOURCE | IMPLTYPEFLAG_FDEFAULT ) )
         {
            // Found it - locate our type info
            HREFTYPE href;
            hRes = pClassInfo->GetRefTypeOfImplType( i, &href );

            if( FAILED( hRes ) )
               // Could not access the ref type (referenced typelib not available?)
               return hRes;

            return pClassInfo->GetRefTypeInfo( href, ppTI );
         }
      }

      // Did not find a suitable interface
      return E_FAIL;
   }

   HRESULT GetSourceIID( ITypeInfo *pTI, IID *piid )
   {
      TYPEATTR *pTA;
      HRESULT hRes = pTI->GetTypeAttr( &pTA );

      if( FAILED( hRes ) )
         return hRes;

      *piid = pTA->guid;
      pTI->ReleaseTypeAttr( pTA );

      return S_OK;
   }

   HRESULT DispEventAdvise( IUnknown *pUnk )
   {
      CComPtr< ITypeInfo > pSourceInfo;
      HRESULT hRes = GetSourceInterfaceInfo( pUnk, &pSourceInfo );
      if( FAILED( hRes ) )
         return hRes;

      IID iid;
      hRes = GetSourceIID( pSourceInfo, &iid );
      if( FAILED( hRes ) )
         return hRes;

      hRes = _base::DispEventAdvise( pUnk, iid );
      if( SUCCEEDED( hRes ) )
         m_pTI = pSourceInfo;

      return hRes;
   }

   HRESULT DispEventUnadvise( IUnknown *pUnk )
   {
      IID iid;
      HRESULT hRes = GetSourceIID( pSourceInfo, &iid );
      _ASSERTE( SUCCEEDED( hRes ) );

      _base::DispEventUnadvise( pUnk, iid );
      m_pTI.Release();

      return S_OK;
   }

   // Functions copied from IDispEventImpl
	STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
	{
      if( pctinfo == NULL )
         return E_POINTER;

      *pctinfo = 1;
      return S_OK;
   }

	STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
	{
      if( itinfo > 0 )
         return E_INVALIDARG;

      if( pptinfo == NULL )
         return E_POINTER;

      return m_pTI->QueryIterface( pptinfo );
   }

	STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
		LCID, DISPID* rgdispid)
	{
      if( !::InlineIsEqualGUID( riid, IID_NULL ) )
         // By spec
         return DISP_E_UNKNOWNINTERFACE;

      return m_pTI->GetIDsOfNames( rgszNames, cNames, rgdispid );
   }

	// Helper for finding the function index for a DISPID
	HRESULT GetFuncInfoFromId(const IID& /*iid*/, DISPID dispidMember, LCID lcid, _ATL_FUNC_INFO& info)
	{
		FUNCDESC* pFuncDesc = NULL;
		UINT nIndex;
		HRESULT hr = m_pTI->GetFuncIndexOfMemId(dispidMember, INVOKE_FUNC, &nIndex);
		if (FAILED(hr))
			return hr;
		hr = m_pTI->GetFuncDesc(nIndex, &pFuncDesc);
		if (FAILED(hr))
			return hr;

		// If this assert occurs, then add a #define _ATL_MAX_VARTYPES nnnn
		// before including atlcom.h
		ATLASSERT(pFuncDesc->cParams <= _ATL_MAX_VARTYPES);
		if (pFuncDesc->cParams > _ATL_MAX_VARTYPES)
			return E_FAIL;

		for (int i=0; i<pFuncDesc->cParams; i++)
		{
			info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.vt;
			if (info.pVarTypes[i] == VT_PTR)
				info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.lptdesc->vt | VT_BYREF;
			if (info.pVarTypes[i] == VT_USERDEFINED)
				info.pVarTypes[i] = GetUserDefinedType(pFuncDesc->lprgelemdescParam[pFuncDesc->cParams-i-1].tdesc.hreftype);
		}

		VARTYPE vtReturn = pFuncDesc->elemdescFunc.tdesc.vt;
		switch(vtReturn)
		{
		case VT_INT:
			vtReturn = VT_I4;
			break;
		case VT_UINT:
			vtReturn = VT_UI4;
			break;
		case VT_VOID:
			vtReturn = VT_EMPTY; // this is how DispCallFunc() represents void
			break;
		case VT_HRESULT:
			vtReturn = VT_ERROR;
			break;
		}
		info.vtReturn = vtReturn;
		info.cc = pFuncDesc->callconv;
		info.nParams = pFuncDesc->cParams;
		m_pTI->ReleaseFuncDesc(pFuncDesc);

		return S_OK;
	}

	VARTYPE GetUserDefinedType(HREFTYPE hrt)
	{
		CComPtr<ITypeInfo> spTypeInfo;
		VARTYPE vt = VT_USERDEFINED;
		HRESULT hr = E_FAIL;
		hr = m_pTI->GetRefTypeInfo(hrt, &spTypeInfo);

		if(FAILED(hr))
			return vt;

		TYPEATTR *pta=NULL;

		m_pTI->GetTypeAttr(&pta);

		if(pta && pta->typekind == TKIND_ALIAS)
		{
			if (pta->tdescAlias.vt == VT_USERDEFINED)
				GetUserDefinedType(spTypeInfo,pta->tdescAlias.hreftype);
			else
				vt = pta->tdescAlias.vt;
		}
	
		if(pta)
			spTypeInfo->ReleaseTypeAttr(pta);

		return vt;
	}
};

#endif
