// MessageLoaders.cpp
// Implementation of message loaders for various data types

#include "StdAfx.h"
#include "MessageLoaders.h"

// The boatload of decoding classes
template< class ValueT >
class cVariantLoader
: public cFieldLoader
{
public:
   virtual void *skip( void *pData )
   {
      return reinterpret_cast< BYTE * >( pData ) + sizeof( ValueT );
   }

   virtual void *align( void *pData, void *pStart )
   {
      size_t nOffset = reinterpret_cast< BYTE * >( pData ) - reinterpret_cast< BYTE * >( pStart );
      if( ( nOffset % sizeof( ValueT ) ) == 0 )
         return pData;

      return ( reinterpret_cast< BYTE * >( pStart ) + nOffset + sizeof( ValueT ) - ( nOffset % sizeof( ValueT ) ) );
   }

   virtual bool testValue( void *pData, void *pEnd )
   {
      return ( ( reinterpret_cast< BYTE * >( pData ) + sizeof( ValueT ) ) <= reinterpret_cast< BYTE * >( pEnd ) );
   }

   ValueT &valueOf( void *pvData )
   {
      return *reinterpret_cast< ValueT * >( pvData );
   }
};

class cByteLoader
: public cVariantLoader< BYTE >
{
public:
   virtual void getValue( void *pvData, LPVARIANT pDest )
   {
      pDest->vt = VT_UI1;
      pDest->bVal = valueOf( pvData );
   }

   virtual long getNumber( void *pvData )
   {
      return valueOf( pvData );
   }
};

class cShortLoader
: public cVariantLoader< short >
{
public:
   virtual void getValue( void *pvData, LPVARIANT pDest )
   {
      pDest->vt = VT_I2;
      pDest->iVal = valueOf( pvData );
   }

   virtual long getNumber( void *pvData )
   {
      return valueOf( pvData );
   }
};

class cLongLoader
: public cVariantLoader< long >
{
public:
   virtual void getValue( void *pvData, LPVARIANT pDest )
   {
      pDest->vt = VT_I4;
      pDest->lVal = valueOf( pvData );
   }

   virtual long getNumber( void *pvData )
   {
      return valueOf( pvData );
   }
};

class cFloatLoader
: public cVariantLoader< float >
{
public:
   virtual void getValue( void *pvData, LPVARIANT pDest )
   {
      pDest->vt = VT_R4;
      pDest->fltVal = valueOf( pvData );
   }
};

class cDoubleLoader
: public cVariantLoader< double >
{
public:
   virtual void getValue( void *pvData, LPVARIANT pDest )
   {
      pDest->vt = VT_R8;
      pDest->dblVal = valueOf( pvData );
   }
};

class cStringLoader
: public cFieldLoader
{
public:
   virtual void *skip( void *pvData )
   {
      WORD wLength = *reinterpret_cast< WORD * >( pvData ) + sizeof( WORD );
      if( ( wLength % sizeof( DWORD ) ) != 0 )
         wLength += sizeof( DWORD ) - ( wLength % sizeof( DWORD ) );

      return reinterpret_cast< BYTE * >( pvData ) + wLength;
   }

   virtual void *align( void *pvData, void * )
   {
      // Error, this is not a valid alignment type
      _ASSERTE( FALSE );

      return pvData;
   }

   virtual bool testValue( void *pvData, void *pvEnd )
   {
      WORD wLength = *reinterpret_cast< WORD * >( pvData ) + sizeof( WORD ),
         wField = wLength;

      if( ( wField % sizeof( DWORD ) ) != 0 )
         wField += sizeof( DWORD ) - ( wField % sizeof( DWORD ) );

      // It fits inside the packet, so our field isn't too big
      if( ( reinterpret_cast< BYTE * >( pvData ) + wField ) > reinterpret_cast< BYTE * >( pvEnd ) )
         return false;

      if( *( reinterpret_cast< char * >( pvData ) + wLength - 1 ) != '\0' )
         // This string is not NULL terminated
         return false;

      // Anything else just makes the string ugly, but won't lead to a memory
      // overrun, so let them play
      return true;
   }

   virtual void getValue( void *pvData, LPVARIANT pDest )
   {
      USES_CONVERSION;

      pDest->vt = VT_BSTR;
      pDest->bstrVal = A2BSTR( reinterpret_cast< char * >( reinterpret_cast< BYTE * >( pvData ) + sizeof( WORD ) ) );
   }
};

long cFieldLoader::getNumber( void *pvData )
{
   // This default implementation does not support this conversion
   _ASSERTE( FALSE );

   return 0;
}

cFieldLoader *cFieldLoader::lookup( const _bstr_t &strName )
{
   cFieldLoaderMap::iterator i = g_primitives.find( strName );
   if( i == g_primitives.end() )
      return NULL;

   return i->second.get();
}

void cFieldLoader::init()
{
   addLoader( _T( "BYTE" ), new cByteLoader );
   addLoader( _T( "WORD" ), new cShortLoader );
   addLoader( _T( "DWORD" ), new cLongLoader );
   addLoader( _T( "float" ), new cFloatLoader );
   addLoader( _T( "double" ), new cDoubleLoader );
   addLoader( _T( "String" ), new cStringLoader );
}

void cFieldLoader::term()
{
   g_primitives.clear();
}

void cFieldLoader::addLoader( LPCTSTR szName, cFieldLoader *pLoader )
{
   g_primitives.insert( cFieldLoaderMap::value_type( szName, std::auto_ptr< cFieldLoader >( pLoader ) ) );
}

cFieldLoader::cFieldLoaderMap cFieldLoader::g_primitives;
