//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd 2002-11  (all rights reserved)
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  Provided no fee is charged (either by the distributer or user)
//  permission to use, copy, modify, and distribute this software and
//  its documentation for any purpose and without fee is hereby granted,
//  provided that the above copyright notice appear in all copies and
//  that both that copyright notice and this permission notice appear
//  in supporting documentation.
//
//  No waranty or suitablity for purpose is implied.
//
//
//  P2PeerMsg implementation
//  NOTES: Container that facilitates the asychronous exchange of
//         messages between P2PeerHub's and subsequent routing through
//         the registered hierachies of derivered P2PeerTarget's
//       : These object's are not designed to be thread or context
//         safe and as such reference MUST be isolated
//
//  Beta release only.
//

#include "StdAfx.h"
#include "P2PeerMsg.h"
#include "P2Pevent.h"


///////////////////////////////////////////////////////////////////////
//  Construction and destruction

UINT g_P2PeerMsgInstances = 0;

//
//  Constructors and destructor
//
//  Parameters:  P2PeerMsg& rhs
//               Copy constructor reference
//
//               const void *pvMessage
//               Complete message, including P2PeerMsgHdr
//
//               short iMsgSize
//               Message size
P2PeerMsg::P2PeerMsg ( )
        // : P3PmsgBSTR ( VBLock_Addr16, 2048 )
{
    // Firstly
    RenderThisSafe ( );
    Init ( 0, AllocBLOB08(sizeof(P2PeerMsgPrefix)) );
}

P2PeerMsg::P2PeerMsg ( const P2PeerMsg& rhs )
         : P3PmsgBSTR( rhs )
{
    // Firstly
    RenderThisSafe ( );
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    pPrefix->uiState &= ~P2PeerMsgState_Posted;
}

P2PeerMsg::P2PeerMsg ( P2PmsgHANDLE  hVBList )
         : P3PmsgBSTR ( hVBList )
{
    // Firstly
    RenderThisSafe ( );
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    pPrefix->uiState &= ~P2PeerMsgState_Posted;
    ASSERT(0);
}

/*P2PeerMsg::P2PeerMsg ( const void *pvMsgImage, P2Psize_t iMsgImageSize )
{
    // Firstly
    RenderThisSafe ( );

    //// Allocate memory
    //// NOTES: This is a special case since the whole message is
    ////        being passed.  Not just the data component
    ASSERT(0);

    // Parallel image
    //P2PmsgBSTR *pBSTR  =(P2PmsgBSTR *)pvMsgImage;
    //m_pP2PmsgBSTR_Heap = MakeBSTR ( pBSTR->iSize );
    //memcpy ( m_pP2PmsgBSTR_Heap, pvMsgImage, iMsgImageSize );

    // Propagation
    //Connect ( m_pP2PmsgBSTR_Heap);
    //SetFragmented ( );
}*/

P2PeerMsg::P2PeerMsg ( const VBListIOmage& oIOmage )
         : P3PmsgBSTR( oIOmage )
{
    // Firstly
    RenderThisSafe ( );
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    pPrefix->uiState &= ~P2PeerMsgState_Posted;
}

P2PeerMsg::P2PeerMsg ( VBListIOmage *pIOmage )
         : P3PmsgBSTR( pIOmage )
{
    // Firstly
    RenderThisSafe ( );
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    pPrefix->uiState &= ~P2PeerMsgState_Posted;
}

P2PeerMsg::P2PeerMsg ( P2PmsgID pszMsgID )
{
    // Firstly
    RenderThisSafe();
    Init ( pszMsgID, AllocBLOB08(sizeof(P2PeerMsgPrefix)) );

    // Addressing
    InitNode ( VBLockBSTR_NET,P3PmsgData() );

    // Data
    InitNode ( VBLockBSTR_MSG,P3PmsgData() );
}

P2PeerMsg::P2PeerMsg ( P2PaddrSTR oSrcSTR, P2PaddrSTR oDstSTR
                     , P2PmsgID pszMsg
                     , const void *pvMsgData, P2Psize_t iMsgDataSize )
{
    // Firstly
    RenderThisSafe();
    Init ( pszMsg, AllocBLOB08(sizeof(P2PeerMsgPrefix)) );

    // Addressing
    P3PmsgNode oNodeAddr = InitNode(VBLockBSTR_NET,P3PmsgData()).r_Object();
    oNodeAddr += P3PmsgField ( TMsg_Src, DataBSTR08(oSrcSTR) );
    oNodeAddr += P3PmsgField ( TMsg_Dst, DataBSTR08(oDstSTR) );

    // Data
    // NOTES: Passed data is placed in the data node header
    if ( iMsgDataSize <= 255 )
      InitNode ( VBLockBSTR_MSG
               , P3PmsgData(pvMsgData,iMsgDataSize,VBLockData_BLOB08) );
    else
      InitNode ( VBLockBSTR_MSG
               , P3PmsgData(pvMsgData,iMsgDataSize,VBLockData_BLOB16) );
}

P2PeerMsg::P2PeerMsg ( const P3PmsgField& oSource, const P3PmsgField& oDestin
                     , P2PmsgID pszMsg
                     , const void *pvMsgData, P2Psize_t iMsgDataSize )
{
    // Firstly
    RenderThisSafe();
    Init ( pszMsg, AllocBLOB08(sizeof(P2PeerMsgPrefix)) );

    // Addressing
    InitNode(VBLockBSTR_NET,P3PmsgData());
    SetSource ( oSource );
    SetDestin ( oDestin );

    // Data
    // NOTES: Passed data is placed in the data node header
    if ( iMsgDataSize <= 255 )
      InitNode ( VBLockBSTR_MSG
               , P3PmsgData(pvMsgData,iMsgDataSize,VBLockData_BLOB08) );
    else
      InitNode ( VBLockBSTR_MSG
               , P3PmsgData(pvMsgData,iMsgDataSize,VBLockData_BLOB16) );
}

