// MessageParsers.cpp
// Implementation of parsers and runtime objects for parsing message data

#include "StdAfx.h"
#include "DecalNet.h"

#include "MessageParsers.h"
#include "MessageLoaders.h"

#include "MessageStruct.h"
#include "MessageVector.h"

class cStructElement
   : public cMessage::cMessageElement
{
public:
   cMessage::cElementList m_members;

   virtual bool load( cMessage::cLoadContext &context )
   {
      // Walk through the list of members and have them load
      int nIndex = context.addField( this, NULL );

      for( cMessage::cElementList::iterator i = m_members.begin(); i != m_members.end(); ++ i )
      {
         if( !i->get()->load( cMessage::cLoadContext( &context ) ) )
            return false;
      }

      // Update the field count
      context.groupField( nIndex );
      return true;
   }

   virtual void getValue( cMessage *pMessage, cMessage::cFieldList::iterator i, LPVARIANT pDest )
   {
      if( i->m_pDisp.p == NULL )
      {
         // Create the vector object
         CComObject< cMessageStructIter > *pVecDisp;
         CComObject< cMessageStructIter >::CreateInstance( &pVecDisp );

         pVecDisp->init( pMessage, ( i - pMessage->m_fields.begin() ) );
         i->m_pDisp = pVecDisp;
      }
      else
         i->m_pDisp->Reset();

      pDest->vt = VT_DISPATCH;
      i->m_pDisp->QueryInterface ( IID_IMessageMember, reinterpret_cast< LPVOID * > ( &pDest->pdispVal ) );
   }
};

class cFieldParser
: public cElementParser
{
public:
   class cFieldElement
      : public cMessage::cMessageElement
   {
   public:
      cFieldLoader *m_pLoader;

      virtual bool load( cMessage::cLoadContext &context )
      {
         if( !m_pLoader->testValue( context.getMessage()->m_pEndCrack, context.getMessage()->m_pEndData ) )
         {
            // The element failed, this will usually indicate bad schema
            _ASSERTE( FALSE );
            return false;
         }

         context.addField( this, context.getMessage()->m_pEndCrack );
         context.getMessage()->m_pEndCrack = reinterpret_cast< BYTE * >( m_pLoader->skip( context.getMessage()->m_pEndCrack ) );

         return true;
      }

      virtual void getValue( cMessage *, cMessage::cFieldList::iterator i, LPVARIANT pDest )
      {
         m_pLoader->getValue( i->m_pvData, pDest );
      }

      virtual long getNumber( cMessage::cFieldList::iterator i )
      {
         return m_pLoader->getNumber( i->m_pvData );
      }
   };

   // Strings
   static _bstr_t g_strType;
   static _bstr_t g_strName;

   virtual cMessage::cMessageElement *parse( cContext &context, MSXML::IXMLDOMElementPtr &pElement )
   {
      // Locate the data type in the list
      _variant_t strDataType = pElement->getAttribute( g_strType );

      if( strDataType.vt != VT_BSTR )
      {
         // Schema error
         _ASSERTE( strDataType.vt == VT_BSTR );
         return NULL;
      }

      _bstr_t bstrDataType = strDataType;

      cFieldLoader *pField = cFieldLoader::lookup( bstrDataType );
      if( pField == NULL )
         return parseStruct( context, pElement, bstrDataType );

      _variant_t strName = pElement->getAttribute( g_strName );

      if( strName.vt != VT_BSTR )
      {
         _ASSERTE( strName.vt == VT_BSTR );
         return NULL;
      }

      cFieldElement *pFieldElement = new cFieldElement;

      pFieldElement->m_strName = strName;
      pFieldElement->m_pLoader = pField;
      return pFieldElement;
   }

   cMessage::cMessageElement *parseStruct( cContext &context, MSXML::IXMLDOMElementPtr &pElement, const _bstr_t &strType )
   {
      // Locate the data type in the list
      _variant_t strName = pElement->getAttribute( cFieldParser::g_strName );

      if( strName.vt != VT_BSTR )
      {
         // Schema error
         _ASSERTE( strName.vt == VT_BSTR );
         return NULL;
      }

      // We have the field name, now look up data type schema
      USES_CONVERSION;

      TCHAR szQuery[ 255 ];
      ::_stprintf( szQuery, _T( "/schema/datatypes/type[@name='%s']" ),
         OLE2T( strType ) );

      MSXML::IXMLDOMElementPtr pStruct = pElement->ownerDocument->selectSingleNode( szQuery );
      if( pStruct.GetInterfacePtr() == NULL )
      {
         // Could not cross reference the structure
         _ASSERTE( pStruct.GetInterfacePtr() != NULL );
         return NULL;
      }

      VSBridge::auto_ptr< cStructElement > pStructElement( new cStructElement() );
      pStructElement->m_strName = strName;
      if( context.parseChildren( pStructElement->m_members, pStruct ) )
         return pStructElement.release();

      return NULL;
   }
};

