//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2002-2009
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//
//  Description: P2PeerConDmx implementation
//               NOTES: Manages P2Peer RS232 connections
//

#include "StdAfx.h"
//#include "P2PTL.h"
#include "Kernel32_Ext.h"
#include "P2PeerConDmx.h"
#include "P2PeerioDmx.h"
#include "P2Pwin32.h"

///////////////////////////////////////////////////////////////////////
//  Constructors and destructor

CListP2PeerConDmx g_oCListP2PeerConDmx;
CRITICAL_SECTION  g_oCSectP2PeerConDmx;

P2PeerConDmx::P2PeerConDmx ( )
{
    // Firstly
    RenderThisSafe ( );
}

//
//  Parameters:  P2PaddrSTR strThatP2Paddr
//               Identification address for that, or the other end
//
//               LPCTSTR lpszPipename
//               Pipe name
//
P2PeerConDmx::P2PeerConDmx ( P2PaddrSTR pThatP2PaddrSTR
                           , LPCTSTR strServiceName
                           , P2PeerConMode_e eMode
                           , P2Peerio *pP2Peerio )
            : P2PeerCon ( pThatP2PaddrSTR, pP2Peerio )
{
    // Firstly
    RenderThisSafe();
    m_sServiceName   = strServiceName;
    //m_oThisP2Paddr   =    strThisP2Paddr;
    //m_oThatP2Paddr   =    strThatP2Paddr;
    m_eP2PeerConMode =    eMode;
}

//
//               short nIPort
//               Listening port
//
P2PeerConDmx::P2PeerConDmx ( P2PaddrSTR pThisP2PaddrSTR
                           , P2PeerConMode_e eMode
                           , P2Peerio *pP2Peerio )
             : P2PeerCon ( pThisP2PaddrSTR, pP2Peerio )
{
    // Firstly
    RenderThisSafe();
    //m_oThisP2Paddr   =    strThisP2Paddr;
    //m_oThatP2Paddr   =    strThisP2Paddr;
    m_eP2PeerConMode =    eMode;
}

P2PeerConDmx::~P2PeerConDmx ( )
{
    // Resource collection
    Drop ( 0 );
}

void
P2PeerConDmx::RenderThisSafe()
{
    // Attributes
    m_pConThat   = 0;

    // Resources
    static bool s_bInitialised = false;
    if ( !s_bInitialised )
      InitializeCriticalSection ( &g_oCSectP2PeerConDmx );
    s_bInitialised = true;
}

//
//  Description: Splits listening connection into listening and
//               accepted P2PeerCon'nections
//               NOTES: Derived classes must delegate
//
//
//  Parameters:  P2PeerCon **pConListen = 0
//               Previously manufactured listening instance
//
//               P2PeerCon **pConAccept = 0
//               Previously manufactured accept instance
//
/*void
P2PeerConDmx::AcceptSplit ( P2PeerCon **ppConListen
                          , P2PeerCon **ppConAccept )
{
    // Locals
    P2PeerConDmx *pConListen = (P2PeerConDmx *)*ppConListen;
    P2PeerConDmx *pConAccept = (P2PeerConDmx *)*ppConAccept;
    if ( pConListen==0&&pConAccept==0)
      return;

    // Listen
    // NOTES: This object by default. Pointer to this objects resides
    //        in s_oCListP2PeerConHub and as such state must be preserved
    //      : Block any further accepted connections until this one
    //        processed
    if ( !pConListen )
      pConListen = this;
    if (  pConListen->m_pOVERLAPPEDaccept )
      pConListen -> m_pOVERLAPPEDaccept
         = pConListen -> DropOVERLAPPED( pConListen->m_pOVERLAPPEDaccept );

    // Accept
    // NOTES: Mandatory P2PeerConPlc integration
    if ( !pConAccept )
    {
       pConAccept  = new P2PeerConDmx ( );
      *this += pConAccept;
    }

    // Settlement
    // NOTES: Accept removes the allocated socket
    P2PeerCon::AcceptSplit ( (P2PeerCon **)&pConListen
                           , (P2PeerCon **)&pConAccept );
    pConAccept -> m_pConThat   = pConListen -> m_pConThat;
                                 pConListen -> m_pConThat = 0;
    pConAccept -> m_hFile      = pConListen -> m_hFile;
                                 pConListen -> m_hFile = 0;
    pConAccept -> m_hFileCPort = pConListen -> m_hFileCPort;
                                 pConListen -> m_hFileCPort = 0;
    pConAccept -> m_pConThat -> m_pConThat = pConAccept;

    // Simply
    // NOTES: Operational ready
    *ppConListen = pConListen;
    *ppConAccept = pConAccept;
}*/