P2PeerMsg::P2PeerMsg ( P2PaddrSTR pSrcSTR, P2PaddrSTR pDstSTR
                     , P2PmsgID pszMsg
                     , LPCTSTR lpszMsgFormat, ... )
//         : P2PmsgNode ( pszMsg, P2PmsgVT_STR, lpszMsgFormat, 0 )
{
    // Introduce locals
    va_list       ap;                  // Variable argument list.
    char          szMsgData[2048];
    P2Psize_t      iMsgDataSize = 0;
    va_start ( ap, lpszMsgFormat );
    if ( lpszMsgFormat )
      iMsgDataSize = vsprintf_s ( szMsgData, sizeof(szMsgData), lpszMsgFormat, ap );
    szMsgData[iMsgDataSize++] = 0;
    szMsgData[iMsgDataSize++] = 0;
    va_end   ( ap );

    // Firstly
    RenderThisSafe();
    Init ( pszMsg, AllocBLOB08(sizeof(P2PeerMsgPrefix)) );

    // Addressing
    P3PmsgNode& oNodeAddr = InitNode(VBLockBSTR_NET,P3PmsgData());
    oNodeAddr += P3PmsgField ( TMsg_Src, DataBSTR08(pSrcSTR) );
    oNodeAddr += P3PmsgField ( TMsg_Dst, DataBSTR08(pDstSTR) );

    // Data
    // NOTES: Passed data is placed in the data node header
    if ( iMsgDataSize <= 255 )
      InitNode ( VBLockBSTR_MSG
               , P3PmsgData(szMsgData,iMsgDataSize,VBLockData_BSTR08) );
    else
      InitNode ( VBLockBSTR_MSG
               , P3PmsgData(szMsgData,iMsgDataSize,VBLockData_BSTR16) );
}

P2PeerMsg::~P2PeerMsg ()
{
    // Garbage collection
    delete m_pP2PmsgWrap;
    delete m_pstrVisualSummary;
    g_P2PeerMsgInstances--;
}

void
P2PeerMsg::RenderThisSafe ( )
{
    // Attributes
    m_pP2PmsgWrap        =  0;
    m_pP2PmsgParent      =  0;
    m_pstrVisualSummary  =  0;
    g_P2PeerMsgInstances++;

    // Dedicated VBList
    ASSERT(sizeof(P2PeerMsgPrefix)<255);
    //r_data() = P3PmsgData ( (void *)0, sizeof(P2PeerMsgPrefix), VBLockData_BLOB08 );    
}

void
P2PeerMsg::ResetThisObject ( )
{
    // Garbage collection
    delete m_pP2PmsgWrap;
           m_pP2PmsgWrap = 0;

    // Delegation
    r_node(0).Truncate();

    // Attributes
    m_pP2PmsgParent  = 0;
}

///////////////////////////////////////////////////////////////////////
//  Overloaded Operators

//
//  operator = () overload
//
//  Parameters:  const P2PeerMsg& rhs
//               Right hand side of expression.
//
//  Returns:     P2PeerMsg&
//               Reference to self.
//
P2PeerMsg&
P2PeerMsg::operator = ( const P2PeerMsg& rhs )
{
    // Optimise
    if ( this == &rhs )
      return *this;
    ResetThisObject ( );

    // Snapshot and base class delegation
   (P3PmsgBSTR&)*this = rhs;

    // Tidy up and
    //SetFragmented ( );
    return *this;
}

//
//  operator [] overload
//  Recursively exposes wrapped messages
//
//  Parameters:  int e
//               Index of recursively wrapped message to be exposed
//               NOTES: Throws an exception should recursion level
//                      not exist
//
//  Returns:     P2PeerMsg&
//               Wrapped message reference
//
P2PeerMsg&
P2PeerMsg::operator [] ( int e )
{
    // Self reference
    if ( e == 0 )
      return *this;

    // To be sure, to be sure
    if ( e < 0                   ||
         e > MAX_P2PeerMsg_Index    )
      EVERR->MODULE->AFP(e)
           ->Message("Invalid wrapped message index=%i (0 to %i)", e, MAX_P2PeerMsg_Index )
           ->Group("P2P")->Throw();
    if ( !IsWrapped() )
      EVERR->MODULE->AFP(e)
           ->Message_T("P2PeerMsg not wrapped" )
           ->Advice_T ("Use IsWrapped() as pre-check")
           ->Throw();

    // Unwrapping
    if ( m_pP2PmsgWrap == 0 )
    {
      VBListIOmage *pIOmage = (VBListIOmage *)r_node(VBLockBSTR_WRP).r_data().c_vBlob();
      m_pP2PmsgWrap = new P2PeerMsg ( *pIOmage );
    }

    // Recursively delegate
    return (*m_pP2PmsgWrap)[e-1];
}

///////////////////////////////////////////////////////////////////////
//  Factories
//  NOTES: All methods return pointers to P2PeerMsg objects for which
//         the client becomes responsible for the life cycle

//
//  Constructs a response message from the encapsulated message
//  NOTES: Clones address properties of the contained message then swaps
//         the source and destination addresses.
//
//  Parameters:  P2PmsgID pszMsg
//               Response message type
//
//               const void *pvMsgData
//               Message data
//
//               short iMsgDataSize
//               Message data size.
//
//  Returns:     P2PeerMsg*
//               Response P2PeerMsg pointer.  Client becomes
//               responsible for life cycle.
//
P2PeerMsg*
P2PeerMsg::ResponseFactory ( P2PmsgID pszMsg
                           , const void *pvMsgData, P2Psize_t iMsgDataSize )
{
    // Build the response message
    // NOTES: Perform raw prefix copy
    P2PeerMsgSP spMsg
        = new P2PeerMsg ( r_Destin(), r_Source()
                        , pszMsg
                        , pvMsgData, iMsgDataSize );
ASSERT(!P2PeerMsg_IsPosted(spMsg));

    // Data
    spMsg -> r_data() = r_data();
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)spMsg->r_data().c_vBlob();
    pPrefix->uiState &= ~P2PeerMsgState_Posted;
