// Message.h : Declaration of the cMessage

#ifndef __MESSAGE_H_
#define __MESSAGE_H_

#include "resource.h"       // main symbols

class cNetService;
class cMessageRoot;

// the public key to unencrypt xmls
#include "..\include\DecalKey.h"

/////////////////////////////////////////////////////////////////////////////
// cMessage
class ATL_NO_VTABLE cMessage : 
	public CComObjectRootEx<CComMultiThreadModel>,
	public IDispatchImpl< IMessage2, &IID_IMessage2, &LIBID_DecalNet >
{
public:
	void DecryptXML( const char *szFilename, std::string &szXML );
	
	class cMessageElement;

   class cField
   {
   public:
      cMessageElement *m_pSchema;
      void *m_pvData;
      int m_nOwns;
      CComPtr< IMessageIterator > m_pDisp;
   };

   typedef std::vector< cField > cFieldList;

   class cLoadContext
   {
      cMessage *m_pMessage;
      DWORD m_dwOffset;
      cLoadContext *m_pParent;

   public:
      cLoadContext( cMessage *pMessage )
         : m_pMessage( pMessage ),
         m_dwOffset( 0 ),
         m_pParent( NULL )
      {
      }

      cLoadContext( cLoadContext *pParent )
         : m_pMessage( pParent->m_pMessage ),
         m_dwOffset( m_pMessage->m_fields.size() ),
         m_pParent( pParent )
      {
      }

      cFieldList::iterator lookupField( cMessageElement *pElement );

      DWORD addField( cMessageElement *pElement, void *pvData )
      {
         cField f;
         f.m_pSchema = pElement;
         f.m_pvData = pvData;
         f.m_nOwns = 1;

         m_pMessage->m_fields.push_back( f );

         return m_pMessage->m_fields.size();
      }

      void groupField( DWORD dwIndex )
      {
         m_pMessage->m_fields[ dwIndex - 1 ].m_nOwns += m_pMessage->m_fields.size() - dwIndex;
      }

      cMessage *getMessage()
      {
         return m_pMessage;
      }
   };

   class cMessageElement
   {
   public:
      _bstr_t m_strName;

      virtual ~cMessageElement()
      {
      }

      // Decodes from the current point and returns
      // the pointer advanced by a certain number of offsets
      virtual bool load( cLoadContext &context ) = 0;
      virtual void getValue( cMessage *pMsg, cFieldList::iterator i, LPVARIANT pDest ) = 0;
      virtual long getNumber( cFieldList::iterator i )
      {
         // Default implementation fails
         _ASSERTE( FALSE );

         return 0;
      }
   };

   typedef std::vector< VSBridge::auto_ptr< cMessageElement > > cElementList;

   class cMessageSchema
   {
   public:
      cElementList m_members;

      void loadSchema( MSXML::IXMLDOMDocumentPtr &pDoc, DWORD dwSchema );
      bool parseChildren( cElementList &list, MSXML::IXMLDOMElementPtr &pElement );
   };

   cNetService *m_pService;

   typedef std::map< DWORD, VSBridge::auto_ptr< cMessageSchema > > cMessageSchemaMap;

   MSXML::IXMLDOMDocumentPtr g_pXML;
   cMessageSchemaMap g_schema;

   void init();
   void term();

	cMessage();

   cFieldList m_fields;
   long m_nType;
   cMessageSchema *m_pSchema;

   cFieldList::iterator getFieldFromElement( cMessageElement *pElement );

   BYTE *m_pStartCrack,
      *m_pEndCrack,
      *m_pEndData;

   void crackMessage( BYTE *pBody, DWORD dwSize );

   cElementList::iterator m_iLoaded;
   cMessageRoot *m_pRoot;

   bool loadNextElement();
   void loadAllElements();

BEGIN_COM_MAP(cMessage)
   COM_INTERFACE_ENTRY(IMessage2)
	COM_INTERFACE_ENTRY(IMessage)
   COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IMessage
public:
	STDMETHOD(get_Count)(/*[out, retval]*/ long *pVal);
	STDMETHOD(get_MemberName)(long nIndex, /*[out, retval]*/ BSTR *pVal);
	STDMETHOD(get_Member)(VARIANT vIndex, /*[out, retval]*/ VARIANT *pVal);
	STDMETHOD(get_Begin)(/*[out, retval]*/ IMessageIterator * *pVal);
	STDMETHOD(get_Data)(/*[out, retval]*/ VARIANT *pVal);
	STDMETHOD(get_Type)(/*[out, retval]*/ long *pVal);
};

#endif //__MESSAGE_H_