P2PeerConDmx*
P2PeerConDmx::ServiceFactory( P2PaddrSTR pThatP2PaddrSTR
                            , LPCTSTR strServiceName )

{
    // Locals
    P2PeerConDmx *pCon = new P2PeerConDmx ( );

    // Attributes
    //pCon -> m_oThisP2Paddr   = pThisP2PaddrSTR;
    pCon -> m_oThatP2Paddr   = pThatP2PaddrSTR;// | L4Cbit;
    pCon -> m_sServiceName   = strServiceName;
    pCon -> m_eP2PeerConMode = P2PeerCon_SERVICE;

    // Protocol
    pCon -> SetP2Peerio ( new P2PeerioDmx( ) );

    // Done
    return pCon;
}

P2PeerConDmx*
P2PeerConDmx::ClientFactory ( P2PaddrSTR pThatP2PaddrSTR
                            , LPCTSTR strServiceName )

{
    // Locals
    P2PeerConDmx *pCon = new P2PeerConDmx ( );

    // Attributes
    //pCon -> m_oThisP2Paddr   = pThisP2PaddrSTR;
    pCon -> m_oThatP2Paddr   = pThatP2PaddrSTR;
    pCon -> m_sServiceName   = strServiceName;
    pCon -> m_eP2PeerConMode = P2PeerCon_CLIENT;

    // Protocol
    pCon -> SetP2Peerio ( new P2PeerioDmx( ) );

    // Done
    return pCon;
}

P2PeerCon*
P2PeerConDmx::AcceptSpawn ( P2PeerCon *pConSpawn )
{
    // Service
    // NOTES: Pointer to this object resides in s_oCListP2PeerConHub
    //        and as such state must be preserved
    //      : Block any further accepted connections until this one
    //        processed
    if ( m_pOVERLAPPEDaccept )
      m_pOVERLAPPEDaccept = DropOVERLAPPED( m_pOVERLAPPEDaccept );

    // Instanciation etc
    // NOTES: Actual object that manages the connection
    if ( pConSpawn == NULL )
      pConSpawn = new P2PeerConDmx ( );

    // Settlement
    // NOTES: Posts object for subsequent connection processing in 
    //        response to posted OVERLAPPED IO objects
    P2PeerCon::AcceptSpawn ( pConSpawn );
    P2PsafeCS     oSafeCS  = g_oCSectP2PeerConDmx;
    P2PeerConDmx *pConThis = (P2PeerConDmx *)pConSpawn;

    pConThis -> m_pConThat   = m_pConThat;
                               m_pConThat = 0;
    pConThis -> m_hFile      = m_hFile;
                               m_hFile = 0;
    pConThis -> m_hFileCPort = m_hFileCPort;
                               m_hFileCPort = 0;

    if ( pConThis->m_pConThat )
    {
      pConThis -> m_pConThat -> m_pConThat   = pConThis;
      pConThis -> m_pConThat -> m_hFile      = (HANDLE)pConThis->GetP2Peerio();
      pConThis -> m_pConThat -> m_hFileCPort = (HANDLE)pConThis;
ASSERT(pConThis==pConThis->m_pConThat->m_pConThat);
    }

    // Done
    // NOTES: Spawned object is free floating
    return pConSpawn;
}