ASSERT(!P2PeerMsg_IsPosted(spMsg));

    // Tidy up, and
    return spMsg.Dereference();
}

//
//  Description: Builds a redirection message from this message.
//
//
//  Paremeters:  P2PaddrSTR strP2Paddr
//               Redirected P2PeerMsg destination address
//
//
//  Returns:     P2PeerMsg*
//               Pointer to redirection message
//               NOTES: Client becomes responsible for life cycle
//
P2PeerMsg*
P2PeerMsg::RedirectFactory ( P2PaddrSTR strDestin )
{
    // Create an image and assign redirection address.
    P2PeerMsg *pMsg = new P2PeerMsg ( *this );
               pMsg -> SetDestin ( strDestin );
//LPCTSTR lpszSource=pMsg->GetSource();
//LPCTSTR lpszDestin=pMsg->GetDestin();
//LPCTSTR lpszMsg=pMsg->c_name();

    // Then
ASSERT(pMsg->Map_MatchName(c_name()));//DELETE-ME
ASSERT(!IsWrapped()||(*this)[1].Map_MatchName((*pMsg)[1].c_name()));

    return pMsg;
}

P2PeerMsg*
P2PeerMsg::RedirectFactory ( P2PaddrSTR strP2PaddrDst
                           , P2PmsgID pszMsg
                           , const void *pvMsgData, P2Psize_t iMsgDataSize )
{
    // Simply
ASSERT(strlen(pszMsg)>0);//DELETE-ME
    return new P2PeerMsg ( GetSource(), strP2PaddrDst
                         , pszMsg
                         , pvMsgData, iMsgDataSize );
}

//
//  Description: Builds a wrapped response message from the
//               encapsulated message.
//
//
//  Paremeters:  P2Pmsg_t nMsg
//               Type of wrapped response
//
//               void *pvMsgData
//               Data pointer,  Null value handled.
//
//               short iDataSize
//               Size of data area in wrapped message.  Size
//               of wrapped message is exclusive to this value.
//
//
//  Returns:     P2PeerMsg*
//               Pointer to wrapped response message
//
P2PeerMsg*
P2PeerMsg::WrappedResponseFactory ( P2PmsgID pszMsg
                                  , const void *pvMsgData, P2Psize_t iDataSize )
{
    // Build a wrapped message
    // NOTES: Add an extra bit on for the size of this message
    //      : Message source and destinations are reversed.
    P2PeerMsgSP spMsg
        = new P2PeerMsg ( GetDestin(), GetSource()
                        , pszMsg
                        , pvMsgData, iDataSize );
spMsg->AssertValid();
    spMsg -> r_data() = r_data();
    P2PeerMsgPrefix *pPrefix0 = (P2PeerMsgPrefix *)(spMsg->r_data().c_vBlob());
                     pPrefix0 -> uiState &= ~P2PeerMsgState_Posted;
spMsg->AssertValid();
    //spMsg -> InitNode ( VBLockBSTR_WRP, P3PmsgData() );
    //spMsg -> r_node   ( VBLockBSTR_WRP) += r_node ( 0 );

    // Copy in details of the wrapped message
    // TODO:LJM This can be optimised
    PrepareIOmage ( ~(DWORD)0 );
spMsg->AssertValid();
    P3PmsgData oData ( IOmage(), IOmageSize(), VBLockData_BLOB16 );
spMsg->AssertValid();
    ReleaseIOmage ( );
spMsg->AssertValid();
    spMsg -> InitNode ( VBLockBSTR_WRP, oData );
    P2PeerMsgPrefix *pPrefix1 = (P2PeerMsgPrefix *)((*spMsg)[1].r_data().c_vBlob());
                     pPrefix1 -> uiState &= ~P2PeerMsgState_Posted;

spMsg->AssertValid();

    // Copy in details of the wrapped message
    //spMsg -> m_pP2PmsgWrap = new P2PeerMsg ( *this );

    // Then
    return spMsg.Dereference();
}

//
//  Constructs a reflection message from the encapsulated message
//  NOTES: Simply clones a copy of the contained message and swaps
//         the source and destination addresses.
//       : The reflection state of the manufactured message is
//         inverted.  Normal messages are manufactured from reflected
//         messages and reflected state messages are manufactured
//         from normal messages
//
//  Returns:     P2PeerMsg*
//               Pointer to reflection message.  Client becomes
//               responsible for life cycle.
//
P2PeerMsg*
P2PeerMsg::ReflectFactory ( ) const
{
    // Build the response message
    // NOTES: Source and destination addresses are swapped. Content
    //        is preserved
    P2PeerMsgSP spMsg = new P2PeerMsg ( *this );
    spMsg -> SetSource ( r_Destin() );
    spMsg -> SetDestin ( r_Source() );

    // Swap reflected state
    // NOTES: Normal messages become reflected and reflected
    //        messages become normal
    //      : Refer ON_P2PeerMsg and ON_P2PeerMsg_CATCH
    //        for intercepting reflected-reflected (normal) P2PeerMsg's
    //      : Refer ON_P2PeerMsg_REFLECT and ON_P2PeerMsg_REFLECT_CATCH
    //        for intercepting reflected-normal P2PeerMsg's
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)spMsg->r_data().c_vBlob();
    if ( (pPrefix->nMsg&P2P_Reflected) == P2P_Reflected )
      pPrefix -> nMsg ^= P2P_Reflected;
    else
      pPrefix -> nMsg |= P2P_Reflected;

    // Then
    return spMsg.Dereference();
}