_bstr_t cFieldParser::g_strType( _T( "type" ) );
_bstr_t cFieldParser::g_strName( _T( "name" ) );

class cMaskParser
: public cElementParser
{
public:
   class cMaskElement
      : public cMessage::cMessageElement
   {
   public:
      cMessage::cMessageElement *m_pValue;

      class cMask
      {
      public:
         DWORD m_dwValue;
         DWORD m_dwFirstChild,
            m_dwLastChild;
      };

      typedef std::vector< cMask > cMaskMap;

      cMaskMap m_masks;
      cMessage::cElementList m_members;

      virtual bool load( cMessage::cLoadContext &context )
      {
         // First, look for our value element in the message
         cMessage::cFieldList::iterator iField = context.lookupField( m_pValue );
         if( iField == context.getMessage()->m_fields.end() )
         {
            // Could not find a mask source - most likely a bad name of some kind
            _ASSERTE( FALSE );
            return false;
         }

         _ASSERT( iField->m_pSchema == m_pValue );

         long nMaskValue = iField->m_pSchema->getNumber( iField );

         // Walk through the mask values and load all of the fields in range
         for( cMaskMap::iterator i = m_masks.begin(); i != m_masks.end(); ++ i )
         {
            if( ( nMaskValue & i->m_dwValue ) == 0 )
               continue;

            // This is a valid mask load up all teh fields in range
            cMessage::cElementList::iterator m_end = m_members.begin() + i->m_dwLastChild;
            for( cMessage::cElementList::iterator j = m_members.begin() + i->m_dwFirstChild; j != m_end; ++ j )
            {
               if( !j->get()->load( context ) )
                  return false;
            }
         }

         return true;
      }

      virtual void getValue( cMessage *, cMessage::cFieldList::iterator, LPVARIANT )
      {
         // This element should never insert itself into the message list
         _ASSERTE( FALSE );
      }
   };

   static _bstr_t g_strMask;
   static _bstr_t g_strValue;

   virtual cMessage::cMessageElement *parse( cContext &context, MSXML::IXMLDOMElementPtr &pElement )
   {
      _variant_t vName = pElement->getAttribute( cFieldParser::g_strName );
      if( vName.vt != VT_BSTR )
      {
         // It must have the name field
         _ASSERTE( vName.vt == VT_BSTR );
         return NULL;
      }

      // Find a matching element in the schema
      cMessage::cMessageElement *pName = context.findElement( vName.bstrVal );
      if( pName == NULL )
      {
         // The mask field was not found
         _ASSERTE( pName != NULL );
         return NULL;
      }

      // Create the mask element
      VSBridge::auto_ptr< cMaskElement > pMaskElement( new cMaskElement );
      pMaskElement->m_pValue = pName;

      // Walk through each of the mask values
      USES_CONVERSION;

      MSXML::IXMLDOMNodeListPtr pMaskList = pElement->selectNodes( g_strMask ); 
      for( MSXML::IXMLDOMElementPtr pMask = pMaskList->nextNode(); pMask.GetInterfacePtr() != NULL; pMask = pMaskList->nextNode() )
      {
         _variant_t vValue = pMask->getAttribute( g_strValue );
         if( vValue.vt != VT_BSTR )
         {
            _ASSERTE( vName.vt == VT_BSTR );
            return NULL;
         }

         // Make sure it has the 'hex' prefix
         if( vValue.bstrVal[ 0 ] != OLESTR( '0' ) )
         {
            _ASSERTE( vValue.bstrVal[ 0 ] == OLESTR( '0' ) );
            return NULL;
         }

         if( vValue.bstrVal[ 1 ] != OLESTR( 'x' ) )
         {
            _ASSERTE( vValue.bstrVal[ 1 ] == OLESTR( 'x' ) );
            return NULL;
         }

         // Attempt to convert the remaining number
         long nMaskValue;
         if( ::_stscanf( OLE2T( vValue.bstrVal + 2 ), _T( "%X" ), &nMaskValue ) != 1 )
         {
            // Could not convert value
            _ASSERTE( FALSE );
            return NULL;
         }

         long nStartOffset = pMaskElement->m_members.size();
         context.parseChildren( pMaskElement->m_members, pMask );

         cMaskElement::cMask mask = { nMaskValue, nStartOffset, pMaskElement->m_members.size() };
         pMaskElement->m_masks.push_back( mask );
      }

      return pMaskElement.release();
   }
};

