// Manager.cpp : Implementation of cManager
#include "stdafx.h"
#include <stdio.h>
#include "Inject.h"
#include "Manager.h"

#include "InjectApi.h"

#include "LayerSite.h"
#include "RootLayer.h"

#include "Canvas.h"

#include "IconCache.h"
#include "Image.h"
#include "FontCache.h"
#include "SolidImage.h"

#include "InputBuffer.h"

#include <stdio.h>


const IID EVTID_AcHooks = { 0xEB282FE5, 0x7170, 0x4a37, { 0xA2, 0x6E, 0x92, 0xAF, 0x36, 0x38, 0x5D, 0x2C } };
const IID LIBID_Decal = { 0xFF7F5F6D, 0x34E0, 0x4B6F, { 0xB3, 0xBB, 0x81, 0x41, 0xDE, 0x2E, 0xF7, 0x32 } };
const IID IID_Plugins = { 0x702D3901, 0xC13A, 0x448e, { 0x88, 0x71, 0xEC, 0xDC, 0x8B, 0xC8, 0xD0, 0x79 } };

/////////////////////////////////////////////////////////////////////////////
// cManager

extern HANDLE _hUpdateEnabled;
CComObject< cManager > *cManager::_p = NULL;

static TCHAR szPathBuffer[ MAX_PATH ];

cManager::cManager()
   : m_nNextPlugin( 1000 ),
   m_pRootSite( NULL ),
   m_pMouseOver( NULL ),
   m_pMouseDown( NULL ),
   m_pKeyboard( NULL ),
   m_portal( ::InjectMapPath( eInjectPathDatFile, _T( "portal.dat" ), szPathBuffer ), 1024 ),
   m_hMain( NULL ),
   m_pfnOld( NULL ),
   m_pD( NULL ),
   m_p3D( NULL ),
   m_p3DDevice( NULL ),
   m_pPrimarySurface( NULL ),
   m_lpSurface( NULL ),
   m_bInitialized( false ),
   m_lPrevSelKey( NULL ),
   m_bContainer( false ),
   m_bXMLViewViewer( false ),
   m_hDecalDLL( NULL ),
   m_eAlphaBlendMode( eAlphaBlendSoftware ),
   m_bSoftware( false )
{
   // Start the decal object
   ::CoInitialize( NULL );
   HRESULT hRes = ::CoCreateInstance( __uuidof( Decal ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IDecal ),
      reinterpret_cast< void ** >( &m_pDecal ) );

   // Load the Decal.dll lib to keep CoFreeAllLibraries from destroying it
   m_hDecalDLL = ::LoadLibrary ( _T( "Decal.dll" ) );

   if( SUCCEEDED( hRes ) )
      hRes = m_pDecal->StartServices( );
}

cManager::~cManager()
{
	if( m_pDecal.p )
	{
		m_pDecal->StopServices( );
		m_pDecal.Release( );
	}

	_ASSERTE ( ::GetWindowLong ( m_hMain, GWL_WNDPROC ) == reinterpret_cast< LONG > ( wndProc ) );
	::SetWindowLong( m_hMain, GWL_WNDPROC, reinterpret_cast< LONG >( m_pfnOld ) );
	m_pfnOld = NULL;

	FreeLibrary( m_hDecalDLL );

	if( m_bContainer )
		::CoUninitialize( );

   _p = NULL;
}

void cManager::setDirectDraw( IDirectDraw4 *pDD, IDirect3D3 *pD3D, IDirectDrawSurface4 *pDDS)
{
	if( pDD != NULL )
		m_pD = pDD;
   
	m_p3D = pD3D;

	m_p3DDevice = NULL;
   
	m_pPrimarySurface = pDDS;

	setWindow( NULL );
}

void cManager::setSurface( IDirect3DDevice3 *pDevice )
{
   m_p3DDevice = pDevice;

   m_pDecal->InitGraphics ( m_pD, pDevice );

   // Cascade the reformat
   if( m_pRootSite != NULL )
      m_pRootSite->reformat();
}

struct cManagerSinkCapTest
{
   eManagerSinkCaps m_mask;
   const IID *m_iid;
};

void (*pfnOldChatMessage)(char*, DWORD) = NULL;
void (*pfnOldChatText)(void) = NULL;

void cManager::convertVersion( LPTSTR szVersion, DWORD &dwVersionMajor, DWORD &dwVersionMinor )
{
   int wVersionParts[ 4 ],
      *i_ver = wVersionParts;

   for( TCHAR *szVersionPart = ::_tcstok( szVersion, _T( "." ) ); szVersionPart != NULL; szVersionPart = ::_tcstok( NULL, _T( "." ) ), ++ i_ver )
      ::_stscanf( szVersionPart, _T( "%i" ), i_ver );

   dwVersionMajor = MAKELONG( wVersionParts[ 1 ], wVersionParts[ 0 ] );
   dwVersionMinor = MAKELONG( wVersionParts[ 3 ], wVersionParts[ 2 ] );

}

void cManager::init()
{
   USES_CONVERSION;

   // Obtain ACHooks Ref & start ACHooks events if we're not in Checksum's Container
   if(!m_bContainer)
   {
		m_pDecal->get_Hooks( &m_pHooks );
		IACHooksEventsImpl<ChatMessageHook, cManager>::advise( m_pHooks );
		IACHooksEventsImpl<ChatTextHook, cManager>::advise( m_pHooks );
   }

	// Prepare the root layer and bar manager before loading plugins
	// We have to manually create the root site
	CComObject< cLayerSite > *pRootSite;
	CComObject< cLayerSite >::CreateInstance( &pRootSite );

	m_pRootSite = pRootSite;

	// Create the paired root layer
	CComObject< cRootLayer > *pRootLayer;
	CComObject< cRootLayer >::CreateInstance( &pRootLayer );
	m_pRootLayer = pRootLayer;

	// To circular reference keeps it all intact
	LayerParams lp = { 0, { 0, 0, 0, 0 }, eRenderClipped };
	m_pRootSite->create( &lp, NULL, pRootLayer );

   m_bInitialized = true;

   // Set the blending Mode from the Registry
   {
	   RegKey key;
	   key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal" ) );

	   if(key.QueryDWORDValue(_T("AlphaBlendMode"), reinterpret_cast<unsigned long &>(m_eAlphaBlendMode))!=ERROR_SUCCESS)
		   m_eAlphaBlendMode = eAlphaBlendSoftware;
   }

   // Install the input filter
   cInputBuffer::init();

   long Val=NULL;

   // Ok, enumerate all of the plugins and load them
   ::ReleaseMutex( _hUpdateEnabled );
}