///////////////////////////////////////////////////////////////////////
//  IOCP Integration
//

//
//  Description: Processes IO completion status
//               NOTES:
//
//
//  Parameters:  DWORD dwError
//               Error code associated with operation
//               
//               DWORD dbBytes
//               Bytes transferred during completed I/O operation.
//
//               OVERLAPPEDcon pOVERLAPPEDcon
//               Address of OVERLAPPEDcon structure that was specified
//               for the I/O operation
//
/*bool
P2PeerConDmx::On_QueuedCompletionStatus ( DWORD dwError
                                         , DWORD dwBytes
                                         , OVERLAPPEDcon *pOVERLAPPEDcon )
{
    // Pre-processing
    HRESULT hr = dwError;
    if ( hr == S_OK )
      hr = pOVERLAPPEDcon -> hr;

    // Because this is all problematic we
    try
    {
      // Accept
      // NOTES: Perform On_P2PeerCon_ACCEPT() notification.  At which
      //        point the intercepting handler will initiate state
      //        changes.
      //      : Perform On_P2PeerCon_LISTEN() notification.  At which
      //        point the intercepting handler will listen for another
      //        connection
      if ( pOVERLAPPEDcon == m_pOVERLAPPEDaccept )
      {
        // Success
        // NOTES: Honour expectation that the On_P2PeerCon_ACCEPT()
        //        processed before the ON_P2PeerCon_LISTEN()
        //        notification
        releaseOVERLAPPED ( pOVERLAPPEDcon );
        if (   hr == S_OK &&
             m_hFileCPort    )
        {
          if ( m_pOVERLAPPEDsend == NULL )
            m_pOVERLAPPEDsend = MakeOVERLAPPED ( );
          if ( m_pOVERLAPPEDrecv == NULL )
            m_pOVERLAPPEDrecv = MakeOVERLAPPED ( MAX_P2Psize );

          // ON_P2PeerOLD_ACCEPT() notification
          // NOTES: Spawn accepted connection
          PostP2Pmsg ( m_oThisP2Paddr, CN_P2PeerCon, P2P_Accept
                     , ExtractAccept(), 0 );

          // ON_P2PeerCon_LISTEN() notification
          // NOTES: Listen for new connection
          PostP2Pmsg ( m_oThisP2Paddr, CN_P2PeerCon, P2P_Listen
                     , this, 0 );
        }

        // Failure
        // NOTES: Accepted connection failure
        else if (   hr         &&
                  m_hFileCPort    )
          EVERR->Module (__FUNCTION__"[%s-%s]"
                        , EncodeP2PeerID(m_oThisP2Paddr)
                        , EncodeP2PeerID(m_oThatP2Paddr) )
               ->Message("Overlapped accept failed")
               ->HResult( hr ) -> Throw();

        // Cancellation
        // NOTES: IoCancel(m_hFileCPort) and CloseHandle(m_hFileCPort)
        //        issued
        else
          m_pOVERLAPPEDaccept = DropOVERLAPPED ( pOVERLAPPEDcon );
      }

      // Connect
      // NOTES: Perform On_P2PeerCon_CONNECT() notification.  At which
      //        point the intercepting handler will initiate the
      //        appropriate state changes.
      else if ( pOVERLAPPEDcon == m_pOVERLAPPEDconnect )
      {
        releaseOVERLAPPED ( pOVERLAPPEDcon );
        if ( hr )
          EVERR->Module  (__FUNCTION__"(%s-%s)"
                         , EncodeP2PeerID(m_oThisP2Paddr)
                         , EncodeP2PeerID(m_oThatP2Paddr) )
               ->Message ("Overlapped ConnectEx failed")
               ->WSAGROUP( hr ) -> Throw();

        // On_P2PeerCon_CONNECT() notification
        // NOTES: Send() and Recv() buffers required beyond this point
        if ( m_pOVERLAPPEDsend == NULL )
          m_pOVERLAPPEDsend = MakeOVERLAPPED (  );
        if ( m_pOVERLAPPEDrecv == NULL )
          m_pOVERLAPPEDrecv = MakeOVERLAPPED ( MAX_P2Psize );

        PostP2Pmsg  ( m_oThatP2Paddr, CN_P2PeerCon, P2P_Connect
                    , this, (P2PeerMsg *)0 );
      }

      // Delegate
      else
        P2PeerCon::On_QueuedCompletionStatus ( dwError, dwBytes, pOVERLAPPEDcon );
    }

    // Exceptions
    // NOTES: Notification will eventually be routed through to
    //        ON_P2PeerCon_CLOSE() handler. At which point Restart()
    //        may be used on non accepted sockets or this object
    //        conDROP'ed
    //      : ON_P2PeerCon_CLOSE() handler to which this posted object
    //        is routed MUST cancel the attached event.  OnClose()
    //        contains such default processing.
    //      : P2P_Close notification MUST be performed once per
    //        attached event. Subsequent P2P_Close notifications are
    //        blocked until the original event is cancelled.
    catch ( P2Pevent *pEVENT )
    {
      Drop ( pEVENT->Isolate() );
    }

    // Tidy up and
    return true;
}*/