_bstr_t cMaskParser::g_strMask( _T( "mask" ) );
_bstr_t cMaskParser::g_strValue( _T( "value" ) );

class cAlignParser
: public cElementParser
{
public:
   class cAlignElement
      : public cMessage::cMessageElement
   {
   public:
      cFieldLoader *m_pLoader;

      virtual bool load( cMessage::cLoadContext &context )
      {
         void *pAlign = m_pLoader->align( context.getMessage()->m_pEndCrack, context.getMessage()->m_pStartCrack );
         if( pAlign > context.getMessage()->m_pEndData )
         {
            _ASSERTE( FALSE );
            return false;
         }

         context.getMessage()->m_pEndCrack = reinterpret_cast< BYTE * >( pAlign );
         return true;
      }

      virtual void getValue( cMessage *, cMessage::cFieldList::iterator i, LPVARIANT pDest )
      {
         // This should never associate itself with a field
         _ASSERTE( FALSE );
      }
   };

   virtual cMessage::cMessageElement *parse( cContext &context, MSXML::IXMLDOMElementPtr &pElement )
   {
      // Locate the data type in the list
      _variant_t strDataType = pElement->getAttribute( cFieldParser::g_strType );

      if( strDataType.vt != VT_BSTR )
      {
         // Schema error
         _ASSERTE( strDataType.vt == VT_BSTR );
         return NULL;
      }

      _bstr_t bstrDataType = strDataType;

      cFieldLoader *pField = cFieldLoader::lookup( bstrDataType );
      if( pField == NULL )
      {
         _ASSERTE( FALSE );
         return NULL;
      }

      cAlignElement *pAlignElement = new cAlignElement;

      pAlignElement->m_pLoader = pField;
      return pAlignElement;
   }
};

class cVectorParser
: public cElementParser
{
public:
   class cVectorElement
      : public cMessage::cMessageElement
   {
   public:
      cMessage::cMessageElement *m_pLength;
      VSBridge::auto_ptr< cMessage::cMessageElement > m_pStruct;
      int m_nSkip;

	  DWORD m_dwMask;

      virtual bool load( cMessage::cLoadContext &context )
      {
         // First, look for our value element in the message
         cMessage::cFieldList::iterator iField = context.lookupField( m_pLength );
         if( iField == context.getMessage()->m_fields.end() )
         {
            // Could not find a mask source - most likely a bad name of some kind
            _ASSERTE( FALSE );
            return NULL;
         }

         _ASSERT( iField->m_pSchema == m_pLength );

         long nLength = iField->m_pSchema->getNumber( iField ) - m_nSkip;

		 if (m_dwMask != 0)
		 {
			 DWORD dwTemp = (DWORD) nLength & m_dwMask;
			 DWORD dwTempMask = m_dwMask;

			 // Shift the length field right as far as we can.
			 while (! (dwTempMask & 0x1))
			 {
				 dwTempMask >>= 1;
				 dwTemp >>= 1;
			 }

			 nLength = (long) dwTemp;
		 }

         // Insert a record for ourself
         DWORD dwIndex = context.addField( this, NULL );

         for( int i = 0; i < nLength; ++ i )
         {
            // Insert a record for the current struct
            if( !m_pStruct->load( cMessage::cLoadContext( &context ) ) )
               return false;
         }

         // Update the used field count
         context.groupField( dwIndex );
         return true;
      }

      virtual void getValue( cMessage *pMessage, cMessage::cFieldList::iterator i, LPVARIANT pDest )
      {
         if( i->m_pDisp.p == NULL )
         {
            // Create the vector object
            CComObject< cMessageVectorIter > *pVecDisp;
            CComObject< cMessageVectorIter >::CreateInstance( &pVecDisp );

            pVecDisp->init( pMessage, ( i - pMessage->m_fields.begin() ) );
            i->m_pDisp = pVecDisp;
         }
         else
            i->m_pDisp->Reset();

         pDest->vt = VT_DISPATCH;
         i->m_pDisp->QueryInterface ( IID_IMessageMember, reinterpret_cast< LPVOID * > ( &pDest->pdispVal ) );
      }
   };

   static _bstr_t g_strLength,
      g_strSkip,
	  g_strMask;

   virtual cMessage::cMessageElement *parse( cContext &context, MSXML::IXMLDOMElementPtr &pElement )
   {
      _variant_t vName = pElement->getAttribute( cFieldParser::g_strName );
      if( vName.vt != VT_BSTR )
      {
         // It must have the name field
         _ASSERTE( vName.vt == VT_BSTR );
         return NULL;
      }
      
      _variant_t vLength = pElement->getAttribute( g_strLength ),
         vSkip = pElement->getAttribute( g_strSkip ),
		 vMask = pElement->getAttribute( g_strMask );

      if( vLength.vt != VT_BSTR )
      {
         // It must have the name field
         _ASSERTE( vLength.vt == VT_BSTR );
         return NULL;
      }

      long nSkip = 0;
      if( vSkip.vt != VT_NULL )
      {
         try
         {
            nSkip = vSkip;
         }
         catch( ... )
         {
            // Failed to make the conversion
            _ASSERT( FALSE );
            return NULL;
         }
      }

	  DWORD dwMask = 0;
	  if( vMask.vt != VT_NULL )
	  {
		  dwMask = ::wcstoul (vMask.bstrVal, NULL, 16);
	  }

      cMessage::cMessageElement *pLength = context.findElement( vLength.bstrVal );
      if( pLength == NULL )
      {
         // The mask field was not found
         _ASSERTE( pLength != NULL );
         return NULL;
      }

      VSBridge::auto_ptr< cStructElement > pStructElement( new cStructElement );
      
      if( !context.parseChildren( pStructElement->m_members, pElement ) )
         return NULL;

      cVectorElement *pVector = new cVectorElement;
      pVector->m_strName = vName;
      pVector->m_pLength = pLength;
      pVector->m_nSkip = nSkip;
	  pVector->m_dwMask = dwMask;
      pVector->m_pStruct = VSBridge::auto_ptr< cMessage::cMessageElement >( pStructElement.release() );

      return pVector;
   }
};