void cManager::term()
{
   // Remove the object from the ROT
   ::WaitForSingleObject( _hUpdateEnabled, INFINITE );

   CComPtr< IRunningObjectTable > pROT;
   if( SUCCEEDED( ::GetRunningObjectTable( 0, &pROT ) ) )
      pROT->Revoke( m_dwROT );

   if( m_pRootSite != NULL )
   {
      m_pRootSite->Destroy( );
	  clearDestroyList( );
	  m_pRootSite = NULL;
   }

   // Terminate the input buffer
   cInputBuffer::term( );
   
   m_bInitialized = false;

   // Release the ACHooks interface and event connection points
   if( !m_bContainer )
   {
		IACHooksEventsImpl<ChatMessageHook, cManager>::unadvise( m_pHooks );
		IACHooksEventsImpl<ChatTextHook, cManager>::unadvise( m_pHooks );
		m_pHooks.Release();
   }

   // Now Destroy the window structure (NOTE: This reference is weak -
   // so the destroy method should free memory)
/*   if( m_pRootSite != NULL )
   {
      m_pRootSite->Destroy();
	  clearDestroyList();
	  m_pRootSite = NULL;
   }
*/
   // Make sure the captures are cleared
   _ASSERTE( m_pMouseOver == NULL );
   _ASSERTE( m_pMouseDown == NULL );
   _ASSERTE( m_pKeyboard == NULL );

   ::ReleaseMutex( _hUpdateEnabled );
}

void cManager::clearDestroyList()
{
   {
      for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++ i )
      {
         if( i->m_dwSinkCaps & eManagerSinkCapWindowMessage )
         {
			// I can't figure out how to stop this from AVing. Spent way too much time on it already.
			try
			{
				CComPtr< IWindowsMessageSink > pSink;
				i->m_pPlugin->QueryInterface( &pSink );
				pSink->WindowMessageEnd();
			}
			catch( ... )
			{
			}
	     }
      }
   }

   {
      for( cLayerList::iterator i = m_destroy.begin(); i != m_destroy.end(); i = m_destroy.erase( i ) )
      {
         // Do internal destruction
         ( *i )->destroy();
         ( *i )->Release();
      }
   }
}

void cManager::loadPlugin( IUnknown *pUnkPlugin )
{
	cPlugin p;
	p.m_pPlugin = pUnkPlugin;
	p.m_dwSinkCaps = 0;

	// Test for sink caps
	static cManagerSinkCapTest _capstest[] = {
		{ eManagerSinkCapWindowMessage, &IID_IWindowsMessageSink },
		{ eManagerSinkCapPlugin, &IID_IPluginSink},
		{ eManagerSinkCapRender, &IID_IRenderSink },
		{ eManagerSinkCapRender3D, &IID_IRender3DSink },
	};

	static cManagerSinkCapTest *_end_caps = _capstest + ( sizeof( _capstest ) / sizeof( cManagerSinkCapTest ) );

	for( cManagerSinkCapTest *i = _capstest; i != _end_caps; ++ i )
	{
		CComPtr< IUnknown > pUnk;
		if( SUCCEEDED( pUnkPlugin->QueryInterface( *( i->m_iid ), reinterpret_cast< void ** >( &pUnk ) ) ) )
			p.m_dwSinkCaps |= i->m_mask;
	}

	// Don't bother adding plugins without any sink interfaces
	if ( p.m_dwSinkCaps != 0 )
		m_plugins.push_back( p );
}

void cManager::unloadPlugins ()
{
   m_plugins.clear ();
}

void cManager::setWindow( HWND hWnd )
{
   // ::MessageBox( NULL, _T( "cManager::setWindow" ), _T( "Inject.dll" ), MB_OK );

   // Subclass the window
   if( hWnd == m_hMain || hWnd == NULL )
      // Do not subclass the window twice
      return;

   if( m_hMain != NULL )
   {
      // Unhook the window proc
      _ASSERTE ( ::GetWindowLong ( m_hMain, GWL_WNDPROC ) == reinterpret_cast< LONG > ( wndProc ) );
      ::SetWindowLong( m_hMain, GWL_WNDPROC, reinterpret_cast< LONG >( m_pfnOld ) );
      m_pfnOld = NULL;
   }

   m_hMain = hWnd;

   if( m_hMain != NULL )
      // Hook the window proc
      m_pfnOld = reinterpret_cast< WNDPROC >( ::SetWindowLong( hWnd, GWL_WNDPROC, reinterpret_cast< LONG >( wndProc ) ) );

   m_pDecal->put_HWND ( reinterpret_cast< long > ( hWnd ) );
}

void cManager::draw2D( )
{
   m_pDecal->Render2D( );

   if( m_pRootSite != NULL )
   {
      _ASSERTMEM( _CrtCheckMemory( ) );
      m_pRootSite->format( );
      m_pRootSite->prerender( );

      _ASSERTMEM( _CrtCheckMemory( ) );

      CComPtr< ICanvas > pCanvas;
      if (SUCCEEDED (GetPrimarySurface( &pCanvas )))
	  {
		m_pRootSite->render( pCanvas );
		
		for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++ i )
		{
			// Reset the pCanvas alpha
			pCanvas->put_Alpha( 255 );

			if( i->m_dwSinkCaps & eManagerSinkCapRender )
			{
				CComPtr< IRenderSink > pSink;
				i->m_pPlugin->QueryInterface( &pSink );
				pSink->CustomDraw( pCanvas );
			}
		}
	  }

      _ASSERTMEM( _CrtCheckMemory( ) );
   }
}

void cManager::draw3D()
{
	if( m_pRootSite != NULL )
	{
	}
}

void cManager::sendPreBeginScene()
{
	for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++i )
	{
		if( i->m_dwSinkCaps & eManagerSinkCapRender3D )
		{
			CComPtr< IRender3DSink > pSink;
			i->m_pPlugin->QueryInterface( &pSink );
			pSink->PreBeginScene( m_p3DDevice );
		}
	}
}

void cManager::sendPostBeginScene()
{
	for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++i )
	{
		if( i->m_dwSinkCaps & eManagerSinkCapRender3D )
		{
			CComPtr< IRender3DSink > pSink;
			i->m_pPlugin->QueryInterface( &pSink );
			pSink->PostBeginScene( m_p3DDevice );
		}
	}
}

void cManager::sendPreEndScene()
{
	for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++i )
	{
		if( i->m_dwSinkCaps & eManagerSinkCapRender3D )
		{
			CComPtr< IRender3DSink > pSink;
			i->m_pPlugin->QueryInterface( &pSink );
			pSink->PreEndScene( m_p3DDevice );
		}
	}
}

