// ProtocolStack.h
// Declaration of class cProtocolStack

#ifndef __PROTOCOLSTACK_H
#define __PROTOCOLSTACK_H

#include "ACMessage.h"

#define DEFAULT_HEADER_SIZE 16
#define FRAGMENT_SIZE 448

#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 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 cProtocolMessage : public ACMessage
   {
   protected:
      mutable BYTE *m_pbData;
      mutable bool *m_pbReceived;
      mutable bool m_bOwn;

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

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

      cProtocolMessage &operator= ( const cProtocolMessage &msg );

	  // ACMessage interface implementation for protocol stack
	  virtual BYTE *getData ()
	  {
         return m_pbData + sizeof( cMessageHeader );
	  }

	  virtual DWORD getSize ()
	  {
         return getMessageHeader()->m_wFragmentLength - sizeof( cMessageHeader );
	  }

	  virtual DWORD getType ()
	  {
         return *reinterpret_cast< DWORD * >( m_pbData + sizeof( cMessageHeader ) );
	  }

      bool isComplete() const;

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

      static DWORD calcMessageLength( BYTE *pbHeader );
   };

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

   ACMessageSink *m_pCallback;
   cMessageList m_messages;

public:
   cMessageStack();
   ~cMessageStack();

   void start( ACMessageSink * );
   void stop();

   void processPacket( DWORD dwLength, BYTE *pbPayload );
   void processPacketOut( DWORD dwLength, const BYTE *pbPayload );

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

#endif