//
//  Constructs a loopback message from the encapsulated message
//  NOTES: Simply clones a copy of the contained message and swaps
//         the source and destination addresses.
//       : Under some circumstances P2PeerHub's may exchange the
//         same message (P2PmsgPing or P2PmsgPoll etc) to accomplish
//         a particular outcome
//
//  Returns:     P2PeerMsg*
//               Loopback P2PeerMsg pointer.  Client becomes
//               responsible for life cycle.
//
P2PeerMsg*
P2PeerMsg::LoopbackFactory ( ) const
{
    P2PeerMsgSP spMsg = new P2PeerMsg ( *this );
    spMsg -> SetSource ( r_Destin() );
    spMsg -> SetDestin ( r_Source() );
    return spMsg.Dereference();
}

//
//  Description: Wraps message for reflection in nominated message
//               type
//
//  Parameters:  P2Pmsg_t nMsg
//               Type of message contents are wrapped into.
//               NOTES: This is the outer message that must be
//                      unwrapped at the destination.
//
//               void *pvMsgData
//               Data pointer.  Null value handled.
//
//               short iMsgDataSize
//               Size of message data area.  This is in addition
//               to the wrapped message.
//
//  Returns:     P2PeerMsg*
//               Pointer to unwrapped message.  Client becomes
//               responsible for life cycle.
//
P2PeerMsg*
P2PeerMsg::WrapFactory ( P2PmsgID szMsg
                       , const void *pvMsgData, P2Psize_t iDataSize )
{
    // Build a wrapped message
    // NOTES: Add an extra bit on for the size of this message
    //      : Message source and destinations are reversed.
    P2PeerMsgSP spMsg
        = new P2PeerMsg ( GetDestin(), GetSource()
                        , szMsg
                        , pvMsgData, iDataSize );
    spMsg -> r_data() = r_data();

    // Copy in details of the wrapped message
    // TODO:LJM This can be optimised
    PrepareIOmage ( ~(DWORD)0 );
//UINT nSizeIOmage=this->IOmageSize();
    P3PmsgData oData ( IOmage(), IOmageSize(), VBLockData_BLOB16 );
//UINT nSizeData=oData.Sizeof();
    ReleaseIOmage ( );
    spMsg -> InitNode ( VBLockBSTR_WRP, oData );

    // and simply
    return spMsg.Dereference();
}
//P2PeerMsg*
//P2PeerMsg::WrapFactory ( P2PmsgID pszMsg
//                       , const void *pvMsgData, P2Psize_t iDataSize )
//{
//    // Build a wrapped message
//    // NOTES: Add an extra bit on for the size of this message
//    //      : Message source and destinations are reversed.
//    P2PeerMsg *pMsg
//        = new P2PeerMsg ( m_pP2PmsgBSTR -> nDestinID
//                        , m_pP2PmsgBSTR -> nSourceID
//                        , pszMsg
//                        , pvMsgData, iDataSize );
//    //pMsg -> m_pMsgHeader -> ucPriority 
//    //                    = m_pMsgHeader -> ucPriority;
//    pMsg -> m_pP2PmsgBSTR ->ucPriority 
//                        = m_pP2PmsgBSTR-> ucPriority;
//    //pMsg -> m_pMsgHeader -> dwAddrTag 
//    //                    = m_pMsgHeader -> dwAddrTag;
//    pMsg -> m_pP2PmsgBSTR ->dwAddrTag
//                        = m_pP2PmsgBSTR-> dwAddrTag;
//
//    // Copy in details of the wrapped message
//    //pMsg -> Allocate_( pvMsgData, iDataSize
//    //                 , m_pMsgHeader_, m_pMsgHeader_->iSize );
//    ASSERT(!pMsg->m_pP2PmsgWrap);
//    pMsg -> m_pP2PmsgWrap = new P2PeerMsg ( *this );
//
//    // and simply
//ASSERT(pMsg->Map_MatchName(pszMsg));
//ASSERT(Map_MatchName((*pMsg)[1].GetName()));//DELETE-ME
//    return pMsg;
//}

void
P2PeerMsg::WrapAttach ( P2PeerMsg *pP2Pmsg )
{
    // To be sure, to be sure
ASSERT(0);
    if ( m_pP2PmsgWrap )
      EVERR->MODULE
           ->Message_T("P2Pmsg already contains wrapped P2Pmsg" )
           ->Advice_T ("Bug, (SNHappen)")
           ->Throw();
    m_pP2PmsgWrap = pP2Pmsg;
}

//
//  Unwraps this message to expose the original wrapped message.
//  NOTES: Refer WrapFactory() for wrapping up messages.  Use
//         IsWrapped() to check wrapped status of a message.
//       : Client becomes responsible for life cycle of the
//         unwrapped message returned.
//
//  Returns:     P2PeerMsg*
//               Pointer to unwrapped message.  Client becomes
//               responsible for life cycle.
//                 0.. No wrapped message.
//
P2PeerMsg*
P2PeerMsg::UnwrapFactory ( )
{
    // Optimisation
    if ( m_pP2PmsgWrap )
      return new P2PeerMsg(*m_pP2PmsgWrap);

    // To be sure, to be sure
    if ( !IsWrapped() )
      EVERR->MODULE
           ->Message_T("P2PeerMsg not wrapped" )
           ->Advice_T ("Use IsWrapped() as pre-check")
           ->Throw();

    // Instanciate
    VBListIOmage *pIOmage = (VBListIOmage *)r_node(VBLockBSTR_WRP).r_data().c_vBlob();
    return new P2PeerMsg ( *pIOmage );
}

//
//  Description: Builds an error response from the passed
//               error message
//
//
//  Paremeters:  P2Pevent *pP2Pevent
//               Error message object from which the response
//               is to be constructed
//
//  Returns:     P2PeerMsg*
//               Pointer to error response message.  Client
//               becomes responsible for life cycle.
//
/*P2PeerMsg*
P2PeerMsg::ErrorResponseFactory ( P2Pevent *pP2Pevent )
{
    // Introduce locals
    P2PeerMsg *pMsg  = 0;

    // Build response message
    EventLogData *pEventLogData
               = pP2Pevent -> LogEventialsFactory();
    pMsg = WrappedResponseFactory ( P2Pmsg_Error
                                  , pEventLogData
                                  , pEventLogData->nSize );
    delete pEventLogData;

    // Tidy up and
    return pMsg;
}*/

