//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd 2001-12  (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.
//
//
//  P2PeerHub definitions and prototypes
//  NOTES: Network is built upon the local and remote exchange of
//         P2PeerMsg's between objects derived from this base class
//       : Only P2PeerHub derived network objects may be assigned
//         network P2Paddress's
//       : Network connections between P2PeeHub's are managed via
//         pumped P2PeerCon objects

#include "StdAfx.h"
#include "Kernel32_Ext.h"
#include "P2PeerHub.h"
#include "P2PeerConWsa.h"
#include "P2Pwin32.h"
#include "P2Pevent.h"

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

//
//  Constructors and destructor
//
//  Parameters:  P2PaddrSTR strP2PaddrHub
//               Unique network global identification
//               NOTES: P2Paddress's are only ever assigned to objects
//                      derived from this class.
//                    : May be NULL in which case P2Paddress is negotiated
//                      and allocated as part of a connection login
//                      sequence.
//
P2PeerHub::P2PeerHub ( P2PaddrSTR strP2PaddrHub )
{
    // Firstly
    RenderHubSafe ( );
    SetP2PaddrHub ( strP2PaddrHub );
}

P2PeerHub::~P2PeerHub ( )
{
    CloseHub ( );
    DeleteCriticalSection ( &m_oCSectionHub );
}

void
P2PeerHub::RenderHubSafe()
{
    // Attributes
    m_nHubID        = 0;               // Inactive hub flag
	m_pP2PeerExpump = 0;               // P2PeerExp object

    // Resources
    InitializeCriticalSection ( &m_oCSectionHub );
}

///////////////////////////////////////////////////////////////////////
//  Hub management
//  NOTES: Started using default P2PeerHub::SpawnHub() or
//         CreateHub() implementations

typedef struct
{
  P2PmsgHubID    *pnHubID;
  P2PeerHub      *pHub;
  P2Paddr         oP2Paddr;
  UINT            nPumpsMax;
  RUN_HUB       pfnRunHub;
} P2ProcContext;

//
//  Spawns P2PmsgHub and commences pumping in a new thread
//  NOTES: Thread processing may be stopped via CloseHub()
//       : P2PeerTarget contains equivalent implementation for pumps
//
//
//  Parameters: LPTHREAD_START_ROUTINE pfnThreadProc = 0
//              The thread procedure of the new thread.  Refer
//              win32 CreateThread() for further details
//
//              RUN_HUB pfnRunHub = 0
//              Operational method of the new thread
//
//  Returns:    HANDLE
//              Returns the handle to the newly created thread
//              or NULL on failure.
//
HANDLE
P2PeerHub::SpawnHub ( LPTHREAD_START_ROUTINE pfnThreadProc
                    , RUN_HUB  pfnRunHub )
{
    // Preparation
    ASSERT(m_nHubID == 0 );
    if ( pfnThreadProc == NULL )
      pfnThreadProc = P2PeerHub::ProcHub;
    if ( pfnRunHub == NULL )
      pfnRunHub = RUN_HUB_cast(&P2PeerHub::RunHub);

    // Pass context through
    P2ProcContext oContext;
                  oContext.pnHubID   = &m_nHubID;
                  oContext.pHub      =     this;
                  oContext.pfnRunHub =  pfnRunHub;
                  oContext.oP2Paddr  = m_oP2PaddrHub;
                  oContext.nPumpsMax = 15;

    // Dedicated P2PmsgHub thread creation
    // NOTES: Spawned hub runs under context of this thread and hence
    //        the allocated win32 threadID becomes the P2PmsgHubID
    HANDLE hThread = CreateThread ( 0, 0
                                  , pfnThreadProc, &oContext
                                  , 0, &m_nHubID );
    if ( !hThread )
      return FALSE;

    // Confirm operation
    // NOTES: Contracted for P2PmsgHub operation upon return
    DWORD dwExitCode;
    while ( !P2PmsgHubExists(m_nHubID) )
    {
      if ( !GetExitCodeThread(hThread,&dwExitCode)                ||
                                       dwExitCode != STILL_ACTIVE    )
        return 0;                      // Operational failure
      SwitchToThread ( );              // Yield
    }

    // Tidy up and
    return P2PmsgHubExists(m_nHubID) ? hThread : 0;
}

//
//  Creates P2PmsgHub within the context of this thread
//  NOTES: Thread processing may be stopped via CloseHub()
//       : Facilitates integration of P2PeerHub processing in
//         3rd Party environments such as MFC or service thread
//
//
//  Parameters: P2PaddrSTR strP2PaddrHub
//              Hub network address
//
//            : UINT nPumpsMax
//              Maximum number of P2PmsgPumps supported by the hub
//
//  Returns:     BOOL
//               Success code
//                 TRUE... Created OK
//                 FALSE.. Failure
//
BOOL
P2PeerHub::CreateHub ( P2PaddrSTR strP2PaddrHub
                     , UINT nPumpsMax )
{
    // Hub is created an run in context of calling thread
    // NOTES: Client is reasponsible for pumping messages through
    //        the P2PmsgHub
    ASSERT(m_nHubID == 0 );
    m_oP2PaddrHub = strP2PaddrHub;
    m_nHubID = CreateP2PmsgHub ( strP2PaddrHub, this, nPumpsMax, 1 );

    // Tidy up and
    return P2PmsgHubExists(m_nHubID);
}