//
//  Description: Drops the encapsulated connection and performs the
//               appropriate ON_P2PeerCon_CLOSE() notification
//               NOTES: Should an EVENT already be attached the passed
//                      EVENT is simply cancelled.
//
//
//  Parameters:  P2Pevent *pEVENT
//               Precipitating event.  May be NULL
//               NOTES: Control assumed over life cycle.
//
void
P2PeerConDmx::Drop ( P2Pevent *pEVENT )
{
    // Firstly tidy up other end
    // NOTES: Cancels all outstanding overlapped IO
    //if ( m_pConThat )
    {
      // Isolation
      P2PsafeCS oSafeCS = g_oCSectP2PeerConDmx;

      // Outstanding OVERLAPPED IO for this object
      if ( m_pOVERLAPPEDrecv          &&
           m_pOVERLAPPEDrecv->bQueued    )
      {
        releaseOVERLAPPED ( m_pOVERLAPPEDrecv );
        m_pOVERLAPPEDrecv -> hr = ERROR_OPERATION_ABORTED;
        PostOVERLAPPED ( m_pOVERLAPPEDrecv );
      }

      P2PeerConDmx *pConThat = m_pConThat;
                               m_pConThat   = 0;
                               m_hFile      = 0;
                               m_hFileCPort = 0;

      // Outstanding OVERLAPPED IO for that object
      if ( pConThat                     &&
           pConThat->m_pConThat == this    )
      {
        pConThat -> m_pConThat = 0;
        if ( pConThat->m_pOVERLAPPEDrecv          &&
             pConThat->m_pOVERLAPPEDrecv->bQueued    )
             //was pConThat->m_pOVERLAPPEDconnect->bQueued    )
        {
          pConThat -> releaseOVERLAPPED( pConThat->m_pOVERLAPPEDrecv );
          pConThat -> m_pOVERLAPPEDrecv -> hr = ERROR_OPERATION_ABORTED;
          pConThat -> PostOVERLAPPED ( pConThat->m_pOVERLAPPEDrecv );
        }
        //pConThat -> m_hFile      = 0;
        //pConThat -> m_hFileCPort = 0;
        //pConThat -> Drop ( 0 );
      }
    }

    // Always delegate
    P2PeerCon::Drop ( pEVENT );
}

///////////////////////////////////////////////////////////////////////
//  Connection state management 
//  NOTES: Referenced from P2PeerCon_MAP handlers only