//
//  Creates P2Pmsg_Exception from encapsulated P2PeerMsg and passed
//  P2Pevent
//  NOTES: Clones routing properties of the contained message and
//         then swaps the source and destination addresses
//       : P2PeerMsg_CATCH handlers are used to intercept posted
//         P2Pmsg_Exceptions
//
//
//  Parameters:  P2Pevent *pEVT
//               Event object from which the exception is to be
//               constructed
//               NOTES: Use the P2PeventFactory() to extract P2Pevent's
//                      from P2Pmsg_Exception's
//
//  Returns:     P2PeerMsg*
//               Exception P2PeerMsg pointer.  Client becomes
//               responsible for life cycle.
//
P2PeerMsg*
P2PeerMsg::ExceptionFactory ( P2Pevent *pEVT )
{
    // Introduce locals
    P2PeerMsg *pMsg;

    // Build response message
    pMsg = WrappedResponseFactory ( P2Pmsg_Exception
                                  , 0, 0 );
    pMsg -> AttachP2Pevent ( pEVT );

    // Tidy up and
    ASSERT(pMsg->Map_MatchName(P2Pmsg_Exception));//DELETE-ME
    ASSERT(pMsg->IsWrapped());//DELETE-ME
    ASSERT(AfxCheckMemory());
    ASSERT(Map_MatchName( (*pMsg)[1].c_name()));
    return pMsg;
}

///////////////////////////////////////////////////////////////////////
//  P2Pevents

//      P2Pevent*
//        UnwrapP2Pevent( );

//  Attaches copy of passed P2Pevent to message
//  NOTES: Any existing P2Pevent is displaced by passed P2Pevent
//
//  Parameters: P2Pevent *pEvent
//              Attached event
//
//              LPCTSTR lpszFieldName = 0
//              Name of node under which event is attached
//
void
P2PeerMsg::AttachP2Pevent ( const P2Pevent *pEVT
                          , LPCTSTR lpszFieldname )
{
    // Field confirmation
    if ( lpszFieldname    == 0 ||
         lpszFieldname[0] == 0    )
      lpszFieldname = _T("P2Pevent");

    // Events node may not exist
    if ( !Exists(VBLockBSTR_EVT) )
      InitNode ( VBLockBSTR_EVT, P3PmsgData() );

    // Attachment
    if ( r_node(VBLockBSTR_EVT).Exists(lpszFieldname) )
      r_node(VBLockBSTR_EVT)[lpszFieldname] = *pEVT;
    else
      r_node(VBLockBSTR_EVT) += *pEVT;
}

//
//  Extracts copy of P2Pevent from message
//  NOTES: Refer complimentary AttachP2Pevent() for attachment details
//
//
//  Parameters: LPCTSTR lpszFieldName = 0
//              Name of field from which P2Pevent is to be extracted
//
//  Returns:    P2Pevent*
//              Extracted instance, client becomes responsible for
//              life cycle
//                0.. Nothing attached
P2Pevent*
P2PeerMsg::ExtractP2Pevent( LPCTSTR lpszFieldname )
{
    // Node confirmation
    if ( lpszFieldname    == 0 ||
         lpszFieldname[0] == 0    )
      lpszFieldname = _T("P2Pevent");

    // Node extraction
    if ( !Exists(VBLockBSTR_EVT)                       ||
         !r_node(VBLockBSTR_EVT).Exists(lpszFieldname)    )
      return 0;

    // Manufacture
    // NOTES: Client responsible for life cycle
    return new P2Pevent(r_node(VBLockBSTR_EVT).SelectNode(lpszFieldname));
}

///////////////////////////////////////////////////////////////////////
//  P2PeerMsg_MAP routing
//  NOTES: Used for P2PeerMsg routing and handler assignment

//
//  Description: Matches P2PeerMsg state against passed filter
//
//
//  Parameters:  USHORT uiMsgStateWildbits
//               Bit mask filter of allowable states
//
//
//  Returns:     bool
//               Filtered comparison result
//                 true... Matched
//                 false.. Failed to match
//
bool
P2PeerMsg::Map_MatchState( USHORT uiMsgStateFilter )
{
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    bool bStateMsg =     pPrefix -> nMsg&P2P_Reflected ? true : false;
    bool bStateFlt =    uiMsgStateFilter&P2P_Reflected ? true : false;

    // Implementation
    return bStateMsg == bStateFlt ? true : false;
}

//
//  Description: 
//
//
//  Parameters:  const char *pszMsgWildcard
//               Message wildcard
//
//  Returns:     bool
//                 true... Message matches wildcard
//                 false.. No match
//
bool
P2PeerMsg::Map_MatchName ( const char *pszMsgWildcard ) const
{
    // Preparation
    bool        star   = false;
    const char *pszMsg = c_name ( );

    // Implmentation
Top:
    const char *p, *s;
    for ( s = pszMsg, p = pszMsgWildcard; *s; ++s, ++p )
    {
      switch (*p) {
         case '?':
            if (*s == '.') goto starCheck;
            break;
         case '*':
            star = TRUE;
            pszMsg = s, pszMsgWildcard = p;
            if (!*++pszMsgWildcard) return TRUE;
            goto Top;
         default:
            //if (mapCaseTable[*s] != mapCaseTable[*p])
            if (*s != *p )
               goto starCheck;
            break;
      } /* endswitch */
   } /* endfor */
   if (*p == '*') ++p;
   return (!*p);

starCheck:
   if (!star) return FALSE;
   pszMsg++;
   goto Top;
}