_bstr_t cVectorParser::g_strLength( _T( "length" ) );
_bstr_t cVectorParser::g_strSkip( _T( "skip" ) );
_bstr_t cVectorParser::g_strMask( _T( "mask" ) );

class cSwitchParser
: public cElementParser
{
public:
   class cSwitchElement
      : public cMessage::cMessageElement
   {
   public:
      cMessage::cMessageElement *m_pValue;

      class cCase
      {
      public:
         DWORD m_dwValue;
         DWORD m_dwFirstChild,
            m_dwLastChild;
      };

      typedef std::vector< cCase > cCaseMap;

      cCaseMap m_cases;
      cMessage::cElementList m_members;

      virtual bool load( cMessage::cLoadContext &context )
      {
         // First, look for our value element in the message
         cMessage::cFieldList::iterator iField = context.lookupField( m_pValue );
         if( iField == context.getMessage()->m_fields.end() )
         {
            // Could not find a mask source - most likely a bad name of some kind
            _ASSERTE( FALSE );
            return NULL;
         }

         _ASSERT( iField->m_pSchema == m_pValue );

         long nCaseValue = iField->m_pSchema->getNumber( iField );

         // Walk through the mask values and load all of the fields in range
         for( cCaseMap::iterator i = m_cases.begin(); i != m_cases.end(); ++ i )
         {
            if( nCaseValue != i->m_dwValue )
               continue;

            // This is a valid mask load up all teh fields in range
            cMessage::cElementList::iterator m_end = m_members.begin() + i->m_dwLastChild;
            for( cMessage::cElementList::iterator j = m_members.begin() + i->m_dwFirstChild; j != m_end; ++ j )
            {
               if( !j->get()->load( context ) )
                  return false;
            }

            // For now we short circuit cases
            break;
         }

         return true;
      }

      virtual void getValue( cMessage *, cMessage::cFieldList::iterator, LPVARIANT )
      {
         // This element should never insert itself into the message list
         _ASSERTE( FALSE );
      }
   };

   static _bstr_t g_strCase;

   virtual cMessage::cMessageElement *parse( cContext &context, MSXML::IXMLDOMElementPtr &pElement )
   {
      _variant_t vName = pElement->getAttribute( cFieldParser::g_strName );
      if( vName.vt != VT_BSTR )
      {
         // It must have the name field
         _ASSERTE( vName.vt == VT_BSTR );
         return NULL;
      }

      // Find a matching element in the schema
      cMessage::cMessageElement *pName = context.findElement( vName.bstrVal );
      if( pName == NULL )
      {
         // The mask field was not found
         _ASSERTE( pName != NULL );
         return NULL;
      }

      // Create the mask element
      VSBridge::auto_ptr< cSwitchElement > pSwitchElement( new cSwitchElement );
      pSwitchElement->m_pValue = pName;

      // Walk through each of the mask values
      USES_CONVERSION;

      MSXML::IXMLDOMNodeListPtr pCaseList = pElement->selectNodes( g_strCase ); 
      for( MSXML::IXMLDOMElementPtr pCase = pCaseList->nextNode(); pCase.GetInterfacePtr() != NULL; pCase = pCaseList->nextNode() )
      {
         _variant_t vValue = pCase->getAttribute( cMaskParser::g_strValue );
         if( vValue.vt != VT_BSTR )
         {
            _ASSERTE( vName.vt == VT_BSTR );
            return NULL;
         }

         // Make sure it has the 'hex' prefix
         if( vValue.bstrVal[ 0 ] != OLESTR( '0' ) )
         {
            _ASSERTE( vValue.bstrVal[ 0 ] == OLESTR( '0' ) );
            return NULL;
         }

         if( vValue.bstrVal[ 1 ] != OLESTR( 'x' ) )
         {
            _ASSERTE( vValue.bstrVal[ 1 ] == OLESTR( 'x' ) );
            return NULL;
         }

         // Attempt to convert the remaining number
         long nCaseValue;
         if( ::_stscanf( OLE2T( vValue.bstrVal + 2 ), _T( "%X" ), &nCaseValue ) != 1 )
         {
            // Could not convert value
            _ASSERTE( FALSE );
            return NULL;
         }

         long nStartOffset = pSwitchElement->m_members.size();
         context.parseChildren( pSwitchElement->m_members, pCase );

         cSwitchElement::cCase _case = { nCaseValue, nStartOffset, pSwitchElement->m_members.size() };
         pSwitchElement->m_cases.push_back( _case );
      }

      return pSwitchElement.release();
   }
};