//
//  Description: Initiates listen processing for object.
//               NOTES: Referenced from On_P2PeerCon_STARTUP handlers
//                      for listen type objects.
//
//
//  Returns:     bool
//               Listen summary
//
bool
P2PeerConDmx::Listen ( )
{
    // Delegate for state confirmation
    P2PeerCon::Listen ( );

    // To be sure, to be sure
    // NOTES: Single entries only
    P2PsafeCS oSafeCS = g_oCSectP2PeerConDmx;
    POSITION   pos    = g_oCListP2PeerConDmx.GetHeadPosition();
    while ( pos )
    {
      if ( g_oCListP2PeerConDmx.GetNext(pos) == this )
        EVERR->Module (__FUNCTION__"(%s)"
                      , GetP2Paddress().c_str() )
             ->Message("Duplicate listen attempted on single connection"
                       "ADVICE\t: Bug(SNHappen)" )
             ->Throw();
    }
    g_oCListP2PeerConDmx.AddTail ( this );

    // On_P2PeerCon_LISTEN() notification
    PostP2Pmsg ( m_oThatP2Paddr, CN_P2PeerCon, P2P_Accept//Was P2P_Listen
               , this, (P2PeerMsg *)0, m_hCPortP2PumpID );

    // Tidy up and
    return true;
}

//
//  Description: Initiates accept facility for object.
//               NOTES: Usually referenced from ON_P2PeerCon_LISTEN
//                      handlers.
//                    : Accepted connections are processed via
//                      ON_P2PeerOLD_ACCEPT handlers
//
//
//  Returns:     bool
//
bool
P2PeerConDmx::Accept ( )
{
    // Delegate for state confirmation
    P2PeerCon::Accept ( );

    // To be sure, to be sure
    if ( m_pConThat          ||
         m_pOVERLAPPEDaccept    )
      EVERR->Module (__FUNCTION__"[%s-%s]"
                    , GetP2PaddrHub().c_str()
                    , (P2PaddrSTR)m_oThatP2Paddr )
           ->Message("Duplicate accepts attempted on single connection"
                    ,"ADVICE\t: Bug (SNHappen)" )
           ->Throw();

    // Initiate wait for client connection
    // NOTES: Must be a non-pended operation
    if ( m_pOVERLAPPEDaccept == NULL )
      m_pOVERLAPPEDaccept = MakeOVERLAPPED ( );

    // Done
    return true;
}

//
//  Performs ACCEPT'ed connection processing
//  NOTES: This processing must be performed for all accepted
//         connections
//       : Reference from within ON_P2PeerOLD_ACCEPT() handlers
//
//
//  Parameters:  const P2Paddr oThatP2Paddr
//               Identification assigned to remote client
//               NOTES: Assignment may be defered to the login 
//                      phase by specifying a NULL value
//
P2PeerCon*
P2PeerConDmx::OnAccept ( )
{
    // To be sure, to be sure
    // NOTES: Something must have externally nominated itself by reaching
    //        into this object to be accepted
    ASSERT(GetMode()==P2PeerCon_SERVICE);
    if ( m_pConThat == NULL )
      return 0;

    // Delegate
    // NOTES: Spawns an object to actually manage the collection which
    //        in turn releases this object to accept additional connections
    P2PeerConDmx *pConSpawn = (P2PeerConDmx *)P2PeerCon::OnAccept ( );

    // Connection notification
    // NOTES: Connecting object has actually latched onto this listening
    //        object but to complete the connection this was previously
    //        all transfered across to the spawned accepted connection
    //        object
    /*if ( pConSpawn             &&
         pConSpawn->m_pConThat    )
    {
      P2PsafeCS oSafeCS = g_oCSectP2PeerConDmx;
      if ( pConSpawn->m_pConThat                       &&
           pConSpawn->m_pConThat->m_pOVERLAPPEDconnect    )
      {
        pConSpawn -> m_pConThat
                  -> PostOVERLAPPED ( pConSpawn->m_pConThat->m_pOVERLAPPEDconnect );
        ASSERT(pConSpawn==pConSpawn->m_pConThat->m_pConThat);
      }
    }*/

    // Lost connection
    if (  pConSpawn             &&
         !pConSpawn->m_pConThat    )
    {
      pConSpawn -> Drop ( 0 );
      pConSpawn = 0;
    }

    // Tidy up, and
    pConSpawn -> SetState ( ConState_Send | ConState_Recv, 0 );
    return pConSpawn;
}
void
P2PeerConDmx::OnAccept ( const P2Paddr oThatP2Paddr )
{
    // Delegate
    P2PeerCon::OnAccept ( oThatP2Paddr );
    ASSERT(GetMode()!=P2PeerCon_SERVICE);

    // Connection notification
    // NOTES: Connecting object latches onto the listening object but to
    //        complete the connection this must be all transfered across
    //        to the allocated accepted connection object
    if ( m_pConThat )
    {
      P2PsafeCS oSafeCS = g_oCSectP2PeerConDmx;
      if ( m_pConThat                       &&
           m_pConThat->m_pOVERLAPPEDconnect    )
      {
        m_pConThat -> m_hFile      = (HANDLE)GetP2Peerio();
        m_pConThat -> m_hFileCPort = (HANDLE)this;
        m_pConThat -> PostOVERLAPPED ( m_pConThat->m_pOVERLAPPEDconnect );
      }
    }

    // Lost connection
    if ( !m_pConThat )
        Drop ( 0 );
}