void
P2PeerMsg::AssertValid ( ) const
{
    // Firstly delegate
    __super::AssertValid ( );
}

void
P2PeerMsg::Print ( FILE *fd )
{
    // Indentation
    int nDepth = GetDepth ( );
    for ( int i = 0; i < nDepth; i++ )
      fprintf ( fd, "  " );

    // Encode message header
    fprintf ( fd, "P2PeerMsg[%i] {\n", nDepth );
    //m_oBSTR.Print ( fd, 1+nDepth );
    __super::Print ( fd, 1+nDepth );
    if ( IsWrapped() )
      (*this)[1].Print ( fd );

    // Tidy up
    for ( int i = 0; i < nDepth; i++ )
      fprintf ( fd, "  " );
    fprintf ( fd, "}\n" );
}

//
//  Generates Visual Runtime Summary for P2PeerMsg instance
//  FORMAT: [P2PeerMsg Name=MsgName, Src=MsgSource, Dst=MsgDestin]
//        : Such brief summaries are used to pass brief P2PeerMsg state
//          information to P2Pevent's
//
//
//  Returns:     LPCTSTR
//               Pointer to encoded P2PeerMsg summary
//               NOTES: (Re-)Encoded upon each reference
//
LPCTSTR
P2PeerMsg::GetVisualRTSummary ( )
{
    // Container
    if ( !m_pstrVisualSummary )
      m_pstrVisualSummary = new CString ( );

    // Simply encode header
    m_pstrVisualSummary
      -> Format ( "[Name=%s, Source=%s, Destin=%s]"
                , c_name()
                , GetSource()
                , GetDestin() );

    // Tidy up and
    return *m_pstrVisualSummary;
}

//
//  Generates Visual Runtime Summary for P2PeerMsg instance
//  FORMAT: [P2PeerMsg Name=MsgName, Src=MsgSource, Dst=MsgDestin]
//        : Such brief summaries are used to pass brief P2PeerMsg state
//          information to P2Pevent's
//
//  Parameters:  LPCTSTR lpszVar
//               Name assigned to the generated P3PmsgNode
//
//  Returns:     LPCTSTR
//               Pointer to encoded P2PeerMsg summary
//               NOTES: (Re-)Encoded upon each reference
//
const P3PmsgNode&
P2PeerMsg::SetP2PeventFParams ( LPCTSTR lpszVar )
{
    // Create a placeholder for receipt of P2PeerMsg details
    P3PmsgNode& oNodeVar = ((P2PeerMsg *)this)->r_node ( VBLockBSTR_ROOT );

    // Tidy up and
    return oNodeVar;
}

///////////////////////////////////////////////////////////////////////
//  Properties

//
//  Get message source P2PaddrSTR
//
//  Returns:     P2PaddrSTR
//               Source address
//
P2PaddrSTR
P2PeerMsg::GetSource ( ) const
{
    // May not exist
    P3PmsgNode& oNodeAddr = ((P3PmsgBSTR&)*this).r_node(VBLockBSTR_NET);
    //return oNodeAddr.Select(TMsg_Src)).c_str(strP2PaddrNULL);
    if ( oNodeAddr.Exists(TMsg_Src) )
      return oNodeAddr[TMsg_Src].c_str();
    return strP2PaddrNULL;
}

//
//  Sets message source P2PaddrSTR
//
//  Parameters:  P2PaddrSTR strSource
//               New source address
//
//  Returns:     P2PaddrSTR
//               Adopted source address
//
P2PaddrSTR
P2PeerMsg::SetSource ( P2PaddrSTR strSource )
{
    // Network node may not exist
    if ( !Exists(VBLockBSTR_NET) )
      InitNode ( VBLockBSTR_NET, P3PmsgData() );

    // Field may not exist
    P3PmsgNode& oNodeAddr = r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists(TMsg_Src) )
      oNodeAddr[TMsg_Src].r_data() = DataBSTR08(strSource);//TODO:LJM could be optimised
    else
      oNodeAddr += P3PmsgField ( TMsg_Src, DataBSTR08(strSource) );
    return oNodeAddr[TMsg_Src].c_str();
}

P3PmsgField&
P2PeerMsg::SetSource ( const P3PmsgField& oSource )
{
    // Network node may not exist
    if ( !Exists(VBLockBSTR_NET) )
      InitNode ( VBLockBSTR_NET, P3PmsgData() );

    // Field may not exist
    P3PmsgNode& oNodeAddr = r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists(TMsg_Src) )
      oNodeAddr[TMsg_Src].r_data() = oSource;
    else
      oNodeAddr += P3PmsgField ( TMsg_Src, oSource.r_data() );

    // Attributes
    P3PmsgField& oSrc = oNodeAddr[TMsg_Src];
    if ( oSource.IsAttributed() )
      oNodeAddr.SelectItem(TMsg_Src).r_Attr() = ((P3PmsgField&)oSource).r_Attr();
    else if ( oSrc.IsAttributed() )
      oSrc.r_Attr().Drop();

    // Stacking
    if ( oSource.IsStacked() )
    {
      oSrc.r_Stck() = oSource.r_Stck();
      oSrc.r_Stck().Rename ( TMsg_Src );
    }
    else if ( oSrc.IsStacked() )
      oSrc.r_Stck().Drop();

    // Tidy up, and
    return oSrc;
}
P3PmsgField&
P2PeerMsg::r_Source ( ) const
{
    return ((P2PeerMsg *)this)->r_node(VBLockBSTR_NET)[TMsg_Src];
}