void cManager::sendPostEndScene()
{
	for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++i )
	{
		if( i->m_dwSinkCaps & eManagerSinkCapRender3D )
		{
			CComPtr< IRender3DSink > pSink;
			i->m_pPlugin->QueryInterface( &pSink );
			pSink->PostEndScene( m_p3DDevice );
		}
	}
}

void cManager::removeImage( cImage *pImage )
{
   for( cImagesList::iterator i = m_images.begin(); i != m_images.end(); ++ i )
   {
      if( *i == pImage )
      {
         m_images.erase( i );
         return;
      }
   }

   // Not found - boo!
   _ASSERTE( FALSE );
}

void cManager::removeFont( cFontCache *pFont )
{
   for( cFontsList::iterator i = m_fonts.begin(); i != m_fonts.end(); ++ i )
   {
      if( *i == pFont )
      {
         m_fonts.erase( i );
         return;
      }
   }

   // Not found - boo!
   _ASSERTE( FALSE );
}

void cManager::enableSoftwareMode( )
{
	m_bSoftware = true;
}

LRESULT cManager::localWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
/*	if((uMsg==WM_NCACTIVATE))/* || (uMsg==WM_ACTIVATE) || (uMsg==WM_ACTIVATEAPP) || (uMsg==WM_KILLFOCUS))
		return 0;*/

   // When destory comes through, shut down the decal object
   if ( uMsg == WM_DESTROY )
   {
      m_pDecal->StopServices ();
      m_pDecal.Release ();

      return ::CallWindowProc( m_pfnOld, m_hMain, uMsg, wParam, lParam );
   }

   if( ( m_pRootSite == NULL ) || ( ( m_p3DDevice == NULL ) && ( !m_bContainer ) && ( !m_bSoftware ) ) )
      // Quick return when we aren't initialized
      return ::CallWindowProc( m_pfnOld, m_hMain, uMsg, wParam, lParam );

   // Forwards messages to the sinks accepting the windows messages
   for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++ i )
   {
      if( i->m_dwSinkCaps & eManagerSinkCapWindowMessage )
      {
         CComPtr< IWindowsMessageSink > pSink;
         if( i->m_pPlugin->QueryInterface( &pSink ) == S_OK )
		 {
			 VARIANT_BOOL bEat = VARIANT_FALSE;
			pSink->WindowMessage( reinterpret_cast< long >( hWnd ), uMsg, wParam, lParam, &bEat );

			if( bEat )
				// This message was eaten stop processing
				return 0;
		}
      }
   }

   // Return if there is no 3D Device
   if( m_p3DDevice == NULL && !m_bContainer )
	   return ::CallWindowProc( m_pfnOld, m_hMain, uMsg, wParam, lParam );

   if( uMsg >= WM_MOUSEFIRST && uMsg < WM_MOUSELAST )
   {
      // Process enter/exit messages
      POINTS ptm = MAKEPOINTS( lParam );

	  if( !m_bContainer )
	  {
		D3DCLIPSTATUS cs;
		m_p3DDevice->GetClipStatus( &cs );

		ptm.y -= ( 28 - static_cast< short >( cs.miny ) );
	  }

      POINT ptLocal;
      CComPtr< ICanvas > pPrimary;
	  cLayerSite *pHit = NULL;
      if (SUCCEEDED (GetPrimarySurface( &pPrimary )))
	  {
			pHit = m_pRootSite->hitTest( ptm, &ptLocal, pPrimary );

		  if( m_pMouseDown != NULL || pHit != NULL )
		  {
			 if( m_pMouseDown != NULL )
				m_pMouseDown->sendMouseEvent( uMsg, wParam, lParam );
			 else
				pHit->sendMouseEvent( uMsg, wParam, lParam );
		  }
	  }

      // Ok - fill out the structure with our fancy info
      MouseState ms = { ( pHit == NULL ) ? NULL : pHit->m_pSink, { ptm.x, ptm.y }, { ( pHit == NULL ) ? 0 : ptLocal.x, ( pHit == NULL ) ? 0 : ptLocal.y }, 
         ( ::GetAsyncKeyState( VK_CONTROL ) & 0x80000000 ) ? VARIANT_TRUE : VARIANT_FALSE,
         ( ::GetAsyncKeyState( VK_SHIFT ) & 0x80000000 ) ? VARIANT_TRUE : VARIANT_FALSE };

      bool bEat = false;

      // First we check for button up messages which end capture
      if( uMsg == WM_LBUTTONUP )
      {
         if( m_pMouseDown != NULL )
         {
            // Send the mouse up message and end capture
            m_pMouseDown->sendMouseUp( &ms );

            // Reset the mouse in, so enter/exit messages get sent properly

            // First, check if enter/exit messages are 
            if( m_pMouseDown->isClippedChild( pHit ) )
               m_pMouseOver = m_pMouseDown;
            else
            {
               // We have not hit a child of the mouse down, reset the mouse over
               // to our parent (which has not received the mouse out message)
               if( m_pMouseDown->m_params.render & eRenderClipped )
                  m_pMouseOver = m_pMouseDown->m_pParent;
               else
                  // If the item is unclipped, then we must send all enter messages
                  m_pMouseOver = NULL;
            }

            bEat = true;
            m_pMouseDown = NULL;
         }
      }

      // Next, send enter/exit messages
      if( pHit != m_pMouseOver )
      {
         if( m_pMouseDown != NULL )
         {
            // There is a capture, send enter/exit messages only to the one with capture
            bool bPreviousHit = m_pMouseDown->isClippedChild( m_pMouseOver );
            if( bPreviousHit != m_pMouseDown->isClippedChild( pHit ) )
            {
               if( bPreviousHit )
                  m_pMouseDown->sendMouseExit( m_pMouseDown->m_pParent, &ms );
               else
                  m_pMouseDown->sendMouseEnter( m_pMouseDown->m_pParent, &ms );
            }
         }
         else
         {
            // Find the common parent
            cLayerSite *pCommonParent;
            if( pHit == NULL || m_pMouseOver == NULL )
               pCommonParent = NULL;
            else
               pCommonParent = m_pMouseOver->getCommonClippedParent( pHit );

            if( m_pMouseOver != NULL )
               m_pMouseOver->sendMouseExit( pCommonParent, &ms );

            if( pHit != NULL )
               pHit->sendMouseEnter( pCommonParent, &ms );
         }

         m_pMouseOver = pHit;
      }

      // Next, send movement messages
      if( m_pMouseDown != NULL )
         m_pMouseDown->sendMouseMove( &ms );
      else if( m_pMouseOver != NULL )
         m_pMouseOver->sendMouseMove( &ms );

      // Last, send mouse capture messages
      if( uMsg == WM_LBUTTONDOWN )
      {
         if( m_pMouseOver != NULL )
         {
            m_pMouseDown = m_pMouseOver;
            m_pMouseDown->sendMouseDown( &ms );

            bEat = true;
         }

         // Check for popups that require closing
         for( cLayerList::iterator i = m_popups.begin(); i != m_popups.end(); )
         {
            if( !( *i )->isChild( m_pMouseOver ) && !( *i )->sendPopupCancel( &ms ) )
            {
               i = m_popups.erase( i );
               continue;
            }

            ++ i;
         }

         // Check if we should be cancelling a keyboard capture
         if( m_pKeyboard != NULL )
         {
            if( !m_pKeyboard->isClippedChild( m_pMouseDown ) )
            {
               // The user is clicking on something other than the keyboard capture,
               // so end capture
               m_pKeyboard->sendKeyboardEndCapture( VARIANT_TRUE );
               m_pKeyboard = NULL;
            }
         }
      }

      if( uMsg == WM_LBUTTONDBLCLK )
      {
         if( m_pMouseOver != NULL )
         {
            m_pMouseOver->sendMouseDblClk( &ms );
            bEat = true;
         }
      }

      if( bEat )
      {
         clearDestroyList();
         return 0;
      }
   }

   if( uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST )
   {
      // We ahve a keyboard message that might be filtered
      if(( m_pKeyboard != NULL ) && (uMsg != WM_KEYUP))
      {
         m_pKeyboard->sendKeyboardEvent( uMsg, wParam, lParam );

         // Check if the message is clearing 
         if( uMsg == WM_CHAR )
         {
			if( wParam == VK_RETURN )
            {
               // End with success
               // cyn, 15/10/2002
               // Clear m_pKeyboard before triggering KeyboardEndCapture,
               // This allows KeyboardEndCapture to re-capture the keyboard
               // Edit will only allow recapture if VARIANT_FALSE is passed
               // I believe Edit is the only control in DecalControls.* that captures the keyboard
               cLayerSite *Keyboard = m_pKeyboard;
               m_pKeyboard = NULL;
               Keyboard->sendKeyboardEndCapture( VARIANT_FALSE );
               Keyboard = NULL; // I don't know if it does some smart pointer reference counting or whatever.. can't hurt

               clearDestroyList();
               return 0;
            }
            else if( wParam == VK_ESCAPE || wParam == VK_TAB )
            {
               // End with cancel
               m_pKeyboard->sendKeyboardEndCapture( VARIANT_TRUE );
               m_pKeyboard = NULL;

               clearDestroyList();
               return 0;
            }

            // Send the key message
            KeyState ks = { wParam,
               ( ::GetAsyncKeyState( VK_CONTROL ) & 0x80000000 ) ? VARIANT_TRUE : VARIANT_FALSE,
               ( ::GetAsyncKeyState( VK_SHIFT ) & 0x80000000 ) ? VARIANT_TRUE : VARIANT_FALSE };

            m_pKeyboard->sendKeyboardChar( &ks );
         }

         // Eat all other keyboard messages when there's a capture
         clearDestroyList();
         return 0;
      }

      // Carry on, nothing special here
   }

   if( uMsg == WM_ACTIVATEAPP && !wParam )
   {
      // The application is being deactivated - clear all mouse and keyboard captures
      MouseState ms = { NULL, { 0, 0 }, { 0, 0 }, VARIANT_FALSE, VARIANT_FALSE };

      if( m_pMouseDown )
      {
         cLayerSite *pMouseDownParent = ( m_pMouseDown->m_params.render & eRenderClipped ) ?
            m_pMouseDown->m_pParent : NULL;

         // Do a mouse exit from the captured control
         if( m_pMouseDown->isClippedChild( m_pMouseOver ) )
         {
            // First exit the mouse down layer
            m_pMouseDown->sendMouseExit( pMouseDownParent, &ms );
            m_pMouseOver = pMouseDownParent;
         }
         else
            m_pMouseOver = pMouseDownParent;

         // Send the mouse up message
         m_pMouseDown->sendMouseUp( &ms );
         m_pMouseDown = NULL;
      }

      // Do an exit for any remaining mouse overs
      if( m_pMouseOver != NULL )
      {
         m_pMouseOver->sendMouseExit( NULL, &ms );
         m_pMouseOver = NULL;
      }

      // Attempt to close all popups
      for( cLayerList::iterator i = m_popups.begin(); i != m_popups.end(); )
      {
         if( !( *i )->sendPopupCancel( &ms ) )
            i = m_popups.erase( i );
         else
            ++ i;
      }

      if( m_pKeyboard )
      {
         m_pKeyboard->sendKeyboardEndCapture( VARIANT_TRUE );
         m_pKeyboard = NULL;
      }
   }

   clearDestroyList( );
   
   return ::CallWindowProc( m_pfnOld, m_hMain, uMsg, wParam, lParam );
}