//
//  Initiates connection processing for object.
//  NOTES: Referenced from On_P2PeerCon_STARTUP handlers for
//         connection type objects.
//
//
//  Returns:     bool
//               Connection summary
//
bool
P2PeerConDmx::Connect ( )
{
    // State confirmation
    P2PeerCon::Connect ( );
    ASSERT(GetMode()!=P2PeerCon_SERVICE);

    // To be sure, to be sure
    if ( m_pConThat )
      EVERR->Module (__FUNCTION__"(%s)"
                    , GetP2Paddress().c_str() )
           ->Message("Previously connected")
           ->Advice ("Bug(SNHappen)" )
           ->Throw();

    // Search for listening connection
    // NOTES: Single entries only
  __time64_t uTimeout = _time64(0)*1000 + 100;
TOP:if ( !m_pConThat ) 
    {
      SwitchToThread ( );
      P2PeerConDmx *pCon    = 0;
      P2PsafeCS     oSafeCS = g_oCSectP2PeerConDmx;
      POSITION       pos    = g_oCListP2PeerConDmx.GetHeadPosition();
      while ( pos )
      {
        pCon = g_oCListP2PeerConDmx.GetNext ( pos );
        if ( pCon->m_eP2PeerConMode == P2PeerCon_SERVICE &&
             pCon->m_sServiceName   == m_sServiceName       )
            break;
        pCon = 0;
      }

      // Accepted connection
      // NOTES: Base class uses m_hFileCPort as state flag
      if (  pCon                               &&
            pCon->m_pOVERLAPPEDaccept          &&
           !pCon->m_pOVERLAPPEDaccept->bQueued &&
           !pCon->m_pConThat                      )
      {
        pCon -> m_pOVERLAPPEDaccept -> dwUserData = (DWORD)this;
        //pCon -> PostOVERLAPPED    ( pCon->m_pOVERLAPPEDaccept ); prior to 22/09/2011
                      m_pConThat   =         pCon;
                      m_hFileCPort = (HANDLE)pCon;
                      m_hFile      = (HANDLE)pCon->GetP2Peerio();
        m_pConThat -> m_pConThat   =         this;
        m_pConThat -> m_hFileCPort = (HANDLE)this;
        m_pConThat -> m_hFile      = (HANDLE)this->GetP2Peerio();
        pCon -> PostOVERLAPPED    ( pCon->m_pOVERLAPPEDaccept ); //after 22/09/2011
      }

      // Yield and try again
      if ( !m_pConThat                 &&
           _time64(0)*1000 <= uTimeout    )
        goto TOP;
    }

    // To be sure, to be sure
    if ( !m_pConThat )
      EVERR->Module (__FUNCTION__"(%s)"
                    , GetP2PaddrHub().c_str() )
           ->Message("Connection %s to %s failed"
                    , GetP2Paddress().c_str(), m_sServiceName )
           ->Advice ("This P2PeerHub not yet running?" )
           ->Throw();

    // Overlapped notification
    // NOTES: Perfomed from m_pConThat->OnAccept()
    if ( m_pOVERLAPPEDconnect == NULL )
      m_pOVERLAPPEDconnect = MakeOVERLAPPED ( 0 );

    // Tidy up and
    return true;
}