//
//  Manages destination P2PaddrSTR
//  NOTES: Destination address of this P2PeerMsg.  Specified address
//         may be subsequently qualified via the ConMap address
//
//  Returns:     P2PaddrSTR
//               Destination address
//
P2PaddrSTR
P2PeerMsg::GetDestin ( ) const
{
    // Simply
    P3PmsgNode& oNodeAddr = ((P3PmsgBSTR&)*this).r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists(TMsg_Dst) )
      return oNodeAddr[TMsg_Dst].c_str();
    return strP2PaddrNULL;
}
P2PaddrSTR
P2PeerMsg::SetDestin ( P2PaddrSTR strDestin )
{
    // Network node may not exist
    if ( !Exists(VBLockBSTR_NET) )
      InitNode ( VBLockBSTR_NET, P3PmsgData() );

    // May not exist
    P3PmsgNode& oNodeAddr = r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists(TMsg_Dst) )
      oNodeAddr[TMsg_Dst].r_data() = DataBSTR08(strDestin);//TODO:LJM could be optimised
    else
      oNodeAddr += P3PmsgField ( TMsg_Dst, DataBSTR08(strDestin) );
    return oNodeAddr[TMsg_Dst].c_str();
}
P3PmsgField&
P2PeerMsg::SetDestin ( const P3PmsgField& oDestin )
{
    // Network node may not exist
    if ( !Exists(VBLockBSTR_NET) )
      InitNode ( VBLockBSTR_NET, P3PmsgData() );

    // Field may not exist
    P3PmsgNode& oNodeAddr = r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists(TMsg_Dst) )
      oNodeAddr[TMsg_Dst].r_data() = oDestin;
    else
      oNodeAddr += P3PmsgField ( TMsg_Dst, oDestin.r_data() );

    // Attributes
    P3PmsgField& oDst = oNodeAddr[TMsg_Dst];
    if ( oDestin.IsAttributed() )
      oNodeAddr.SelectItem(TMsg_Dst).r_Attr() = ((P3PmsgField&)oDestin).r_Attr();
    else if ( oDst.IsAttributed() )
      oDst.r_Attr().Drop();

    // Stacking
    if ( oDestin.IsStacked() )
    {
      oDst.r_Stck() = oDestin.r_Stck();
      oDst.r_Stck().Rename ( TMsg_Dst );
    }
    else if ( oDst.IsStacked() )
      oDst.r_Stck().Drop();

    // Tidy up, and
    return oDst;
}
P3PmsgField&
P2PeerMsg::r_Destin ( ) const
{
    return ((P2PeerMsg *)this)->r_node(VBLockBSTR_NET)[TMsg_Dst];
}

//
//  Manages destination connect map P2PaddrSTR
//  NOTES: Specified address will be qualified via this ConMap
//         at an internetwork boundary
//
//  Returns:     P2PaddrSTR
//               Destination address
//
P2PaddrSTR
P2PeerMsg::GetConmap ( )
{
    // Simply
    P3PmsgNode& oNodeAddr = ((P3PmsgBSTR&)*this).r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists("Map") )
      return oNodeAddr["Map"].c_str();
    return strP2PaddrNULL;
}
P2PaddrSTR
P2PeerMsg::SetConmap ( P2PaddrSTR strConmap )
{
    // Network node may not exist
    if ( !Exists(VBLockBSTR_NET) )
      InitNode ( VBLockBSTR_NET, P3PmsgData() );

    // May not exist
    P3PmsgNode& oNodeAddr = r_node(VBLockBSTR_NET);
    if ( oNodeAddr.Exists("Map") )
      oNodeAddr["Map"].r_data() = DataBSTR08(strConmap);//TODO:LJM could be optimised
    else
      oNodeAddr += P3PmsgField ( "Map", DataBSTR08(strConmap) );
    return oNodeAddr["Map"].c_str();
}


//
//  Sets message data
//
//
//  Parameters:  P2Pmsg_t nMsg
//               Message type
//
//               const void *pvData
//               Message data
//
//               P2Psize_t iSize
//               Size of message data
//
//  Returns:     P2Psize_t
//               Total message size
//
P2Psize_t
P2PeerMsg::SetData ( P2PmsgID pszMsg
                   , const void *pvData, P2Psize_t iDataSize )
{
    // Persist and
    r_name().c_name ( pszMsg );
    r_node(VBLockBSTR_MSG).c_vBlob( pvData, iDataSize );
    return Sizeof ( );
}

//
//  Sets message priority
//
//  Returns:     UCHAR
//               Message priority
//
UCHAR
P2PeerMsg::Priority ( )
{
    // Simply
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    return pPrefix -> ucPriority;
}

//
//  Sets message priority
//
//  Parameters:  UCHAR ucPriority
//
//  Returns:     UCHAR
//               New message priority
//
UCHAR
P2PeerMsg::SetPriority ( UCHAR ucPriority )
{
    // Simply
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    return pPrefix -> ucPriority = ucPriority;
}

//
//  Description: Exposes message data pointer
//
//
//  Returns:     char*
//               Message data pointer
//
char*
P2PeerMsg::Data ( ) const
{
    // Simply
    return (char *)((P3PmsgBSTR&)*this).r_node(VBLockBSTR_MSG).c_vBlob();
}

//
//  Description: Exposes message data size
//
//
//  Returns:     short
//               Size of message data
//
short
P2PeerMsg::DataSize ( )
{
    // Simply
    return r_node(VBLockBSTR_MSG).r_data().c_size();
    //return P2PmsgData::Sizeof ( );
    //return m_pMsgHeader_->iSizeData;
}

//
//  Description: Exposes address tag field from the message header
//               NOTES: Refer SetAddrTag() method for further
//                      details
//
//
//  Returns:     DWORD
//               User data field contents
//
DWORD
P2PeerMsg::AddrTag ( )
{
    // Simply
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    return pPrefix -> dwAddrTag_;
}

//
//  Description: Sets user defined source address tag in header
//               NOTES: This P2PeerMsgHdr field should always be
//                      included as part of any P2PeerMsg response
//                    : Upon receipt of a P2PeerMsg response this
//                      tag may then be used identify the originating
//                      sequence
//
//
//  Parameters:  DWORD dwAddrTag
// 
//
//  Returns:     DWORD
//               Adopted user addr tag
//
DWORD
P2PeerMsg::SetAddrTag ( const void *pvTagData, P2Psize_t iSize )
{
    // May not exist
    P3PmsgNode& oNodeAddr = r_node(VBLockBSTR_NET);
    //TODO:LJM activate oNodeAddr.DeclareField ( TMsg_Tag, P3PmsgData(pvTagData,iSize) );

    // Simply
    return 0;
}

