// DatService.cpp : Implementation of cDatService
#include "stdafx.h"
#include "DecalDat.h"
#include "DatService.h"

#include "DatLibrary.h"

#include <ApiHook.h>

/////////////////////////////////////////////////////////////////////////////
// cDatService

HANDLE WINAPI CreateFileF( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
                          DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile )
{
   dwShareMode |= FILE_SHARE_READ | FILE_SHARE_WRITE;
   return cDatService::g_fn_CreateFile( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
      dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile );
}

static cHookDescriptor _hooks[] = {
   { eByName, _T( "kernel32.dll" ), _T( "CreateFileA" ), 0, reinterpret_cast< DWORD >( CreateFileF ), 0 },
};

cDatService::cFileFilter *cDatService::getFilter( LPTSTR szFilter, int nLength )
{
   std::string strFilter( szFilter, nLength );

   for( cFileFilterList::iterator i = m_filters.begin(); i != m_filters.end(); ++ i )
   {
      if( strFilter == i->m_strName )
		  return &(*(i));
   }

   return NULL;
}

HRESULT cDatService::createFilter( cDatService::cFileFilter *pFilter, REFIID iid, LPVOID *ppvItf )
{
   static _bstr_t _strFilter( _T( "FileFilters" ) );

   CComPtr< IDecalEnum > pEnumFilter;
   if( m_pDecal->get_Configuration( _strFilter, pFilter->m_clsid, &pEnumFilter ) != S_OK )
      return E_FAIL;

   return pEnumFilter->CreateInstance( iid, ppvItf );
}

HRESULT cDatService::onInitialize()
{
   USES_CONVERSION;

   static _bstr_t _strPortal( _T( "%ac%\\portal.dat" ) ),
      _strCell( _T( "%ac%\\cell.dat" ) );

   if( g_p == NULL )
   {
      hookFunctions( _hooks, 1, true );
      g_fn_CreateFile = reinterpret_cast< fn_CreateFile >( _hooks[ 0 ].m_pOldFunction );
      g_p = this;
   }

   CComObject< cDatLibrary > *pComCell, *pComPortal;
   CComObject< cDatLibrary >::CreateInstance( &pComCell );
   CComObject< cDatLibrary >::CreateInstance( &pComPortal );

   pComCell->AddRef();
   pComPortal->AddRef();

   m_pCell = pComCell;
   m_pPortal = pComPortal;

   m_pCell->load( this, _strCell, eCell, 256 );
   m_pPortal->load( this, _strPortal, ePortal, 1024 );

   // Load the list of filters
   static _bstr_t _strFilter( _T( "FileFilters" ) ),
      _strPrefix( _T( "Prefix" ) ),
      _strCache( _T( "Cache" ) );
   CComPtr< IDecalEnum > pEnum;

   m_pDecal->get_Configuration( _strFilter, GUID_NULL, &pEnum );

   while( pEnum->Next() == S_OK )
   {
      CComVariant vPrefix, vCache;
      cFileFilter;

      cFileFilter ff;
      pEnum->get_ComClass( &ff.m_clsid );

      HRESULT hRes = pEnum->get_Property( _strPrefix, &vPrefix );
      if( !SUCCEEDED( hRes ) || vPrefix.vt != VT_BSTR )
      {
         _ASSERT( FALSE );
         continue;
      }

      hRes = pEnum->get_Property( _strCache, &vCache );
      if( !SUCCEEDED( hRes ) || vCache.vt != VT_I4 )
      {
         _ASSERT( FALSE );
         continue;
      }

      ff.m_bCache = !!vCache.lVal;

      LPTSTR szPrefix = OLE2T( vPrefix.bstrVal );
      ::_tcslwr( szPrefix );
      ff.m_strName = szPrefix;

      m_filters.push_back( ff );
   }

   return S_OK;
}

void cDatService::onTerminate()
{
   m_cache.clear();
   m_filters.clear();

   if( g_p == this )
   {
      hookFunctions( _hooks, 1, false );
      g_p = NULL;
   }

   m_pCell->Release();
   m_pPortal->Release();

   m_pCell = NULL;
   m_pPortal = NULL;
}

cDatService *cDatService::g_p = NULL;
cDatService::fn_CreateFile cDatService::g_fn_CreateFile = NULL;

STDMETHODIMP cDatService::Lookup( BSTR strName, IUnknown **ppItf )
{
   static _bstr_t _strPortal( _T( "portal" ) ),
      _strCell( _T( "cell" ) );

   if( ::VarBstrCmp( strName, _strPortal, 0, NORM_IGNORECASE ) == VARCMP_EQ )
      return m_pPortal->QueryInterface( IID_IUnknown, reinterpret_cast< void ** >( ppItf ) );

   if( ::VarBstrCmp( strName, _strCell, 0, NORM_IGNORECASE ) == VARCMP_EQ )
      return m_pCell->QueryInterface( IID_IUnknown, reinterpret_cast< void ** >( ppItf ) );

   // None of the above
   return E_INVALIDARG;
}