//
//  Pause all hub operations
//  NOTES: Upon completion hub effectively exists in idle space
//       : Both hub context and non-hub context OK
void
P2PeerHub::PauseHub ( )
{
    // Pause processing must be performed from context of this hub
    // NOTES: OK to re-signal from any other non-hub context
    if ( GetCurrentThreadId() != GetHubID() )
    {
      if ( GetHubID() > 0 )
        SignalP2PmsgHub ( GetHubID(), P2PsigHub_PAUSE );
      return;
    }
      
    // Close all idle client connections
    ConSignal ( _T("*"), P2PsigCon_CLOSEONIDLE );
}

//
//  Wakeup all webserver operations
//  NOTES: Upon completion hub effectively operational
//       : Both hub context and non-hub context OK
void
P2PeerHub::WakeupHub ( )
{
    // Wakeup processing must be performed from context of this hub
    // NOTES: OK to re-signal from any other non-hub context
    if ( GetCurrentThreadId() != GetHubID() )
    {
      if ( GetHubID() > 0 )
        SignalP2PmsgHub ( GetHubID(), P2PsigHub_WAKEUP );
      return;
    }
      
    // Requires implementation
    ASSERT(0);
}

//
//  Close hub and associated pumps managed through this hub
//  NOTES: Hub processing may be re-started via P2Peer::SpawnHub()
//         or CreateHub()
//
void
P2PeerHub::CloseHub ( )
{
    // Asynchronous closure
    // NOTES: P2PmsgHub exists in another context
    if ( m_nHubID > 0                     &&
         m_nHubID != GetCurrentThreadId()    )
    {
      SignalP2PmsgHub ( m_nHubID, P2PsigHub_CLOSE );
      while ( m_nHubID > 0 && P2PmsgPumpExists(m_nHubID) )
        SwitchToThread ( );
    }

	  // P2PeerExp closure
    DropP2PeerExpump ( );

    // Synchronous closure
    // NOTES: P2PmsgHub exists in this context
    if ( m_nHubID > 0                     &&
         m_nHubID != GetCurrentThreadId()    )
      CloseP2PmsgHub ( );
}

//
//  Default thread starting address nominated in CreateThread()
//  NOTES: Provide alternative implementation to override default
//         action.
//
//
//  Parameters: void *pvData
//              Thread data passed via CreateThread()
//
//  Returns:    DWORD
//              Completion code
//
DWORD WINAPI
P2PeerHub::ProcHub ( void *pvData )
{
    // Introduce locals
    // NOTES: P2ProcContext is assumed not to persist
    P2ProcContext *pContext = (P2ProcContext *)pvData;
    P2Paddr        oP2Paddr =  pContext -> oP2Paddr;
    P2PeerHub     *pHub     =  pContext -> pHub;
    P2PmsgHubID  *pnHubID   =  pContext -> pnHubID;
    UINT           nPumpsMax=  pContext -> nPumpsMax;
    RUN_HUB      pfnRunHub  =  pContext -> pfnRunHub;
    pHub -> P2PeerHub::AssertValid ( );

    // Mandatory P2PeerHub thread environment
    // NOTES: Sequence contains mandatory P2PeerHub and P2PmsgHub
    //        life cycle management sequences
    CreateP2PmsgHub ( oP2Paddr, pHub, nPumpsMax, 1 );
    (pHub->*pfnRunHub) ( );           // Nominated Run() method
    if ( pnHubID && *pnHubID == GetCurrentThreadId() )
      *pnHubID = 0;                   // Flags pump closure
    CloseP2PmsgHub ( );

    // Tidy up, and thread is dead
    //pHub -> PostDestroyHub ( );
    return 0;
}