LRESULT cManager::wndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
   _ASSERTE ( cManager::_p != NULL );
   return cManager::_p->localWndProc( hWnd, uMsg, wParam, lParam );
}

void cManager::createSurface( LPSIZE psz, IDirectDrawSurface4 **ppSurf )
{
   DDSURFACEDESC2 ddsd;

   ::memset( &ddsd, 0, sizeof( DDSURFACEDESC2 ) );
   ddsd.dwSize = sizeof( DDSURFACEDESC2 );

   ddsd.ddpfPixelFormat.dwSize = sizeof( DDPIXELFORMAT );

   // Set the pixel format to the same as the primary surface
   CComPtr< ICanvas > pCanvas;
   if (SUCCEEDED (GetPrimarySurface( &pCanvas )))
   {
	   CComPtr< IDirectDrawSurface4 > pPrimary;


	   pCanvas->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pPrimary ) );

	   pPrimary->GetPixelFormat( &ddsd.ddpfPixelFormat );

	   // Now make a drawing surface of the same size as our client area
	   ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
	   ddsd.dwWidth = psz->cx;
	   ddsd.dwHeight = psz->cy;
	   ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

	   HRESULT hRes = m_pD->CreateSurface( &ddsd, ppSurf, NULL );
	   _ASSERTE( SUCCEEDED( hRes ) );

	   DDCOLORKEY ddc = { 0x07FF, 0x07FF };
	   ( *ppSurf )->SetColorKey( DDCKEY_SRCBLT, &ddc );
  }

   _ASSERTMEM( _CrtCheckMemory( ) );
}

STDMETHODIMP cManager::UnloadPlugin(long nID)
{
	return E_NOTIMPL;
}

STDMETHODIMP cManager::GetDirectDraw(REFIID iid, void **ppvItf)
{
   _ASSERTE( ppvItf != NULL );

   return m_pD->QueryInterface( iid, ppvItf );
}