_bstr_t cSwitchParser::g_strCase( _T( "case" ) );

cMessage::cMessageElement *cElementParser::cContext::findElement( const _bstr_t &strElement )
{
   for( cContext *pContext = this; pContext != NULL; pContext = pContext->m_pParent )
   {
      for( cMessage::cElementList::iterator i = m_pElements->begin() + m_dwStartOffset; i != m_pElements->end(); ++ i )
      {
         if( i->get()->m_strName == strElement )
            return i->get();
      }
   }

   return NULL;
}

bool cElementParser::cContext::parseChildren( MSXML::IXMLDOMElementPtr &pElement )
{
   MSXML::IXMLDOMNodeListPtr pChildList = pElement->selectNodes( g_strAll ); 
   for( MSXML::IXMLDOMElementPtr pChild = pChildList->nextNode(); pChild.GetInterfacePtr() != NULL; pChild = pChildList->nextNode() )
   {
      _bstr_t strElementName = pChild->tagName;
      cElementParser *pParser = cElementParser::lookup( strElementName );
      if( pParser == NULL )
      {
         // Could not find a parse for this element type
         _ASSERTE( FALSE );
         return false;
      }

      cMessage::cMessageElement *pElement = pParser->parse( *this, pChild );
      if( pElement == NULL )
         return false;

      m_pElements->push_back( cMessage::cElementList::value_type( pElement ) );
   }

   return true;
}

_bstr_t cElementParser::cContext::g_strAll( _T( "*" ) );

bool cElementParser::cContext::parseChildren( cMessage::cElementList &elements, MSXML::IXMLDOMElementPtr &pElement )
{
   cContext c( &elements, this );
   return c.parseChildren( pElement );
}

cElementParser *cElementParser::lookup( const _bstr_t &strElement )
{
   cElementParserMap::iterator i = g_parsers.find( strElement );
   if( i == g_parsers.end() )
      return NULL;

   return i->second.get();
}

void cElementParser::init()
{
   addParser( _T( "field" ), new cFieldParser );
   addParser( _T( "maskmap" ), new cMaskParser );
   addParser( _T( "vector" ), new cVectorParser );
   addParser( _T( "switch" ), new cSwitchParser );
   addParser( _T( "align" ), new cAlignParser );
}

void cElementParser::term()
{
   g_parsers.clear();
}

void cElementParser::addParser( LPCTSTR szName, cElementParser *pParser )
{
   g_parsers.insert( cElementParserMap::value_type( szName, VSBridge::auto_ptr< cElementParser >( pParser ) ) );
}

cElementParser::cElementParserMap cElementParser::g_parsers;