//
//  Plain network P2PmsgHub implementation
//  NOTES: Simply pumps P2PeerSys, P2PeerCon and P2PeerMsg objects
//         through the P2PeerTarget base class
//       : P2PeerMsg's can be swapped out of this context via
//         ContextSwap() and processed independantly in the fullness
//         of time
//       : It's possible to have processing delays in this context.
//         But keep in mind P2PeerMsg's may keep building up
//
void
P2PeerHub::RunHub (  )
{
    // Introduce locals
    DWORD      dwResult;
    P2PsigID    nSigID;
    DWORD      dwMSec = 8000;
    ASSERT(m_nHubID==GetCurrentThreadId());

    // Because sequence must end orderly
    try
    {
      // Latencies
      SetP2PmsgPumpFunc ( 0, _T(__FUNCTION__) );
      ThrowP2Pevent();

      // Pump messages through the P2PeerSys, P2PeerCon and
      // P2PeerMsg_MAP's until terminated and exhausted
      while ( (dwResult=PumpP2Pmsg(dwMSec,nSigID)) != 0 )
      {
        // Signal - Immediate closure
        if ( nSigID == P2PsigHub_CLOSE )
        {
          P2PeerCon *pCon = 0;
          while ( EnumP2PmsgCon(m_nHubID,&pCon) )
            pCon -> Signal ( P2PsigCon_DESTROY );

          P2PumpID nPumpID = 0;
          while ( EnumP2PmsgPump(m_nHubID,nPumpID) )
            SignalP2PmsgPump ( nPumpID, P2PsigPump_CLOSE );
          break;
        }

        // Signal - Idle closure
        if ( nSigID == P2PsigHub_CLOSEONIDLE )
        {
          P2PeerCon *pCon = 0;
          while ( EnumP2PmsgCon(m_nHubID,&pCon) )
            pCon -> Signal ( P2PsigCon_CLOSEONIDLE );

          P2PumpID nPumpID = 0;
          while ( EnumP2PmsgPump(m_nHubID,nPumpID) )
            SignalP2PmsgPump ( nPumpID, P2PsigPump_CLOSEONIDLE );
          break;
        }

        // Signal - Pause
        if ( nSigID == P2PsigHub_PAUSE )
          PauseHub ( );
          
        // Signal - Wakeup
        if ( nSigID == P2PsigHub_WAKEUP )
          WakeupHub ( );
        ASSERT(m_nHubID==GetCurrentThreadId());
      }
    }

    // Exceptions
    catch ( P2Pevent *pEVT )
    {
      pEVT->Advice(_T("P2PeerHub(%s) P2PmsgHub terminated")
                   , m_oP2PaddrHub.c_str() )
          ->Cancel();
    }
    catch ( ... )
    {
      EVERR->Module (_T(__FUNCTION__) )
           ->Message(_T("Last resort exception of unknown type") )
           ->Advice (_T("P2PeerHub(%s) P2PmsgHub terminated")
                    , m_oP2PaddrHub.c_str() )
           ->Cancel();
    }

    // Tidy up, and
    // NOTES: Manadatory last operation
    m_nHubID = 0;
}

//
//  Performs post P2PmsgHub destruction processing
//  NOTES: Specialise for external notifications etc
//
void
P2PeerHub::PostDestroyHub ( )
{
    ASSERT(m_nHubID==0);
}

///////////////////////////////////////////////////////////////////////
//  P2PeerCon management

//
//  Posts P2PeerCon object to this hub
//  NOTES: Control over life cycle of posted P2PeerCon object is
//         assumed.  Either service or client type objects may be
//         posted
//
//
//  Parameters: P2PeerCon *pCon
//              Connection object to be posted
//
//              P2PumpID nPumpID = 0
//              Identification code of pump to which P2PeerCon
//              object is to be posted
//                0.. Defaults to P2PmsgHub context
//
//  Returns:    BOOL
//              Completion summary flag
//                TRUE..  P2PeerCon object posted
//                FALSE.. P2Pevent generated
BOOL
P2PeerHub::PostP2PeerCon ( P2PeerCon *pCon, P2PumpID nPumpID )
{
    // Introduce locals
    SafeP2PeerCon sppCon  = pCon;
    P2Paddr   oP2PaddrCon = pCon -> GetP2Paddress();
    P2PsafeCS oSafeCS     = m_oCSectionHub;
    if ( g_bP2Pmsg_AssertValid )
      pCon -> AssertValid ( );
	  if ( nPumpID <= 0 )
		  nPumpID = m_nHubID;

    // Environmental
    // NOTES: P2PeerCon objects may only be posted to runnning hubs,
    //        negates risk of dead connections.
    if ( m_nHubID == NULL )
    {
      EVERR->Module (_T(__FUNCTION__) )->AFPcon(pCon)->AFP(nPumpID)
           ->Message(_T("P2PeerHub(%s) is not operational, ")
                     _T("P2PeerCon(%s) not posted")
                    , oP2PaddrCon.c_str()
                    , m_oP2PaddrHub.c_str() )
           ->Advice (_T("Perform SpawnHub() or CreateHub() before PostP2PeerCon()") )
           ->Advice (_T("Hub has failed?") )
           ->Display()->SetLast();
      return FALSE;
    }

    // Iterate through P2PeerCon list
    // NOTES: Trap duplicates.  Null identification addresses
    //        are special
    P2PeerCon *pConEnum = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pConEnum) )
    {
      if ( pConEnum->GetP2Paddress() != oP2PaddrCon )
        continue;
      EVERR->Module (_T(__FUNCTION__))->AFPcon(pCon)->AFP(nPumpID)
           ->Message(_T("P2PeerCon[%s] instance already exists ")
                     _T("within P2PmsgHub[%s]")
                    ,   oP2PaddrCon.c_str()
                    , m_oP2PaddrHub.c_str() )
           ->SetLast();
      return FALSE;
    }

    // Tidy up, and
    pCon -> m_pP2PeerTarget = this;
    PostP2PmsgCon ( nPumpID, pCon );
    PostP2Pmsg ( oP2PaddrCon, CN_P2PeerCon, P2P_Startup
               , pCon, (P2PeerMsg *)0, nPumpID );
    return TRUE;
}