STDMETHODIMP cManager::GetPrimarySurface(ICanvas **ppCanvas)
{
   _ASSERTE( ppCanvas != NULL );
//   _ASSERTE( m_p3DDevice != NULL );

   if ( m_bContainer )
   {
	   // Create a canvas object
	   CComObject< cCanvas > *pPrimCanvas;
	   CComObject< cCanvas >::CreateInstance( &pPrimCanvas );

	   GetScreenSize( &pPrimCanvas->m_sz );
	   pPrimCanvas->m_pSurface = m_pPrimarySurface;

	   RECT rc;

	   GetWindowRect(m_hMain, &rc);

	   ClipParams cClip = { { static_cast< long >( 0 ), static_cast< long >( 0 ), static_cast< long >( rc.right - rc.left ), static_cast< long >( rc.bottom - rc.top ) }, { 0, 0 }, VARIANT_TRUE };
	   pPrimCanvas->m_clipping.push( cClip );

	   pPrimCanvas->AddRef( );
	   *ppCanvas = pPrimCanvas;
   }
   else
   {
	   CComPtr< IDirectDrawSurface4 > pPrimary;
	   
	   HRESULT hRes = m_p3DDevice->GetRenderTarget( &pPrimary );
	   if( FAILED( hRes ) )
		   return E_FAIL;

	   // Create a canvas object
	   CComObject< cCanvas > *pPrimCanvas;
	   CComObject< cCanvas >::CreateInstance( &pPrimCanvas );

	   GetScreenSize( &pPrimCanvas->m_sz );
	   pPrimCanvas->m_pSurface = pPrimary;

	   D3DCLIPSTATUS cs;
	   m_p3DDevice->GetClipStatus( &cs );

	   // Set up the clipping parameters (depends on the 3d clipping area)
	   ClipParams cClip = { { static_cast< long >( cs.minx ), static_cast< long >( cs.miny ), static_cast< long >( cs.maxx ), static_cast< long >( cs.maxy ) },
		  { 0, 0 }, VARIANT_TRUE };

	   pPrimCanvas->m_clipping.push( cClip );

	   pPrimCanvas->AddRef();
	   *ppCanvas = pPrimCanvas;
   }
   return S_OK;
}

STDMETHODIMP cManager::Get3DDevice(REFIID iid, void **ppvItf)
{
   _ASSERTE( ppvItf != NULL );

   return m_p3DDevice->QueryInterface( iid, ppvItf );
}

STDMETHODIMP cManager::LoadBitmapFile(BSTR strFilename, IImageCache **ppImage)
{
   _ASSERTE( strFilename != NULL );
   _ASSERTE( ppImage != NULL );

   USES_CONVERSION;

   LPCTSTR szFilename = OLE2T( strFilename );

   TCHAR szPath[ MAX_PATH ];
   ::InjectMapPath( eInjectPathAgent, szFilename, szPath );

   // First search the image cache for a matching file
   for( cImagesList::iterator i = m_images.begin(); i != m_images.end(); ++ i )
   {
      if( ( *i )->m_source == cImage::eFile && ::_tcsicmp( ( *i )->m_szFilename, szPath ) == 0 )
      {
         // Here's the match - return the existing image with another reference
         ( *i )->AddRef();
         *ppImage = *i;

         return S_OK;
      }
   }

   // The image wasn't found, so make a new entry
   CComObject< cImage > *pNewImage;
   CComObject< cImage >::CreateInstance( &pNewImage );

   pNewImage->m_source = cImage::eFile;
   ::_tcscpy( pNewImage->m_szFilename, szPath );

   m_images.push_back( pNewImage );

   pNewImage->AddRef();

   *ppImage = pNewImage;
   return S_OK;
}

STDMETHODIMP cManager::GetIconCache(LPSIZE psz, IIconCache **ppCache)
{
   _ASSERTE( psz != NULL );
   _ASSERTE( psz->cx > 0 && psz->cy > 0 );
   _ASSERTE( ppCache != NULL );

   for( cIconsList::iterator i = m_icons.begin(); i != m_icons.end(); ++ i )
   {
      if( ( *i )->m_szIcon.cx == psz->cx && ( *i )->m_szIcon.cy == psz->cy )
      {
         *ppCache = ( *i );
         ( *i )->AddRef();

         return S_OK;
      }
   }

   // Not found, so create a new cache
   CComObject< cIconCache > *pNewCache;
   CComObject< cIconCache >::CreateInstance( &pNewCache );

   pNewCache->m_szIcon = *psz;
   pNewCache->m_nEdge = 8;
   pNewCache->AddRef();

   m_icons.push_back( pNewCache );

   *ppCache = pNewCache;

   // Make the icon cache actually cache
   pNewCache->AddRef();

	return S_OK;
}

STDMETHODIMP cManager::LoadBitmapPortal(long nFile, IImageCache **ppImage)
{
   _ASSERTE( ppImage != NULL );

   DWORD dwFile = *reinterpret_cast< DWORD* >( &nFile );
   for( cImagesList::iterator i = m_images.begin(); i != m_images.end(); ++ i )
   {
      if( ( *i )->m_source == cImage::ePortal && ( *i )->m_dwID == dwFile )
         return ( *i )->QueryInterface( IID_IImageCache, reinterpret_cast< void ** >( ppImage ) );
   }
   
   // The image wasn't found, so make a new entry
   CComObject< cImage > *pNewImage;
   CComObject< cImage >::CreateInstance( &pNewImage );

   pNewImage->m_source = cImage::ePortal;
   pNewImage->m_dwID = dwFile;
   pNewImage->AddRef();

   m_images.push_back( pNewImage );

   *ppImage = pNewImage;
   return S_OK;
}

STDMETHODIMP cManager::CreateFont(BSTR strFaceName, long nHeight, long nFlags, IFontCache **ppFont)
{
   _ASSERTE( strFaceName != NULL );
   _ASSERTE( ppFont != NULL );
   _ASSERTE( nHeight > 0 );

   USES_CONVERSION;

   LPCTSTR szFaceName = OLE2T( strFaceName );

   // Iterate through existing foonts to see if there's a match
   for( cFontsList::iterator i = m_fonts.begin(); i != m_fonts.end(); ++ i )
   {
      if( ( *i )->m_lf.lfHeight == nHeight &&
            ::_tcscmp( szFaceName, ( *i )->m_lf.lfFaceName ) == 0 && 
            ( ( *i )->m_lf.lfWeight == FW_BOLD ) == !!( nFlags & eFontBold ) &&
            !!( ( *i )->m_lf.lfItalic ) == !!( nFlags & eFontItalic ) &&
            !!( ( *i )->m_lf.lfUnderline ) == !!( nFlags &eFontUnderline ) )
         // Here's the match
         return ( *i )->QueryInterface( IID_IFontCache, reinterpret_cast< void ** >( ppFont ) );
   }

   // Not Found, create a new font
   CComObject< cFontCache > *pNewFont;
   CComObject< cFontCache >::CreateInstance( &pNewFont );

   ::memset( &pNewFont->m_lf, '\0', sizeof( LOGFONT ) );
   pNewFont->m_lf.lfHeight = nHeight;
   ::_tcscpy( pNewFont->m_lf.lfFaceName, szFaceName );

   if( nFlags & eFontBold )
      pNewFont->m_lf.lfWeight = FW_BOLD;

   if( nFlags & eFontItalic )
      pNewFont->m_lf.lfItalic = TRUE;

   if( nFlags & eFontUnderline )
      pNewFont->m_lf.lfUnderline = TRUE;

   // Commit the font
   m_fonts.push_back( pNewFont );
   pNewFont->AddRef();

   *ppFont = pNewFont;
   return S_OK;
}

