// ProtocolStack.h
// Declaration of class cProtocolStack

#ifndef __PROTOCOLSTACK_H
#define __PROTOCOLSTACK_H

#include <crtdbg.h>	// For assertions

#define DEFAULT_HEADER_SIZE 16
#define FRAGMENT_SIZE 448

#define MAX_FRAGMENT_COUNT 1024

#pragma pack( push, 1 )
// The packet header
struct cPacketHeader
{
	DWORD	m_dwSequence;
	DWORD	m_dwFlags;
	DWORD	m_dwCRC;

	WORD	m_wUnk1;
	WORD	m_wUnk2;
	WORD	m_wTotalSize;
	WORD	m_wUnk3;
};
#pragma pack( pop )


class FragmentMask
{
public:
	FragmentMask () { ::memset (m_mask, 0, sizeof (m_mask)); m_size = 0; }

	void SetSize (WORD wSize)
	{
		_ASSERTE (wSize < MAX_FRAGMENT_COUNT);
		m_size = wSize;

		::memset (m_mask, 0, sizeof (m_mask));
		for (WORD wIndex = 0; wIndex < wSize; ++wIndex)
		{
			m_mask[wIndex/32] |= (1L << (wIndex % 32));
		}
	}

	void GotFragment (WORD wWhich)
	{
		_ASSERTE (wWhich < m_size && wWhich >= 0);

		m_mask[wWhich/32] &= ~(1 << (wWhich % 32));
	}

	bool IsComplete ()
	{
		for (WORD wLoop = 0; wLoop < (FRAGMENT_SIZE/32); ++wLoop)
		{
			if (m_mask[wLoop])
			{
				return false;
			}
		}

		return true;
	}

private:
	DWORD m_mask[FRAGMENT_SIZE/32];
	WORD m_size;
};

class cMessageStack
{
public:
#pragma pack( push, 1 )
   struct cMessageHeader
   {
      DWORD m_dwSequence;
      DWORD m_dwObjectID;
      WORD m_wFragmentCount;
      WORD m_wFragmentLength;
      WORD m_wFragmentIndex,
         m_wUnknown1;
   };
#pragma pack( pop )

   class cMessage
   {
   protected:
//      DWORD m_dwFragmentsMask;
      BYTE *m_pbData;
      mutable bool m_bOwn;
	  mutable FragmentMask m_FragmentMask;

   public:
      cMessage( BYTE *pbData );
      cMessage( const cMessage &msg );
      ~cMessage();

      cMessageHeader *getMessageHeader() const
      {
         return reinterpret_cast< cMessageHeader * >( m_pbData );
      }

      cMessage &operator= ( const cMessage &msg );

      BYTE *getBody() const
      {
         return m_pbData + sizeof( cMessageHeader );
      }

      DWORD getBodyLength() const
      {
         return getMessageHeader()->m_wFragmentLength - sizeof( cMessageHeader );
      }

      bool isComplete() const
      {
         return m_FragmentMask.IsComplete ();
      }

      DWORD getMessageCode() const
      {
         return *reinterpret_cast< DWORD * >( m_pbData + sizeof( cMessageHeader ) );
      }

      bool fragmentMatch( BYTE *pFragmentStart );
      void insertFragment( BYTE *pFragmentStart );

      static DWORD calcMessageLength( BYTE *pbHeader );
   };

   // Objects wishing to receive 
   class cCallback
   {
   public:
      virtual void onMessage( cMessage & ) = 0;
   };

private:
   typedef std::list< cMessage > cMessageList;

   cCallback *m_pCallback;
   cMessageList m_messages;

public:
   cMessageStack();
   ~cMessageStack();

   void start( cCallback * );
   void stop();

   void processPacket( DWORD dwLength, BYTE *pbPayload );

private:
   void splitPacket( DWORD dwLength, BYTE *pbPayload );
};

#endif