//
//  Signals nominated P2PeerCon object
//
//
//  Parameters: P2PaddrSTR strP2Paddr
//              Address domain defining of P2PeerCon objects to
//              be signalled
//
//              P2PconID nConID
//              Identification of the P2PeerCon object to be signalled
//
//              P2PsigID nSigID
//              Signal to be posted
//
//              void *pvData = 0
//              Signal data
//
//              int iDataSize = 0
//              Signal data size
//
//  Returns     BOOL
//              Signalled event summary
//                TRUE... Located
//                FALSE.. Do not exist
BOOL
P2PeerHub::ConSignal ( P2PaddrSTR strP2PaddrTP
                     , P2PsigID nSigID, void *pvData, int iDataSize  )
{
    // Introduce locals
    P2Paddr   oP2Paddr = strP2PaddrTP;
    BOOL      bResult  = FALSE;
    P2PsafeCS oSafeCS  = m_oCSectionHub;
    ASSERT(m_nHubID>0);

    // Iterate through P2PeerCon'nections list
    P2PeerCon *pConEnum = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pConEnum) )
    {
      if ( !oP2Paddr.IsMapped(pConEnum->GetP2Paddress()) )
        continue;
      bResult = TRUE;
      pConEnum -> Signal ( nSigID, pvData, iDataSize );
      SwitchToThread ( );
    }

    // Tidy up and
    return bResult;
}
BOOL
P2PeerHub::ConSignal ( P2PconID nConID
                     , P2PsigID nSigID, void *pvData, int iDataSize  )
{
    // Introduce locals
    BOOL      bResult  = FALSE;
    P2PsafeCS oSafeCS  = m_oCSectionHub;
    ASSERT(m_nHubID>0);

    // Iterate through P2PeerCon'nections list
    P2PeerCon *pConEnum = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pConEnum) )
    {
      if ( nConID                          &&
           nConID != pConEnum->m_nP2PconID    )
        continue;
      bResult = TRUE;
      pConEnum -> Signal ( nSigID, pvData, iDataSize );
      SwitchToThread ( );
    }

    // Tidy up and
    return bResult;
}

//
//  Checks if P2PeerCon connection object exists for passed
//  P2PaddrSTR
//
//
//  Parameters: P2PaddrSTR strP2Paddr
//              Connection identification code to be checked
//
//  Returns     BOOL
//              Existence summary
//                TRUE... Exists
//                FALSE.. Does not exist
BOOL
P2PeerHub::ConExists  ( P2PaddrSTR strP2Paddr )
{
    // Locals
    P2PsafeCS oSafeCS = m_oCSectionHub;

    // Iterate through P2PeerCon list
    P2PeerCon *pCon = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pCon) )
    {
      if ( pCon->GetP2Paddress() == strP2Paddr )
        return TRUE;
    }

    // Nope, does not exist
    return FALSE;
}

//
//  Queries hub for nominated P2PeerCon
//  NOTES: Designed for use from P2PeerMsg handlers whereby
//         configuration issues require access to underlying
//         P2PeerCon'nections
//       : P2PeerCon pointers are transitory and as such should
//         only retained for the life of a P2PeerMsg handler
//
//
//  Parameters: P2PaddrSTR strP2Paddr
//              Connection idetification
//
//              SafeP2PeerCon& rSafeCon
//              Safe container
//  
//  Returns:    bool
//              Query summary
//                true... Located
//                false.. Not located
bool
P2PeerHub::ConQuery ( P2PaddrSTR strP2Paddress, SafeP2PeerCon& rSafeCon )
{
    // Locals
    P2PsafeCS oSafeCS = m_oCSectionHub;

    // Iterate through P2PeerCon list
    P2PeerCon *pCon = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pCon) )
    {
      if ( pCon->GetP2Paddress() == strP2Paddress )
      {
        rSafeCon = pCon;
        return true;
      }
    }

    // Nope, does not exist
    rSafeCon = (P2PeerCon *)0;
    return false;
}

///////////////////////////////////////////////////////////////////////
//  P2PeerMsg operations

//
//  Routes P2PeerMsg's through P2PeerCon objects posted to this hub
//  NOTES: Local P2PeerMsg routing previously handled
//       : Referenced from P2Pwin32, PostP2PeerMsg() for local hub
//         routing
//
//
//  Parameters: P2PeerMsg *pMsg
//              Message to be routed.
//
//  Returns:    msgRESULT
//              Routing result
msgRESULT
P2PeerHub::RouteP2PeerMsg ( P2PeerMsg *pMsg )
{
    if ( m_pTargetParent )
      return m_pTargetParent -> RouteP2PeerMsg ( pMsg );

    // To be sure, to be sure
    // NOTES: Local routing must already have been performed
    ASSERT(pMsg->GetDestin()!=m_oP2PaddrHub);
    P2PaddrSTR   pP2PaddrMsg = pMsg -> GetDestin ( );

    // Facilitate message peeking
    // NOTES: Application may wish to observe outgoing messages
    msgRESULT msgResult = PeekP2PeerMsg ( pMsg );
    if ( msgResult != msgCONTINUE )
      return msgResult;

    // P2PeerCon routing
    // NOTES: Connections manage their own routing logic
    P2PeerCon *pCon = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pCon) )
    { 
      const P2Paddr& oP2PaddrCon = pCon -> GetP2Paddress();
      if ( pCon->GetMode() == P2PeerCon_SERVICE )
      {
        //TODO:LJM this logic should not be required
        continue;
      }

      // Immediate
      if ( oP2PaddrCon == pP2PaddrMsg )
        return P2PeerContextSwap ( pCon, pMsg );

      // Children
      // NOTES: P2PeerCon is our network child, and
      //        P2PeerMsg is routable down through P2PeerCon
      if ( m_oP2PaddrHub.IsChild(oP2PaddrCon) &&
             oP2PaddrCon.IsRable(pP2PaddrMsg)    )
        return P2PeerContextSwap ( pCon, pMsg );

      // Parent
      // NOTES: P2PeerCon is our network parent, and
      //        P2PeerMsg is not routable down through this P2Peer
      if (    oP2PaddrCon.IsChild(m_oP2PaddrHub) &&
           !m_oP2PaddrHub.IsRable(pP2PaddrMsg)      )
        return P2PeerContextSwap ( pCon, pMsg );
    }
   
    // Undeliverable
    return P2PeerTarget::RouteP2PeerMsg ( pMsg );
}