STDMETHODIMP cManager::GetScreenSize(LPSIZE sz)
{
   _ASSERTE( sz != NULL );

   RECT rc;
   ::GetClientRect( m_hMain, &rc );

   sz->cx = rc.right - rc.left;
   sz->cy = rc.bottom - rc.top;

   return S_OK;
}

STDMETHODIMP cManager::CreateCanvas(LPSIZE psz, ICanvas **ppCanvas)
{
   _ASSERTE( psz != NULL );
   _ASSERTE( psz->cx > 0 && psz->cy > 0 );
   _ASSERTE( ppCanvas != NULL );

   CComObject< cCanvas > *pCanvas;
   CComObject< cCanvas >::CreateInstance( &pCanvas );

   pCanvas->m_sz = *psz;

   // Set the root clipper
   ClipParams cClip = { { 0, 0, psz->cx, psz->cy },
      { 0, 0 }, VARIANT_TRUE };

   pCanvas->m_clipping.push( cClip );

   *ppCanvas = pCanvas;
   pCanvas->AddRef();

	return S_OK;
}

STDMETHODIMP cManager::CreateView(ViewParams *pParams, ILayer *pLayer, IView **ppView)
{
   CComPtr< IRootLayer > pRoot;
   m_pRootSite->m_pSink->QueryInterface( &pRoot );

   return pRoot->CreateView( pParams, pLayer, ppView );
}

STDMETHODIMP cManager::LoadView(BSTR strSchema, IView **ppView)
{
   CComPtr< IRootLayer > pRoot;
   m_pRootSite->m_pSink->QueryInterface( &pRoot );

	return pRoot->LoadView( strSchema, ppView );
}

STDMETHODIMP cManager::CreateBrushImage(long nColor, IImageCache **ppImg)
{
   CComObject< cBrushImage > *pBrush;
   CComObject< cBrushImage >::CreateInstance( &pBrush );

   pBrush->put_Color( nColor );

   *ppImg = pBrush;
   pBrush->AddRef();

	return S_OK;
}

STDMETHODIMP cManager::LoadImageSchema(IUnknown *pSchema, IImageCache **ppImg)
{
   MSXML::IXMLDOMElementPtr pElement = pSchema;

   // Look for elements that specify a background image
   _variant_t vFileSrc = pElement->getAttribute( _T( "imagefilesrc" ) ),
      vPortalSrc = pElement->getAttribute( _T( "imageportalsrc" ) ),
      vBrushSrc = pElement->getAttribute( _T( "imagebrushsrc" ) );

   if( vFileSrc.vt != VT_NULL )
   {
      _ASSERTE( vFileSrc.vt = VT_BSTR );

      // We has a file source, attempt to create an image
      HRESULT hRes = LoadBitmapFile( vFileSrc.bstrVal, ppImg );
      _ASSERTE( SUCCEEDED( hRes ) );
   }
   else if( vPortalSrc.vt != VT_NULL )
   {
      try
      {
         long nFileID = static_cast< long >( vPortalSrc );
         _ASSERTE( nFileID > 0 );
         _ASSERTE( nFileID < 0xFFFFL );

         long nPortalID = 0x06000000 + nFileID;

         // It certainly converted properly - attempt to load the bitmap
         HRESULT hRes = LoadBitmapPortal( nPortalID, ppImg );
         _ASSERTE( SUCCEEDED( hRes ) );
      }
      catch( ... )
      {
         // It must be possible to convert the portal src into
         // a long int value.
         _ASSERTE( FALSE );
      }
   }
   else if( vBrushSrc.vt != VT_NULL )
   {
      try
      {
         long nColor = vBrushSrc;
         CreateBrushImage( nColor, ppImg );
      }
      catch( ... )
      {
         // Failed to convert to long
         _ASSERTE( FALSE );
      }
   }
   else
      // Otherwise, this background is transparent
      *ppImg = NULL;

   return S_OK;
}

STDMETHODIMP cManager::CreateFontSchema(long nDefHeight, long nDefOptions, IUnknown *pSchema, IFontCache **ppCache)
{
   MSXML::IXMLDOMElementPtr pElement = pSchema;

   _variant_t vFacename = pElement->getAttribute( _T( "fontface" ) ),
      vSize = pElement->getAttribute( _T( "fontsize" ) ),
      vStyle = pElement->getAttribute( _T( "fontstyle" ) );

   _bstr_t strFacename;
   long nHeight = nDefHeight,
      nStyle = nDefOptions;

   if( vFacename.vt != VT_NULL )
   {
      _ASSERTE( vFacename.vt == VT_BSTR );
      strFacename = vFacename.bstrVal;
   }
   else
   {
	   BSTR bstrFontName;
	   get_FontName(&bstrFontName);
	   strFacename = bstrFontName;//_T( "Times New Roman" );
   }

   if( vSize.vt != VT_NULL )
      nHeight = static_cast< long >( vSize );

   if( vStyle.vt != VT_NULL )
   {
      _ASSERTE( vStyle.vt == VT_BSTR );

      nStyle = 0;

      // Make a copy of the value since strtok will destroy it nicely
      _bstr_t strStyle = vStyle.bstrVal;

      for( wchar_t *pTok = ::wcstok( strStyle, L" " ); pTok != NULL; pTok = ::wcstok( NULL, L" " ) )
      {
         if( ::_wcsicmp( L"bold", pTok ) == 0 )
            nStyle |= eFontBold;
         else if( ::_wcsicmp( L"italic", pTok ) == 0 )
            nStyle |= eFontItalic;
         else if( ::_wcsicmp( L"underline", pTok ) == 0 )
            nStyle |= eFontUnderline;
         else
            // Unknown token value, data problem
            _ASSERTE( FALSE );
      }
   }

	return CreateFont( strFacename, nHeight, nStyle, ppCache );
}