void
P2PeerConDmx::Close  ( )
{
    // Primary resource recovery
    // NOTES: P2PeerConDmx's are a little different and need to be
    //        tidied up before delegation to the base class
    //      : Remember "m_hFileCPort" is actually a pointer to the other
    //        P2PeerConDmx object
    //      : Activity requires isolation
    if ( m_pConThat )                  // Scoping CriticalSection 
    {
      P2PsafeCS oSafeCS = g_oCSectP2PeerConDmx;
      if ( m_pConThat )
      {
        // TODO: We must notify the other side
        if ( m_pConThat->m_pConThat == this )
          m_pConThat -> m_pConThat = 0;
        m_pConThat   = 0;
        m_hFile      = 0;              // P2PeerCon attribute
        m_hFileCPort = 0;              // P2PeerCon attribute
      }
    }

    // Always delegate
    P2PeerCon::OnClose();
}

//
//  Description: Close connection
//               NOTES: Closed connections may be Restart()'ed
//                    : Usually performed in the ON_P2PeerCon_CLOSE()
//                      handler
//
//
//  Returns:     BOOL
//               Close object status
//
BOOL
P2PeerConDmx::OnClose ( )
{
    // Firstly tidy up other end
    // NOTES: Cancels all outstanding overlapped IO
    if ( m_pConThat )
    {
      P2PsafeCS oSafeCS = g_oCSectP2PeerConDmx;
      P2PeerConDmx *pConThat = m_pConThat;
                               m_pConThat   = 0;
                               m_hFile      = 0;
                               m_hFileCPort = 0;
      if ( pConThat                     &&
           pConThat->m_pConThat == this    )
      {
        pConThat -> m_pConThat   = 0;
        pConThat -> m_hFile      = 0;
        pConThat -> m_hFileCPort = 0;
        pConThat -> Drop ( 0 );
      }
    }

    // Always delegate
    return P2PeerCon::OnClose();
}

///////////////////////////////////////////////////////////////////////
//  Troubleshooting

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

    // TODO: Additional validation
}

P3PmsgNode
P2PeerConDmx::Serialise ( LPCTSTR lpszVar )
{
    // Locals
    bool bDsc = true;

    // Create a placeholder for receipt of P2PeerConDmx details
    // NOTES: This will be passed by value back up the stack
    P3PmsgNode oNodeVar ( P3PmsgField(lpszVar,P3PmsgData(m_nP2PconID)) );
    if ( lpszVar == 0 || strlen(lpszVar) <= 0 )
      (P3PmsgField&)oNodeVar = P3PmsgName ( _T("{P2PeerConDmx}") );
    else
      oNodeVar.r_data() = P3PmsgData ( _T("{P2PeerConDmx}") );

    // Append our state to node
    P3PmsgField_SERIALISE ( oNodeVar, _T("ServiceName"), (LPCTSTR)m_sServiceName, bDsc
                          , _T("Allocated connection address") );

    // Tidy up and
    oNodeVar += __super::Serialise ( 0 );
    return oNodeVar;
}

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

DWORD
P2PeerConDmx::GetUDState ( )
{
    return (DWORD)m_pConThat;
}