//
//  Posts P2PeerMsg to this hub for asynchronous routing
//  NOTES: Always posted to the P2PmsgHub enabling associated
//         P2PmsgPump's to observe implementation
//       : P2PmsgHub must be operational otherwise an exception
//         is thrown
//
//
//  Parameters: P2PeerMsg *pMsg
//              Message to be posted to this hub
//              NOTES: Hub assumes control over message life cycle.
//
//  Returns:    P2PeerMsg*
//              Message posted flag
//                 0.. Posted OK
//
P2PeerMsg*
P2PeerHub::PostP2PeerMsg ( P2PeerMsg *pMsg )
{
    if ( m_pTargetParent )
      return m_pTargetParent->PostP2PeerMsg ( pMsg );
    // To be sure, to be sure
    if ( g_bP2Pmsg_AssertValid )
      pMsg -> AssertValid ( );

    // Delegate
    return PostP2Pmsg ( pMsg, m_nHubID );
}

///////////////////////////////////////////////////////////////////////
//  P2PeerExpump management

//
//  Posts P2PeerExpump object to hub
//  NOTES: Hub assumes control over life cycle.  P2PeerExpump object
//         state and configuration externally managed.
//       : Refer GetP2PeerExpump() for exposure of posted object
//
//  Returns:    P2PeerTarget*
//              Object posted flag
//                 0.. Posted OK
P2PeerTarget*
P2PeerHub::PostP2PeerExpump ( P2PeerTarget *pP2PeerExpump )
{
    if ( m_pP2PeerExpump )
    {
      EVERR->Module ( T__FUNCTION__ )
           ->Message(_T("Attempt to post multiple P2PeerExpump objects to P2PeerHub[%s]")
                    , m_oP2PaddrHub.c_str() )
           ->Display()->SetLast();
      return pP2PeerExpump;
    }

    // Tidy up, and
    m_pP2PeerExpump = pP2PeerExpump;
    return 0;
}

BOOL
P2PeerHub::DropP2PeerExpump ( )
{
    if ( !m_pP2PeerExpump )
      return FALSE;
    delete m_pP2PeerExpump;
           m_pP2PeerExpump = 0;
    return TRUE;
}

//
//  Sets the P2Paddr of this P2PeerHub
//  NOTES: Usually the P2Paddr of a hub is assigned at creation
//         time.  However, under some circumstances such as negotiating
//         console connections, P2Paddr needs to be set dynamically by
//         the server.
//
//
//  Parameters: P2PaddrSTR strP2PaddrHub
//              New P2PeerHub address.
//              NOTES: The passed address must maintain the child
//                     relationship with P2PeerHub parent and
//                     parent relationship with any P2PeerHub
//                     children
//
//  Returns:    P2Paddr&
//              Adopted hub address
//
const P2Paddr&
P2PeerHub::SetP2PaddrHub ( P2PaddrSTR strP2PaddrHub )
{
    // Simply
    // NOTES: Observe operational status of hub
    P2PsafeCS oSafeCS = m_oCSectionHub;
    if ( m_nHubID )
      SetP2PmsgHubAddr ( m_nHubID, strP2PaddrHub );
    m_oP2PaddrHub = strP2PaddrHub;

    // Tidy up and
    return m_oP2PaddrHub;
}

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

//
//  Exposes P2Paddr of this P2PeerHub
//  NOTES: Overrides P2PeerTarget base class and as such exposes the
//         P2Paddr of the P2PeerHub to which all P2PeerTarget
//         derived objects must altimately be registered, either
//         directly or indirectly.
//
//
//  Returns:    const P2Paddr&
//              Address of this P2PeerHub
//
const P2Paddr&
P2PeerHub::GetP2PaddrHub ( )
{
    if ( m_pTargetParent )
      return GetP2PeerHub()->GetP2PaddrHub();
    // Snapshot, and
    if ( m_nHubID                > 0 &&
         m_oP2PaddrHub.IsEmpty()        )
    {
      P2PsafeCS oSafeCS = m_oCSectionHub;
      m_oP2PaddrHub = GetP2PmsgHubAddr ( m_nHubID );
    }
    return m_oP2PaddrHub;
}