STDMETHODIMP cManager::LoadResourceModule(BSTR strLibrary, long *pnModule)
{
   USES_CONVERSION;

   LPCTSTR szLibrary = OLE2T( strLibrary );

   HMODULE hLoaded = GetModuleHandle( szLibrary );
   if( hLoaded == NULL )
   {
      TCHAR szPath[ MAX_PATH ];
      *pnModule = reinterpret_cast< long >( ::LoadLibrary( ::InjectMapPath( eInjectPathAgent, szLibrary, szPath ) ) );

      _ASSERTE( *pnModule != NULL );
   }
   else
      *pnModule = reinterpret_cast< long >( hLoaded );

	return S_OK;
}

STDMETHODIMP cManager::get_ResourcePath(BSTR *pVal)
{
   _ASSERTE( pVal != NULL );
   TCHAR szPath[ MAX_PATH ];

   *pVal = T2BSTR( ::InjectMapPath( eInjectPathAgent, _T( "" ), szPath ) );

	return S_OK;
}

STDMETHODIMP cManager::get_Plugin(BSTR strProgID, LPDISPATCH *pVal)
{
   _ASSERTE( strProgID != NULL );
   _ASSERTE( pVal != NULL );

   // Prepend a fully-qualified service id
   _bstr_t strPath ( _T( "plugins\\" ) );
   strPath += strProgID;

   return m_pDecal->get_Object ( strPath, IID_IDispatch, reinterpret_cast< LPVOID * > ( pVal ) );
}

STDMETHODIMP cManager::get_NetworkFilter(BSTR strProgID, LPDISPATCH *pVal)
{
   _ASSERTE( strProgID != NULL );
   _ASSERTE( pVal != NULL );

   // Prepend a fully-qualified service id
   _bstr_t strPath ( _T( "services\\DecalNet.NetService\\" ) );
   strPath += strProgID;

   return m_pDecal->get_Object ( strPath, IID_IDispatch, reinterpret_cast< LPVOID * > ( pVal ) );
}

STDMETHODIMP cManager::LoadViewObject(IUnknown *pSchema, IView **ppView)
{
   CComPtr< IRootLayer > pRoot;
   m_pRootSite->m_pSink->QueryInterface( &pRoot );

	return pRoot->LoadViewObject( pSchema, ppView );
}

STDMETHODIMP cManager::CreateInputBuffer(IInputBuffer **ppInput)
{
   CComObject< cInputBuffer > *pNew;
   CComObject< cInputBuffer >::CreateInstance( &pNew );

	return pNew->QueryInterface( ppInput );
}

STDMETHODIMP cManager::get_HWND(long *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = reinterpret_cast< long >( m_hMain );

	return S_OK;
}

