/*
* This file is part of UAS2.
*
* UAS2 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* UAS2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License
* along with UASv1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/**
 *	@file Message.cpp
 *	Encapsulates cMessage functionality.
 */

#include "Message.h"

#pragma intrinsic( memcpy, memset, strlen )
					
cMessage::cBlock::cBlock ()
: m_pStart ( g_dispenser.get () ),
m_pUsed ( m_pStart )
{
}

cMessage::cBlock::cBlock ( const cBlock &from )
: m_pStart ( from.m_pStart ),
m_pUsed ( from.m_pUsed )
{
   from.m_pStart = NULL;
   from.m_pUsed = NULL;
}

cMessage::cBlock::~cBlock ()
{
   if ( m_pStart != NULL )
      g_dispenser.release ( m_pStart );
}

BOOL cMessage::cBlock::pasteData ( BYTE * &pData, int &nBytes )
{
   int nAvailable = BUFFER_BLOCK_SIZE - ( m_pUsed - m_pStart );
   if ( nAvailable < nBytes )
   {
      // Paste what we can
      CopyMemory( m_pUsed, pData, nAvailable );
      nBytes -= nAvailable;
      m_pUsed += nAvailable;

      return FALSE;
   }

   CopyMemory( m_pUsed, pData, nBytes );
   m_pUsed += nBytes;

   return TRUE;
}

int cMessage::cBlock::getAlign ( int nBoundary ) const
{
   int nAlign = ( m_pUsed - m_pStart ) % nBoundary;
   return ( nAlign == 0 ) ? 0 : ( nBoundary - nAlign );
}

BOOL cMessage::cBlock::pasteAlign ( int &nBytes )
{
   int nAvailable = BUFFER_BLOCK_SIZE - ( m_pUsed - m_pStart );
   if ( nAvailable < nBytes )
   {
      ZeroMemory( m_pUsed, nAvailable );
      nBytes -= nAvailable;
      m_pUsed += nAvailable;

      return FALSE;
   }

   ZeroMemory( m_pUsed, nBytes );
   m_pUsed += nBytes;
   return TRUE;
}

BOOL cMessage::cBlock::getData ( size_t &nOffset, size_t &nLength, BYTE * &pBuffer ) const
{
   int nAvailable = BUFFER_BLOCK_SIZE - nOffset;

   if ( nAvailable < nLength )
   {
      CopyMemory( pBuffer, m_pStart + nOffset, nAvailable );

      nOffset = 0;
      nLength -= nAvailable;
      pBuffer += nAvailable;

      return FALSE;
   }

   CopyMemory( pBuffer, m_pStart + nOffset, nLength );
   return TRUE;
}

cMessage::cMessage()
{
   // Immediately allocate one block
   m_blocks.push_back ( cBlock() );
}

void cMessage::pasteData ( BYTE *pbData, int nBytes )
{
   while ( !m_blocks.back().pasteData ( pbData, nBytes ) )
      m_blocks.push_back ( cBlock() );
}

void cMessage::pasteAlign ( int nBoundary )
{
   int nBytes = m_blocks.back().getAlign ( nBoundary );
   if ( nBytes != 0 )
   {
      while ( !m_blocks.back().pasteAlign ( nBytes ) )
         m_blocks.push_back ( cBlock() );
   }
}

void cMessage::pasteString ( const char *str )
{
   if( str == NULL )
   {
      // Paste an empty string, 1 char (null) and then dword align
      paste ( 1L );
      return;
   }

   int nLength = lstrlen( str ) + 1;
   paste ( static_cast< short > ( nLength ) );
   pasteData ( reinterpret_cast< BYTE * > ( const_cast< char * > ( str ) ), nLength );
   pasteAlign ( sizeof ( long ) );
}

size_t cMessage::getSize () const
{
   return ( m_blocks.size () - 1 ) * BUFFER_BLOCK_SIZE + m_blocks.back().getUsed ();
}

void cMessage::getData ( size_t nOffset, size_t nLength, BYTE *pBuffer ) const
{
   nOffset %= BUFFER_BLOCK_SIZE;
   for ( cBlockList::const_iterator i = m_blocks.begin() + ( nOffset / BUFFER_BLOCK_SIZE );
      !i->getData ( nOffset, nLength, pBuffer); ++ i );
#if _DEBUG
	//char szUpdate[50];
//	sprintf(szUpdate,"nOffset: %d",nOffset);
//	UpdateConsole((char *)szUpdate);
#endif
}

cMessage::cBlockDispenser::cBlockDispenser ()
: m_hHeap( ::HeapCreate ( 0, 0, 0 ) )
{
}

cMessage::cBlockDispenser::~cBlockDispenser ()
{
   ::HeapDestroy( m_hHeap );
}

BYTE *cMessage::cBlockDispenser::get ()
{
   return reinterpret_cast< BYTE * > ( ::HeapAlloc ( m_hHeap, 0, BUFFER_BLOCK_SIZE ) );
}

void cMessage::cBlockDispenser::release ( BYTE *pBlock )
{
   ::HeapFree ( m_hHeap, 0, pBlock );
}

cMessage::cBlockDispenser cMessage::g_dispenser;