//
//  Exposes pointer to this P2PeerHub
//  NOTES: Overrides P2PeerTarget base class and as such exposes pointer
//         to P2PeerHub to which all P2PeerTarget derived objects must
//         altimately be registered, either directly or indirectly.
//
//  Returns:    P2PeerHub*
//              Pointer to this object
//
P2PeerHub*
P2PeerHub::GetP2PeerHub ( )
{
    if ( m_pTargetParent )
      return m_pTargetParent->GetP2PeerHub ( );
    return this;
}

//
//  Exposes pointer to encapsulated P2PeerExpummp object
//  NOTES: P2PeerExpump objects are managed through their corresponding
//         base class P2PeerTarget objects.
//       : Intended to limit exposure of P2PeerExpump definitions and  
//         implementation
//
//  Returns:    P2PeerTarget*
//              Pointer to downcast P2PeerExpump object
//
P2PeerTarget*
P2PeerHub::GetP2PeerExpump ( ) const
{
    return m_pP2PeerExpump;
}

//
//  Exposes P2PmsgHubID managed via this instance
//  NOTES: Overrides P2PeerTarget base class and as such exposes pointer
//         to P2PmsgHubID to which all P2PeerTarget derived objects must
//         altimately be registered, either directly or indirectly.
//
//  Returns:    P2PmsgHubID
//              Identification code of this instance.  Refer
//              CreateP2PmsgHub() for further details
//
P2PmsgHubID
P2PeerHub::GetHubID ( ) const
{
    if ( m_pTargetParent )
      return __super::GetHubID();
    return m_nHubID;
}

///////////////////////////////////////////////////////////////////////
//  P2PeerCon handlers
//  NOTES: If there is a desire to optimise the connection map
//         subjectively place frequent messages at the top and less
//         frequent messages at the bottom of the map
// 
BEGIN_P2PeerCon_MAP(P2PeerHub, P2PeerTarget)
END_P2PeerCon_MAP()

///////////////////////////////////////////////////////////////////////
//  P2PeerSys handlers
//  NOTES: If there is a desire to optimise the connection map
//         subjectively place frequent messages at the top and less
//         frequent messages at the bottom of the map
// 
BEGIN_P2PeerSys_MAP(P2PeerHub, P2PeerTarget)
END_P2PeerSys_MAP()

///////////////////////////////////////////////////////////////////////
//  P2PeerMsg Handlers
//  NOTES: Default handler set available in P2PeerHub derived objects
//       : If there is a desire to optimise the message map
//         subjectively place frequent messages at the top and less
//         frequent messages at the bottom of the map
//
BEGIN_P2PeerMsg_MAP(P2PeerHub, P2PeerTarget)
    ON_P2PeerMsg(P2Pmsg_BCast, On_P2PeerBCast)
    ON_P2PeerMsg_CATCH(P2Pmsg_BCast, On_MsgCatch)
    ON_P2PeerMsg(P2Pmsg_Error, On_P2PeerError)
END_P2PeerMsg_MAP()