STDMETHODIMP cManager::get_Focus(VARIANT_BOOL *pVal)
{
   _ASSERTE( pVal != NULL );
   
   *pVal = ( m_pKeyboard != NULL ) ? VARIANT_TRUE : VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP cManager::get_OldWndProc(long *pOldWndProc)
{
	_ASSERTE( pOldWndProc != NULL );

	*pOldWndProc = reinterpret_cast< long > (m_pfnOld);

	return S_OK;
}

STDMETHODIMP cManager::put_CurrentSelection( long nID)
{
	return m_pHooks->put_CurrentSelection( nID );
}

STDMETHODIMP cManager::get_CurrentSelection(long *nID)
{
	return m_pHooks->get_CurrentSelection( nID );
}

STDMETHODIMP cManager::put_PreviousSelection(long nID)
{	
	return m_pHooks->put_PreviousSelection( nID );
}

STDMETHODIMP cManager::get_PreviousSelection(long *nID)
{	
	return m_pHooks->get_PreviousSelection( nID );
}

STDMETHODIMP cManager::WriteToChatWindow(BSTR szText, long lColor)
{
	return m_pHooks->ChatOut( szText, lColor );
}

STDMETHODIMP cManager::RawWriteToChatWindow(BSTR szText, long lColor)
{
	return m_pHooks->RawChatOut( szText, lColor );
}

STDMETHODIMP cManager::SetCursorPosition(long x, long y)
{
	return m_pHooks->SetCursorPosition( x, y );
}

STDMETHODIMP cManager::QueryMemLoc(BSTR bstrTag, long *pVal)
{
	if( m_bContainer )
	{
		*pVal = NULL;
		return S_OK;
	}

	return m_pHooks->QueryMemLoc( bstrTag, pVal );
}

STDMETHODIMP cManager::QueryKeyboardMap(BSTR bstrName, long *pAsciiVal)
{
	USES_CONVERSION;
	// Para > this damned proc

	RegKey key;

	//HANDLE hFile;
	
	DWORD dwType = REG_EXPAND_SZ;
	DWORD dwLengthPath = MAX_PATH;
	DWORD dwLengthMap;
	//DWORD dwFileSize;
	//DWORD lpdwBytesRead;
	
	//int count = 0;

	//char* pDest;
	//char *pEnd;
	TCHAR tempbuf[MAX_PATH];
	TCHAR tempfile[MAX_PATH];

	*pAsciiVal = 0;

	if (key.Open(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00"), KEY_QUERY_VALUE) == ERROR_SUCCESS)
	{
		ZeroMemory(tempbuf, sizeof(tempbuf));
		if (key.QueryStringValue(_T("Path"), tempbuf, &dwLengthPath) == ERROR_SUCCESS)
		{
			dwLengthMap = MAX_PATH;
			ZeroMemory(tempfile, sizeof(tempfile));
			if (key.QueryValue(_T("CurrentInputMap"), &dwType, tempfile, &dwLengthMap) != ERROR_SUCCESS) 
				lstrcat(tempbuf, _T("\\default.map"));
			else
			{
				// someone less peaved than me can choose a better answer to the 2 strcat's
				lstrcat(tempbuf, _T("\\"));
				lstrcat(tempbuf, tempfile);
			}
		}
		else
			return E_FAIL;
	}
	else
		return E_FAIL;
	
	//::MessageBox(NULL, tempbuf, "QKM Path", 0);
	// The following requires more testing than what i've given it.

	FILE * fKeyMap = fopen(tempbuf, "r");
	if (fKeyMap == NULL)
		return S_FALSE;

	DWORD dwNumMaps;
	fscanf(fKeyMap, "%i\r\n", &dwNumMaps);

	DWORD dwBlah1;
	DWORD dwChar;
	DWORD dwBlah3;
	char szName[256];
	DWORD dwBlah5;
	DWORD dwBlah6;

	// Not sure if this method is faster than the code below,
	// but it's a bit easier to tell what's going on
	while (fscanf(fKeyMap, "%i\t%i\t%i\t%s\t%i\t%i\r\n", &dwBlah1, &dwChar, &dwBlah3, szName, &dwBlah5, &dwBlah6) != 0)
	{
		if (stricmp(szName, OLE2A(bstrName)) == 0)
			break;

		memset(szName, 0, 256);
		dwChar = 0;
	}
	*pAsciiVal = (long)dwChar;
	fclose(fKeyMap);
	return S_OK;
/*
	hFile = CreateFile(tempbuf, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	
	if(hFile==INVALID_HANDLE_VALUE)
	{
		return E_FAIL;
	}
	
	dwFileSize = GetFileSize(hFile, NULL);

	char* pBuffer;
	pBuffer = new char[dwFileSize+1];

	BOOL bSuccess = ReadFile (hFile, pBuffer, dwFileSize, &lpdwBytesRead, 0);

	::CloseHandle (hFile);

	if (!bSuccess)
	{
		delete[] pBuffer;
		return E_FAIL;
	}

	pBuffer [lpdwBytesRead] = 0;

	pDest = strstr(pBuffer, OLE2T(bstrName));
	
	if(pDest != NULL)
	{
		while(pDest != pBuffer)
		{
			pDest--;
			if(*pDest=='\t')
			{
				count++;
				if(count==3)
				{
					*pEnd=0;
					pDest++;
					*pAsciiVal = atoi(pDest);
					break;
				}
				pEnd = pDest;
			}
		}
	}
	delete[] pBuffer;

	return S_OK;
	*/
}

STDMETHODIMP cManager::CastSpell(long lSpellID, long lObjectID)
{
	return m_pHooks->CastSpell( lSpellID, lObjectID );
}

STDMETHODIMP cManager::MoveItem(long lObjectID, long lPackID, long lSlot, long lStack)
{
	if( lStack == 0 )
		return m_pHooks->MoveItem( lObjectID, lPackID, lSlot, VARIANT_FALSE );
	else
		return m_pHooks->MoveItem( lObjectID, lPackID, lSlot, VARIANT_TRUE );
}

STDMETHODIMP cManager::SelectItem(long lObjectID)
{
	return m_pHooks->SelectItem( lObjectID );
}

STDMETHODIMP cManager::UseItem(long lObjectID, long lUseOnSelectedItem)
{
	return m_pHooks->UseItem( lObjectID, lUseOnSelectedItem );
}

STDMETHODIMP cManager::get_CombatState(long *pVal)
{
	return m_pHooks->get_CombatState( pVal );
}

STDMETHODIMP cManager::get_ChatState(VARIANT_BOOL *pVal)
{
	HRESULT HRes = m_pHooks->get_ChatState( pVal );
	
	if( m_pKeyboard != NULL )
		*pVal = VARIANT_TRUE;

	return HRes;
}

STDMETHODIMP cManager::UseItemEx(long Use, long UseOn)
{
	return m_pHooks->UseItemEx( Use, UseOn );
}

STDMETHODIMP cManager::get_Decal(IDecal **pVal) {
	return m_pDecal->QueryInterface(pVal);
}

STDMETHODIMP cManager::GetFellowStats(long charID)
{
	return m_pHooks->GetFellowStats( charID );
}

STDMETHODIMP cManager::get_Hooks(IACHooks **pVal)
{
	return m_pHooks->QueryInterface(pVal);	
}

STDMETHODIMP cManager::RedrawBar()
{
	m_pRootSite->reformat() ;
	return S_OK ;
}


/////////////////////////////////////////////////////////////////////////////
// Used to determine the font to use in Decal
/////////////////////////////////////////////////////////////////////////////

static _bstr_t g_bstrDecalFontName = "Times New Roman";
static bool g_bDecalFontRead = false;

STDMETHODIMP cManager::get_FontName(BSTR *pFontName)
{
	// Do we need to read the font name???
	if (!g_bDecalFontRead)
	{
		RegKey key;

		// Open the main Decal Key
		if (key.Create(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Decal")) == ERROR_SUCCESS)
		{
			// Get font type
			DWORD dwFontType;
			if (key.QueryDWORDValue("FontType", dwFontType) == ERROR_SUCCESS)
			{
				// Act on type
				switch (dwFontType)
				{
				case 0: // Default Font
					{
						// No-op
					}
					break;

				case 1: // Current AC Client Font
					{
						RegKey k;
						TCHAR   szFontName[512];
						DWORD   dwCount = 512;

						// Get the AC Client font
						k.Create(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00"));
						if (k.QueryStringValue("Font", szFontName, &dwCount) == ERROR_SUCCESS)
						{
							g_bstrDecalFontName = _bstr_t(szFontName);
						}

						k.Close();
					}
					break;

				case 2: // Custom Font Name
					{
						TCHAR szFontName[512];
						DWORD dwCount = 512;

						// Get the custom font
						if (key.QueryStringValue("FontName", szFontName, &dwCount) == ERROR_SUCCESS)
						{
							g_bstrDecalFontName = _bstr_t(szFontName);
						}
					}
					break;
				}
			}
		}

		key.Close();

		// Assume its found (don't try again)
		g_bDecalFontRead = true;

		//MessageBox(NULL, (LPCTSTR) g_bstrDecalFontName, NULL, MB_OK);
	}

	// Pass back the font name
	*pFontName = g_bstrDecalFontName;

	return S_OK;
}

void cManager::onChatMessage( BSTR bstrText, long lColor, VARIANT_BOOL *pbEat )
{
	VARIANT_BOOL bEat = VARIANT_FALSE;
	for( cPluginList::iterator i = _p->m_plugins.begin(); i != _p->m_plugins.end(); ++i )
	{
		if( i->m_dwSinkCaps & eManagerSinkCapPlugin )
		{
			CComPtr<IPluginSink> pPluginSink;
			i->m_pPlugin->QueryInterface( &pPluginSink );
			pPluginSink->ChatMessage( bstrText, &lColor, &bEat );
			if( bEat == VARIANT_TRUE )
				*pbEat = VARIANT_TRUE;
		}
	}
}

void cManager::onChatText( BSTR bstrText, VARIANT_BOOL *pbEat )
{
	VARIANT_BOOL bEat = VARIANT_FALSE;
	for( cPluginList::iterator i = _p->m_plugins.begin(); i != _p->m_plugins.end(); ++i )
	{
		if( i->m_dwSinkCaps & eManagerSinkCapPlugin )
		{
			CComPtr<IPluginSink> pPluginSink;
			i->m_pPlugin->QueryInterface( &pPluginSink );
			pPluginSink->ChatText( bstrText, &bEat );
			if( bEat == VARIANT_TRUE )
				*pbEat = VARIANT_TRUE;
		}
	}
}