//
//  Summarises wrapped status of message
//
//  Returns:     bool
//               Wrapped summary for message
//                 true... Wrapped message.
//                 false.. Not a wrapped message.
//
bool
P2PeerMsg::IsWrapped ( )
{
    if ( m_pP2PmsgWrap )
      return true;
    return Exists(VBLockBSTR_WRP);
}

//
//  Description: Exposes reflection status of message
//
//
//  Returns:     bool
//               Reflection suumary for message
//                 true... Reflected message
//                 false.. Non-reflected message
//
bool
P2PeerMsg::IsReflected ( )
{
    // Simply
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)r_data().c_vBlob();
    return ((pPrefix->nMsg&P2P_Reflected)==P2P_Reflected) ? true : false;
}

P2Psize_t
P2PeerMsg::Sizeof ( ) const
{
    // P2PmsgBSTR component
    P2Psize_t iSizeof = __super::Sizeof();

    // Wrapped messges
    if ( m_pP2PmsgWrap )
      iSizeof += m_pP2PmsgWrap -> Sizeof();
    return iSizeof;
}

int
P2PeerMsg::GetDepth ( ) const
{   
    if ( m_pP2PmsgParent )
      return  m_pP2PmsgParent -> GetDepth() + 1;
    return 0;
}

///////////////////////////////////////////////////////////////////////
//  Property exposure

LPCTSTR
P2PeerMsg::c_name ( ) const
{
    return ((P3PmsgBSTR*)this)->r_name().c_name();
}

///////////////////////////////////////////////////////////////////////
//  Support functions

//
//  Swaps P2PeerMsg source and destination addresses
//  NOTES: Swapping addresses facilitates routing of P2PeerMsg's back
//         to their source.
//       : Optimised by swapping names only
//
//  Parameters:  P2PeerMsg *pMsg
//               Message for which the source a.
//
//  Returns:     P2PeerMsg*
//               Original P2PeerMsg pointer (chaining etc)
//
P2PeerMsg*
P2PeerMsg_DstSwapSrc ( P2PeerMsg *pMsg )
{
    P3PmsgNode oNodeNET = pMsg->r_node(VBLockBSTR_NET).r_Object();

    // Source becomes destination and destination becomes source
    // NOTES: Swapping raw "Src" and "Dst" names preserves subsequent routing
    //        details
    P3PmsgField oFieldSrc = oNodeNET.SelectObject(TMsg_Src);
    P3PmsgField oFieldDst = oNodeNET.SelectObject(TMsg_Dst);
                oFieldSrc.r_name().c_name(TMsg_Dst);
                oFieldDst.r_name().c_name(TMsg_Src);

    // Tidy up, and
    return pMsg;
}

//
//  Sets the reflected state of a message
//  NOTES: Use ON_P2PeerMsg_REFLECT() to intercept reflected and
//         ON_P2PeerMsg() to intercept normal equivalents
//
//  Parameters:  P2PeerMsg *pMsg
//               Message for which reflected state is to be adjusted
//
//               bool bReflected
//                 true... ON_P2PeerMsg_REFLECT() state
//                 false.. ON_P2PeerMsg() state
//
//  Returns:     P2PeerMsg*
//               Original P2PeerMsg pointer (chaining etc)
//
P2PeerMsg*
P2PeerMsg_SetReflected ( P2PeerMsg *pMsg, bool bReflected )
{
    // Simply
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)pMsg->r_data().c_vBlob();
    if ( bReflected )
      pPrefix -> nMsg |=  P2P_Reflected;
    else
      pPrefix ->nMsg  &= ~P2P_Reflected;

    // Tidy up, and
    return pMsg;
}

//
//  Performs generic wildcard pattern matching
//
//
//  Parameters:  P2PmsgID strMsgWildcard
//               Message wildcard
//
//               P2PmsgID strMsgName
//               Message name to be matched against wildcard
//
//  Returns:     bool
//                 true... Message matches wildcard
//                 false.. No match
//
bool
Wildcard ( P2PmsgID strMsgWildcard, P2PmsgID strMsgName )
{
    // Locals
    BOOL bStar = FALSE;

    // Implmentation
TOP:P2PmsgID p, s;
    for ( s = strMsgName, p = strMsgWildcard; *s; ++s, ++p )
    {
      switch (*p)
      {
         case _T('?'):
            if ( *s == _T('.') )
            {
              goto starCheck;
            }
            break;
         case _T('*'):
            bStar = TRUE;
            strMsgName = s, strMsgWildcard = p;
            if ( !*++strMsgWildcard )
              return TRUE;
            goto TOP;
         default:
            //if (mapCaseTable[*s] != mapCaseTable[*p])
            if ( *s != *p )
              goto starCheck;
            break;
      }
   }
   if ( *p == _T('*') ) ++p;
   return (!*p);

starCheck:
   if ( !bStar ) return FALSE;
   strMsgName++;
   goto TOP;
}

//
//  Summarises posted status
//
//  Parameters:  P2PeerMsg *pMsg
//               Message whose posted status is to be summarised
//
//  Returns:     BOOL
//                 true... Posted
//                 false.. Not posted
BOOL
P2PeerMsg_IsPosted ( const P2PeerMsg *pMsg )
{
    P2PeerMsgPrefix *pPrefix = (P2PeerMsgPrefix *)((P2PeerMsg *)pMsg)->r_data().c_vBlob();
    return (pPrefix->uiState&P2PeerMsgState_Posted) == P2PeerMsgState_Posted ? TRUE : FALSE;
}