//
//  MSG_P2PexpumpCtrl handler
//  NOTES: Broadcasts messages to P2PeerHub children through P2PeerCon
//         connections for which broadcasts are enabled
//       : Provide implementation in derived class to intercept a
//         local copy, alternatively specialise PeekP2PeerMsg()
//
//
//  Parameters: P2PeerMsg *pMsg
//              P2Pexp control message
//
//  Returns:    msgRESULT
//              Completion summary
//
/*msgRESULT
P2PeerHub::On_P2PexpumpCtrl ( P2PeerMsg *pMsg )
{
    // Confirm thread context
    // NOTES: MSG_P2PmsgExp messages MUST be processed
    //        sequentually in the context of the P2PmsgHub
    //      : The P2PexpumpID may or may not exist
    if ( !P2PeerContext(m_nHubID) )
      return P2PeerContextSwap ( m_nHubID );

    // Delegate implementation
    // NOTES: Effectively 2 phase start-up, first being external
    //        instanciation of P2PeerExpump object and second being
    //        the demand activation of such object
    //      : External instanciation is an application dependant
    //        activity.  P2PmsgKERNEL provides no such service
    //      : Absense of m_pP2PeerExpump object effectively blocks
    //        exploration through this routing vector.
    P2PeerExpump *pP2PeerExpump = dynamic_cast<P2PeerExpump*>(m_pP2PeerExpump);
    //if ( pP2PeerExpump )
    // return pP2PeerExpump -> On_P2PexpumpCtrl ( pMsg );
    
    // Because this is all problematic
	try
	{
	  // NOTES: Each field becomes a command
      P3PmsgCurs& oCurs = pMsg->r_datn().GetCurs();
      for ( int i = 0; oCurs.Goto(i); i++ )
      {
	    P2PeerExpump *pP2PeerExpump = dynamic_cast<P2PeerExpump*>(m_pP2PeerExpump);
        P3PmsgField& oField = oCurs.r_field();

        // Create P2PeerExpump object
	    // NOTES: Handle redundant requests
        if ( oField == "CREATE" )
        {
		  P3PmsgField& oCreate = oCurs.r_field();
		  if (   pP2PeerExpump             &&
		       ( oCreate.IsNull()     ||
			     oCreate.c_int() == 0     )    )
		    EVERR->MODULE
		         ->Message("P2PeerExp object already exists")
			     ->Advice ("Qualify with non-zero integer")
			     ->Throw ( );
		  m_pP2PeerExpump = new P2PeerExplorer ( this );
        }

        // Spawn P2Pexpump
	    // NOTES: Handle redundant requests
        else if ( oField == "SPAWN" )
        {
		  P3PmsgField& oSpawn = oCurs.r_field();
		  if ( pP2PeerExpump == NULL )
		    EVERR->MODULE
		         ->Message("P2PeerExp does not exist")
			     ->Advice ("Preceed SPAWN with CREATE command")
			     ->Throw ( );
		  if (   pP2PeerExpump->m_nExpumpID &&
		       ( oSpawn.IsNull()     ||
			     oSpawn.c_int() == 0     )      )
		    EVERR->MODULE
		         ->Message("P2Pexpump already spawned")
			     ->Advice ("Qualify with non-zero integer")
	             ->Throw ( );
		  pP2PeerExpump -> SpawnExpump ( 0, 0 );
        }

        // Close P2Pexpump
	    // NOTES: Handle redundant requests
        else if ( oField == "CLOSE" )
        {
		  P3PmsgField& oClose = oCurs.r_field();
		  if (   !pP2PeerExpump->m_nExpumpID &&
		       ( !oClose.IsNull() &&
			     !oClose.c_int()     )          )
		    EVERR->MODULE
		         ->Message("P2Pexpump is not running")
			     ->Advice ("Qualify with null or non-zero integer to skip")
	             ->Throw ( );
		  pP2PeerExpump -> CloseExpump ( 0 );
        }

        // Drop P2PeerExpump object
	    // NOTES: Handle redundant requests
        else if ( oField == "DROP" )
        {
		  P3PmsgField& oDrop = oCurs.r_field();
		  if (   !pP2PeerExpump       &&
		       ( !oDrop.IsNull() &&
			     !oDrop.c_int()     )    )
		    EVERR->MODULE
		         ->Message("P2Pexpump does not exist")
			     ->Advice ("Qualify with null or non-zero integer to skip")
	             ->Throw ( );
		  delete m_pP2PeerExpump;
                 m_pP2PeerExpump = 0;
        }

        // Accept P2PeerConWsa interaction
	    // NOTES: 
        else if ( oField == "AcceptWSA" )
        {
		  if ( !pP2PeerExpump              || 
		       !pP2PeerExpump->m_nExpumpID    )
		    EVERR->MODULE
		         ->Message("P2Pexpump is not running")
			     ->Advice ("Preceed with CREATE and/or SPAWN commands")
			     ->Throw ( );
		  short nEConsPort = oField.c_short();
          P2PeerConWsa *pWsaECid
             = P2PeerConWsa::ServiceFactory( "EXPump", nEConsPort );
                        pWsaECid -> SetState ( ConState_BCasts
                                             | ConState_UCasts, 0 );
          pP2PeerExpump -> PostP2PeerCon ( pWsaECid );

        }

        // Query P2Pexpump status
	    // NOTES: Responds with snapshot 
        else if ( oField == "QUERY" )
	    {
		  P3PmsgField& oQuery = oCurs.r_field();
		  LPCTSTR lpszMsgResponse = "P2Pexpump_Props";
		  if ( !oQuery.IsNull() )
            lpszMsgResponse = oQuery.c_str();
          P2PeerMsgSP spMsg = pMsg->ResponseFactory ( lpszMsgResponse, 0, 0 );
          if ( pP2PeerExpump )
            spMsg->r_datn() += P3PmsgField ( "CREATE", P3PmsgData((int)1) );
		  else
		    spMsg->r_datn() += P3PmsgField ( "CREATE", P3PmsgData((int)0) );
		  if ( pP2PeerExpump              &&
		       pP2PeerExpump->m_nExpumpID    )
		    spMsg->r_datn() += P3PmsgField ( "SPAWN", P3PmsgData((int)1) );
          else
            spMsg->r_datn() += P3PmsgField ( "SPAWN", P3PmsgData((int)0) );
	    }

        // Unknown command
        else
        {
          EVERR->MODULE
		       ->Message("Unknown P2PeerHubCtrl command (%s)"
                        , oField.c_name() )
		       ->Advice ("Valid commands CREATE, SPAWN, CLOSE, DROP, AcceptWSA, QUERY")
		       ->Throw ( );
        }
      }
    }

    // Exceptions
    catch ( P2Pevent *pEVT )
    {
      pEVT ->AFPmsg(pMsg);
      PostP2PeerMsg ( pMsg->ExceptionFactory(pEVT) );
      pEVT ->Cancel ( );
    }
    catch ( ... )
    {
      P2Pevent *pEVT =
      EVERR->MODULE->AFPmsg(pMsg)
           ->Message("Last resort exception" );
      PostP2PeerMsg ( pMsg->ExceptionFactory(pEVT) );
      pEVT ->Cancel( );
    }

    // Tidy up and
    return msgHANDLED;
}*/

//
//  MSG_P2PeerBCast handler
//  NOTES: Broadcasts messages to P2PeerHub children through P2PeerCon
//         connections for which broadcasts are enabled
//       : Provide implementation in derived class to intercept a
//         local copy, alternatively specialise PeekP2PeerMsg()
//
//
//  Parameters: P2PeerMsg *pMsg
//              Broadcast message
//
//  Returns:    msgRESULT
//              Completion summary
//
msgRESULT
P2PeerHub::On_P2PeerBCast ( P2PeerMsg *pMsg )
{
    // P2PeerCon routing
    // NOTES: Connections manage their own routing logic
    P2PeerMsg *pMsgBCast = 0;
    P2PeerCon *pCon      = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pCon) )
    { 
      const P2Paddr& oP2PaddrCon =  pCon->GetP2Paddress();

      // Parents
      // NOTES: Only broadcast to P2PeerCon network parents
      //        for which upcasts are enabled
      if ( pCon->HasState(ConState_BCasts) &&
          !pCon->HasState(ConBCasts_OK)    )
          pCon=pCon;
      if ( !m_oP2PaddrHub.IsChild(oP2PaddrCon) ||
           !pCon->HasState ( ConBCasts_OK )       )
        continue;
      pMsgBCast = pMsg -> RedirectFactory ( oP2PaddrCon );
      pMsgBCast = pCon -> PostP2PeerMsg   ( pMsgBCast );
    }

    // Tidy up and
    return msgHANDLED;
}

//
//  MSG_P2PeerUCast handler
//  NOTES: UCasts messages to P2PeerHub parents through P2PeerCon
//         connections for which broadcasts are enabled
//       : Provide implementation in derived class to intercept a
//         local copy, alternatively specialise PeekP2PeerMsg()
//
//
//  Parameters: P2PeerMsg *pMsg
//              Broadcast message
//
//  Returns:    msgRESULT
//              Completion summary
//
msgRESULT
P2PeerHub::On_P2PeerUCast ( P2PeerMsg *pMsg )
{
    // P2PeerCon routing
    // NOTES: Connections manage their own routing logic
    P2PeerMsg *pMsgUCast = 0;
    P2PeerCon *pCon      = 0;
    while ( EnumP2PmsgCon(m_nHubID,&pCon) )
    { 
      const P2Paddr& oP2PaddrCon = pCon -> GetP2Paddress();

      // Parents
      // NOTES: Only upcast to P2PeerCon network parents for
      //        which upcasts are enabled
      if ( !oP2PaddrCon.IsChild(m_oP2PaddrHub) ||
           !pCon->GetState(ConUCasts_OK)          )
        continue;
      pMsgUCast = pMsg -> RedirectFactory ( oP2PaddrCon );
      pMsgUCast = pCon -> PostP2PeerMsg   ( pMsgUCast );
    }

    // Tidy up and
    return msgHANDLED;
}

//
//  MSG_P2PeerError handler
//  NOTES: Default implementation for error messages.
//
//
//  Parameters: P2PeerMsg *pMsg
//              Error message
//
//  Returns:    msgRESULT
//              Completion summary
//
msgRESULT
P2PeerHub::On_P2PeerError ( P2PeerMsg *pMsg )
{
    // Simply report the message
    P2Pevent *pP2Pevent = pMsg -> ExtractP2Pevent ( );
    if ( pP2Pevent )
       pP2Pevent -> Cancel();

    // Tidy up and
    return msgHANDLED;
}

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

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

    // TODO: Additional validation
}

//
//  Serialise a state snapshot for this P2PeerHub instance
//  NOTES: Such snapshots are used to inject P2PeerHub state information
//         into P2PeerMsg's and P2Pevent's etc
//
//  Parameters:  LPCTSTR lpszVar
//               Name assigned to the generated P2PmsgNode
//                 0.. Use {P2PeerHub}
//
//               bool bDsc = false
//                 true... Add description field attributes
//                 false.. No description field added
//
//  Returns:     P3PmsgNode
//               Snapshot instrance
//
P3PmsgNode
P2PeerHub::Serialise ( LPCTSTR lpszVar, bool bDsc )
{
    // Create a placeholder for receipt of P2PeerHub details
    // NOTES: This will be passed by value back up the stack
    P3PmsgNode oNodeVar ( P3PmsgField(lpszVar,P3PmsgData()) );
    if ( lpszVar == 0 || _tcslen(lpszVar) <= 0 )
      (P3PmsgField&)oNodeVar = P3PmsgName ( _T("{P2PeerHub}") );
    else
      oNodeVar.r_data() = P3PmsgData ( _T("{P2PeerHub}") );

    // Append P2PeerHub state to node
    P3PmsgField_SERIALISE ( oNodeVar, _T("PumpsMax"), 0, bDsc
                          , _T("Maxmimum number of pumps supported by Hub") );
    P3PmsgField_SERIALISE ( oNodeVar, _T("Name"), GetP2PaddrHub().c_name(), bDsc
                          , _T("Allocated Hub name") );
    P3PmsgField_SERIALISE ( oNodeVar, _T("P2Paddr"), GetP2PaddrHub().c_str(), bDsc
                          , _T("Allocated Hub address") );
    P3PmsgField_SERIALISE ( oNodeVar, _T("P2Padom"), GetP2PaddrHub().c_str(), bDsc
                          , _T("Allocated Hub domain") );

    // Append P2PeerTarget state to node
    oNodeVar += __super::Serialise ( _T("") );

    // Tidy up and
    return oNodeVar;
}
