//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  1997-2011
//               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.
//
//
//  P2Pevent implementation
//

#include "StdAfx.h"
#include "afxwin.h"
#include <sys\timeb.h>
#include "P2PTL.h"
#include "P2Pwin32.h"
#include "P2Pevent.h"

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

static  TCHAR s_szServiceName[32]  = { 0 };
static  DWORD s_nEventNo           =   0;
static  DWORD s_dwP2Pevotn_MASK    = (1<<P2Pevent_ERROR);
UINT    WM_P2PeventNOTN = WM_APP + 0x01E0;

#pragma pack(push,1)
typedef struct                         // P2PeventNode details
{
    BYTE         eClass;               // Event class
    time_t       dwEpoch;              // Event epoch
    DWORD        nEventNo;             // Event number
    HRESULT      hr;
} P2PeventNode;
#pragma pack(pop)

//
//  Parameters:  const P2Pevent& rhs
//               Self reference
//
P2Pevent::P2Pevent ( )
        : P3PmsgNode ( P3PmsgField( _T("P2Pevent")
                     , AllocBLOB08( sizeof(P2PeventNode) ) ) )
{
    RenderEventSafe();
}
P2Pevent::P2Pevent ( const P2Pevent& rhs )
{
    RenderEventSafe();
    *this = rhs;
}
P2Pevent::P2Pevent ( const P3PmsgNode& rhs )
        : P3PmsgNode ( rhs )
{
    RenderEventSafe ();
}
P2Pevent::~P2Pevent ( )
{
    // Last events maintenance
    if ( GetP2Pevent() == this )
      IsolateP2Pevent ( );
}

void
P2Pevent::RenderEventSafe ( )
{
    // Attributes
    m_bDisplayed = false;
}

//
//  Cancels the encapsulated P2Pevent
//  NOTES: Object self destructs.  P2Pevent's are intended to 
//         accumulate information over their life cycle.  As such
//         notifications are performed immediately before cancellation
//
//  Parameters:  bool bNotify = true
//               Notifications flag
//                 true... Perform notifications
//                 false.. Skip all notifications
//
//  Returns:     P2Pevent* = 0
//               Null pointer
P2Pevent*
P2Pevent::Cancel ( bool bNotify )
{
    // Confirm
    if (    bNotify    &&
         !m_bDisplayed    )
      Display();

    // Perform notifications
    if ( bNotify )
      PerformP2PeventNotn ( this );

    // Simply
    delete this;
    return 0;
}

///////////////////////////////////////////////////////////////////////
//  Factories

//
//  Makes empty P2Pevent image suitable for chaining
//  NOTES: Problematic exercise on the edge of application stability
//
//  Parameters: P2Pevent_e eClass
//              Instance class
//
//  Returns:    P2Pevent*
//              Pointer to P2Pevent object suitable P2Pevent chaining
P2Pevent*
P2Pevent::MakeEvent( P2Pevent_e eClass )
{
    // Implementation
    P2Pevent *pP2Pevent = 0;
    try
    {
      pP2Pevent = new P2Pevent ( );
     ((P2PeventNode *)pP2Pevent->c_vBlob()) ->  eClass = static_cast<BYTE>(eClass);
     ((P2PeventNode *)pP2Pevent->c_vBlob()) -> dwEpoch = time(0);
      if ( s_szServiceName[0] )
        (*pP2Pevent) += P3PmsgField ( TEvent__Svc, DataBSTR08(s_szServiceName) );

      // Origin of event details
      // NOTES: We may or may not be P2PmsgHub context
      P2PmsgHubID nP2PmsgHubID = GetP2PmsgHubContext();
      if ( nP2PmsgHubID > 0 )
      {
        LPCTSTR lpszHubName  = GetP2PmsgHubName ( nP2PmsgHubID );
        (*pP2Pevent) += P3PmsgField ( TEvent__Hub, DataBSTR08(lpszHubName) );
        LPCTSTR lpszPumpName = GetP2PmsgPumpName( );
        (*pP2Pevent) += P3PmsgField ( TEvent__Pmp, DataBSTR08(lpszPumpName) );
      }
    }
    catch ( ... )
    {
      delete pP2Pevent;
             pP2Pevent = 0;
      throw;                           // Propagate original exception
    }

    // Never a NULL pointer
    return pP2Pevent;
}
//
//  Initialise P2Pevent object
//  NOTES: Problematic exercise on the edge of application stability
//
//  Parameters: P2Pevent_e eClass
//              Instance class
//
//  Returns:    P2Pevent*
//              Pointer to P2Pevent object suitable P2Pevent chaining
P2Pevent*
P2Pevent::InitEvent( P2Pevent_e eClass )
{
    // Implementation
    ((P2PeventNode *)c_vBlob()) ->  eClass = static_cast<BYTE>(eClass);
    ((P2PeventNode *)c_vBlob()) -> dwEpoch = time(0);
    if ( s_szServiceName[0] )
      (*this) += P3PmsgField ( TEvent__Svc, DataBSTR08(s_szServiceName) );

    // Origin of event details
    // NOTES: We may or may not be P2PmsgHub context
    P2PmsgHubID nP2PmsgHubID = GetP2PmsgHubContext();
    if ( nP2PmsgHubID > 0 )
    {
      LPCTSTR lpszHubName  = GetP2PmsgHubName ( nP2PmsgHubID );
      (*this) += P3PmsgField ( TEvent__Hub, DataBSTR08(lpszHubName) );
      LPCTSTR lpszPumpName = GetP2PmsgPumpName( );
      (*this) += P3PmsgField ( TEvent__Pmp, DataBSTR08(lpszPumpName) );
    }

    // Never a NULL pointer
    return this;
}

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

//
//  operator = () overload
//
//  Parameters:  const P2Pevent& rhs
//               Right hand side
//
//  Returns:     P2Pevent&
//               Self reference
//
P2Pevent&
P2Pevent::operator = ( const P2Pevent& rhs )
{
    // Optimise
    if ( this == &rhs )
      return *this;
    P3PmsgNode::operator = ( rhs );

    // Tidy up and
    return *this;
}

///////////////////////////////////////////////////////////////////////
//  Operations

//
//  Sets formatted modulename
//  NOTES: Displaces previous field contents
//
//  Parameters:  const char *pszModuleName
//               Module name to be formatted and set
//
//               ...
//               Variable argument list associated with above format
//               string.
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Module ( LPCTSTR lpszModuleFormat, ... )
{
    // Locals
    va_list ap;
    TCHAR   szModule[512];
    int      cSize = 0;

    // Complete formating from variable argument list.
    va_start ( ap, lpszModuleFormat );
    if ( lpszModuleFormat )
      cSize += _vstprintf_s ( &szModule[cSize], sizeof(szModule)-cSize, lpszModuleFormat, ap );
    szModule[cSize++] = 0;
    VERIFY(cSize<255);

    // Persist and
    if ( P3PmsgNode::Exists(TEvent__Fnc) )
      (*this)[TEvent__Fnc] = DataBSTR08(szModule);
    else
      (*this) += P3PmsgNode ( P3PmsgField(TEvent__Fnc,DataBSTR08(szModule)) );

    // Tidy up, and
    va_end ( ap );
    return this;
}
P2Pevent*
P2Pevent::Module_ ( LPCTSTR lpszModule )
{
    // Persist and
    if ( P3PmsgNode::Exists(TEvent__Fnc) )
      (*this)[TEvent__Fnc] = DataBSTR08(lpszModule);
    else
      (*this) += P3PmsgNode ( P3PmsgField(TEvent__Fnc,DataBSTR08(lpszModule)) );

    // Tidy up, and
    return this;
}

//
//  Sets formatted function parameters
//  NOTES: Appends to existing parameter list
//
//  Parameters:  LPCTSTR lpszFParamName
//               Name of the function parameter
//
//               const P3PmsgData& oData
//               Data associated with above parameter
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::FParam  ( LPCTSTR lpszFParamName, const P3PmsgData& oData )
{
    // Simply append to module parameter list
    if ( !P3PmsgNode::Exists(TEvent__Fnc) )
      (*this) += P3PmsgNode ( P3PmsgField(TEvent__Fnc,DataBSTR08(_T(""))) );
    P3PmsgNode& oNode = dynamic_cast<P3PmsgNode&>((*this)[TEvent__Fnc]);
    oNode += P3PmsgField ( lpszFParamName, oData );

    // Tidy up, and
    return this;
}

P2Pevent*
P2Pevent::FParam  ( LPCTSTR lpszFParamName, const P3PmsgNode& oNode )
{
    // Function parameter list housekeeping
    // NOTES: Creation upon demand etc
    if ( !P3PmsgNode::Exists(TEvent__Fnc) )
      (*this) += P3PmsgNode ( P3PmsgField(TEvent__Fnc,DataBSTR08(_T(""))) );
    P3PmsgNode& oNodeModule = dynamic_cast<P3PmsgNode&>((*this)[TEvent__Fnc]);

    // Create placeholder and append passed contents
    // NOTES: Parameter order is defined by order of creation
    oNodeModule += P3PmsgNode ( P3PmsgField(lpszFParamName,DataBSTR08("")) );
    P3PmsgNode& oNodeVar = dynamic_cast<P3PmsgNode&>(oNodeModule[lpszFParamName]);
    oNodeVar    += oNode;

    // Tidy up, and
    return this;
}

//
//  Placeholder for formatting 3rd Party function parameters
//
//  Parameters:  P2Pevent *pEvent
//               Re-chaining object
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::FParam ( P2Pevent *pEvent )
{
    // Tidy up, and
    ASSERT(this==pEvent);
    return this;
}

//
//  Allocates P2Pevent message
//  NOTES: Build a list of successive message strings
//
//  Parameters:  LPCTSTR lpszFormat
//               Message format
//
//               ...
//               Variable argument list associated with above format
//               string.
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Message ( LPCTSTR lpszFormat, ... )
{
    // Introduce the locals.
    va_list       ap;                  // Variable argument list
    va_start ( ap, lpszFormat );

    // Complete formating from variable argument list.
    if ( lpszFormat )
    {
      // Encode
      TCHAR szBuffer[2048];
      int    cSize;
      cSize = _vstprintf_s ( szBuffer, sizeof(szBuffer), lpszFormat, ap );
      szBuffer[cSize++] = 0;

      // Persist
      // NOTES: Confirm container exists and append
      if ( !P3PmsgNode::Exists(TEvent__Dsc) )
        (*this) += P3PmsgList ( P3PmsgField(TEvent__Dsc) );
      P3PmsgList& oList = dynamic_cast<P3PmsgList&>((*this)[TEvent__Dsc]);
      if ( cSize < 255 )
        oList += DataBSTR08(szBuffer);
      else
        oList += DataBSTR16(szBuffer);
    }

    // Tidy up and
    m_csDescription.Empty ( );         // Activates reformat
    va_end ( ap );
    return this;
}

//
//  Allocates P2Pevent advice
//  NOTES: Build a list of successive advice strings
//
//  Parameters:  LPCTSTR lpszFormat
//               Advice format
//
//               ...
//               Variable argument list associated with above format
//               string.
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Advice ( LPCTSTR lpszFormat, ... )
{
    // Introduce the locals.
    va_list       ap;                  // Variable argument list
    va_start ( ap, lpszFormat );

    // Complete formating from variable argument list.
    if ( lpszFormat )
    {
      // Encode
      TCHAR szBuffer[2048];
      int    cSize;
      cSize = _vstprintf_s ( szBuffer, sizeof(szBuffer), lpszFormat, ap );
      szBuffer[cSize++] = 0;

      // Persist
      // NOTES: Confirm container exists and append
      if ( !P3PmsgNode::Exists(TEvent__Adv) )
        (*this) += P3PmsgList ( P3PmsgField(TEvent__Adv) );
      P3PmsgList& oList = dynamic_cast<P3PmsgList&>((*this)[TEvent__Adv]);
      if ( cSize < 255 )
        oList += DataBSTR08(szBuffer);
      else
        oList += DataBSTR16(szBuffer);
    }

    // Tidy up and
    m_csAdvice.Empty();                // Activates reformat
    va_end ( ap );
    return this;
}

//
//  Sets error group
//  NOTES: Usually a short description
//           WIN.. Windows
//           WSA.. Windows Socket Architecture
//           P2P.. Peer2Peer
//       : Displaces previous field contents
//
//
//  Parameters:  LPCTSTR lpszGroup
//
//               ...
//               Variable argument list associated with above
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Group ( LPCTSTR lpszGroupFormat, ... )
{
    // Introduce the locals.
    va_list       ap;                  // Variable argument list
    TCHAR         szGroup[512];
    int            nSize = 0;

    // Complete formating from variable argument list.
    va_start ( ap, lpszGroupFormat );
    if ( lpszGroupFormat )
      nSize = _vstprintf_s ( szGroup, sizeof(szGroup), lpszGroupFormat, ap );
    szGroup[nSize++] = 0;

    // Persist, and
    // NOTES: Optional field
    if ( P3PmsgNode::Exists(TEvent__Grp) )
      (*this)[TEvent__Grp] = DataBSTR08(szGroup);
    else
      (*this) += P3PmsgField ( TEvent__Grp, DataBSTR08(szGroup) );

    // Tidy up, and
    va_end ( ap );
    return this;
}

//
//  Sets HRESULT code for object
//  NOTES: Over writes existing HRESULT for this level
//
//
//  Parameters:  HRESULT hr
//               System error to be set
//
//               LPCTSTR lpszhr = 0
//               Text description associated with above error
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::HResult ( HRESULT hr, LPCTSTR lpszHR )
{
    // Persist
    TCHAR szHR[256];
    if ( lpszHR == 0 )
    {
      DWORD dwSize = FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM
                                   , 0
                                   , hr
                                   , 0
                                   , szHR, sizeof(szHR)
                                   , 0 );
               szHR[dwSize] = 0;
      lpszHR = szHR;
    }

    // Simply
    ((P2PeventNode *)c_vBlob()) -> hr = hr;
    if ( P3PmsgNode::Exists(TEvent__Sys) )
      (*this)[TEvent__Sys] = DataBSTR16(lpszHR);
    else
      (*this) += P3PmsgField ( TEvent__Sys, DataBSTR16(lpszHR) );
    return this;
}

//
//  Sets this P2Pevent as our last thread P2Pevent
//  NOTES: Delegates directly through to SetP2Pevent()
//
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::SetLast ( )
{
    // Simply
    SetP2Pevent ( this );
    return this;
}

//  Throws pointer to this P2Pevent
//  NOTES: Allocated memory must be released upon capture
//       : Usually only EVERR type events are thrown
//
//  Parameters:  bool bSetLastEvent = true
//                 true... Set this event as last event for 
//                         thread
//                 false.. Skip last thread event sequence
void
P2Pevent::Throw ( bool bSetLastEvent )
{
    // Thrown P2Pevent's become the last
    if ( bSetLastEvent )
      SetLast ( );

    // Simply
    throw this;
}

//
//  Displays P2Pevent message
//  NOTES: Refer Notify() for performing registered P2Pevent sink
//         notifications
//
//
//  Parameters:  HWND hWnd
//               Parent window for which error displayed
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Display ( HWND hWnd )
{
    // Display the message.
    P2Pevent_e eClass = GetClass ( );
    //if ( IsP2PeventReg(1<<eClass) ||
    //     eClass == P2Pevent_ERROR )
    if ( (s_dwP2Pevotn_MASK&(1<<eClass)) ||
          eClass == P2Pevent_ERROR          )
    {
      // Some locals
      CString csMessage;

      // Address component
      CString csOrigin;
      if ( GetService() )
        csOrigin += GetService();
      if ( P3PmsgNode::Exists(TEvent__Hub) )
      {
        csOrigin += _T("[Hub=");
        csOrigin += (*this)[TEvent__Hub].c_str();
        csOrigin += _T("]");
      }
      if ( P3PmsgNode::Exists(TEvent__Pmp) )
      {
        csOrigin += _T("[Pump=");
        csOrigin += (*this)[TEvent__Pmp].c_str();
        csOrigin += _T("]");
      }
      if ( !csOrigin.IsEmpty() )
      {
        if ( !csMessage.IsEmpty() )
          csMessage += _T("\n");
        csMessage += csOrigin;
      }

      // Function component
      CString strFunction;
      strFunction.Format(_T("[%s]%s"), GetService(), GetModule() );
      if ( P3PmsgNode::Exists(TEvent__Fnc) )
      {
        P3PmsgNode& oNodeFunc = dynamic_cast<P3PmsgNode&>((*this)[TEvent__Fnc]);
        P3PmsgCurs& oCursFunc = oNodeFunc.r_Curs ( );
        strFunction += _T("(");
        for ( int i = 0; oCursFunc.Goto(i); i++ )
        {
          if ( i > 0 )
            strFunction += _T(", ");
          strFunction += oCursFunc.r_name().c_name();
          if ( oCursFunc.IsField() )
          {
            strFunction += _T("=");
            strFunction += oCursFunc.r_field().ToString();
          }
        }
        strFunction += _T(")");
      }

      // HRESULT component
      if ( GetHRESULT() )
      {
        CString csHResult = GetHRESULText();
        if ( !csHResult.IsEmpty() )
          csMessage += csHResult;
        csHResult.Format (_T("[HRESULT=%ld]\n"), GetHRESULT() );
        csMessage += csHResult;
      }

      // Message component
      if ( !GetMessage().IsEmpty() )
      {
        if ( !csMessage.IsEmpty() )
          csMessage += "\n";
        csMessage += GetMessage();
      }

      // Advice component
      if ( !GetAdvice().IsEmpty() )
      {
        if ( !csMessage.IsEmpty() )
          csMessage += "\n";
        csMessage += GetAdvice();
      }

      // Module component
      // NOTES: Window with focus will loose that focus
      //      : MessageBoxEx(), for some reason, performs better
      //        in a muti-threaded environment
      HWND hWndFocus = ::GetFocus ( );

      if ( eClass == P2Pevent_DEBUG )
        MessageBoxEx ( 0, csMessage, strFunction
                     , MB_OK | MB_ICONINFORMATION
                     | MB_TASKMODAL, 0 );
      else if ( eClass == P2Pevent_TRACE )
        MessageBoxEx ( 0, csMessage, strFunction
                     , MB_OK | MB_USERICON
                     | MB_TASKMODAL, 0 );
      else 
        MessageBoxEx ( 0, csMessage, strFunction
                     , MB_OK | MB_ICONERROR
                     | MB_TASKMODAL, 0 );

      // Restore focus of original window
      // NOTES: MessageBoxEx() will NOT restore focus upon
      //        completion.
      //      : This action is only performed when user has
      //        provided us with a parent window
      if ( hWnd      &&
           hWndFocus    )
        ::SetFocus ( hWndFocus );

    }

    // Tidy up and
    m_bDisplayed = true;
    return this;
}

//
//  Prints P2Pevent message
//  NOTES: Refer Notify() for performing registered P2Pevent sink
//         notifications
//
//
//  Parameters:  FILE *fd = stdout
//               TTY output device
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Print ( FILE *fd )
{
    // Delegate, essentually intercepted for chaining
    P3PmsgNode::Print ( fd, 0, 1 );
    return this;
}

//
//  Isolates this P2Pevent instance
//  NOTES: Isolated P2Pevent objects are no longer thread attached
//         and as such may be thread swapped
//       : Refer SetP2Pevent() and GetP2Pevent() for further deatils
//
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Isolate ( )
{
    // Object is floating free
    if ( GetP2Pevent() == this )
      IsolateP2Pevent();
    return this;
}

//
//  Perform notifications for P2Pevent
//  NOTES: Notifications are normally performed upon event
//         cancellation, refer Cancel() for further details
//
//  Returns:     P2Pevent*
//               Pointer to this object suitable for P2Pevent chaining
P2Pevent*
P2Pevent::Notify ( )
{
    // Perform notifications
    PerformP2PeventNotn ( this );
    return this;
}

///////////////////////////////////////////////////////////////////////
//  Configuration 

//
//  Sets a formatted service name
//  NOTES: Service name is global and usually constant for a process
//         and as such is set as part of the initialisation sequences
//       : "Svc" field is added to all P2Pevents generated in the
//         process domain for which it's defined
//
//
//  Parameters:  LPCTSTR lpszServiceName
//               Service name to be formatted and loaded.
//
//               ...
//               Variable argument list associated with above format
//               string.
//
//  Returns:     LPCTSTR
//               Formatted service name
LPCTSTR
P2Pevent::Service ( LPCTSTR lpszServiceFormat, ... )
{
    // Introduce the locals.
    va_list       ap;                  // Variable argument list
    TCHAR         szService[512];
    int           cSize = 0;
    va_start ( ap, lpszServiceFormat );

    // Complete formating from variable argument list.
    if ( lpszServiceFormat )
      cSize = min ( _vstprintf_s ( &szService[cSize], sizeof(szService), lpszServiceFormat, ap )
                  , sizeof(s_szServiceName)-1 );
    memcpy ( s_szServiceName, szService, cSize );
    s_szServiceName[cSize] = 0;
    va_end ( ap );

    // Persist, and
    return s_szServiceName;
}

//
//  Configures P2Pevent reporting 
//  NOTES: Reporting is only performed for configured events and is
//         independant of the registered P2Pevent notification sinks
//
//
//  Parameters:  ReportCmd_e eCmd
//               Configuration command
//
//               DWORD dwCmdArg
//               Argument associated with above command
//
//  Returns:     DWORD
//               Configuration command dependent result
//
DWORD
P2Pevent::Configure ( ReportCmd_e eCmd, DWORD dwCmdArg )
{
    // Command application
    if ( eCmd == ADDMASK )
      return s_dwP2Pevotn_MASK |=  dwCmdArg;
    if ( eCmd == REMMASK )
      return s_dwP2Pevotn_MASK &= ~dwCmdArg;
    if ( eCmd == SETMASK )
      return s_dwP2Pevotn_MASK  =  dwCmdArg;
    if ( eCmd == GETMASK )
      return s_dwP2Pevotn_MASK;

    // Tidy up and
    ASSERT(0);
    return (DWORD)~0;
}

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

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

//
//  Breaks operation and places operator in the debugger
//  NOTES: Null operation in release mode
//       : Add and remove from P2Pevent chain during debugging etc
//
//  Returns:     P2Pevent*
//               Pointer to this object used for P2Pevent chaining
P2Pevent*
P2Pevent::Break ( ) const
{
  __debugbreak ( );
    return (P2Pevent *)this;
}

///////////////////////////////////////////////////////////////////////
//  Property management

//
//  Fetches the P2Pevent class
//  NOTES: Refer MakeEvent() for setting P2Pevent class
//
//  Returns:     P2Pevent_e
//               Event class
P2Pevent_e
P2Pevent::GetClass ( )
{
    return (P2Pevent_e)((P2PeventNode *)c_vBlob())->eClass;
}

LPCTSTR
P2Pevent::GetClassText ( )
{
    P2Pevent_e eClass = GetClass();
    if ( eClass == P2Pevent_UNDEF )
      return _T("EVUND");
    if ( eClass == P2Pevent_ERROR )
      return _T("EVERR");
    if ( eClass == P2Pevent_WARNING )
      return _T("EVWRN");
    if ( eClass == P2Pevent_DEBUG )
      return _T("EVDBG");
    if ( eClass == P2Pevent_TRACE )
      return _T("EVTRC");
    if ( eClass == P2Pevent_LOG )
      return _T("EVLOG");
    if ( eClass ==  6 )
      return _T("EV005");
    if ( eClass ==  7 )
      return _T("EV005");
    if ( eClass ==  8 )
      return _T("EV005");
    if ( eClass ==  9 )
      return _T("EV005");
    if ( eClass == 10 )
      return _T("EV005");
    if ( eClass == 11 )
      return _T("EV005");
    if ( eClass == 12 )
      return _T("EV005");
    if ( eClass == 13 )
      return _T("EV005");
    if ( eClass == 14 )
      return _T("EV005");
    if ( eClass == 15 )
      return _T("EV005");
    if ( eClass == 16 )
      return _T("EV005");
    if ( eClass == 17 )
      return _T("EV005");
    if ( eClass == 18 )
      return _T("EV005");
    if ( eClass == 19 )
      return _T("EV005");
    if ( eClass == 20 )
      return _T("EV005");
    if ( eClass == 21 )
      return _T("EV005");
    if ( eClass == 22 )
      return _T("EV005");
    if ( eClass == 23 )
      return _T("EV005");
    if ( eClass == 24 )
      return _T("EV005");
    if ( eClass == 25 )
      return _T("EV005");
    if ( eClass == 26 )
      return _T("EV005");
    if ( eClass == 27 )
      return _T("EV005");
    if ( eClass == 28 )
      return _T("EV005");
    if ( eClass == 29 )
      return _T("EV005");
    if ( eClass == 30 )
      return _T("EV005");
    if ( eClass == 31 )
      return _T("EV005");

    // Hmmm
    return _T("EV___");
}

//
//  Fetches P2Pevent service or application name
//  NOTES: Service auto-assigned in MakeEvent()
//
//  Returns:     LPCTSTR
//               Application or service name
//
LPCTSTR
P2Pevent::GetService ( )
{
    if ( P3PmsgNode::Exists(TEvent__Svc) )
      return (*this)[TEvent__Svc].c_str();
    return _T("");
}

//
//  Fetches P2Pevent group name
//  NOTES: Refer Group() for group name assignment
//
//  Returns:     LPCTSTR
//               Group name
//
LPCTSTR
P2Pevent::GetGroup ( )
{
    if ( P3PmsgNode::Exists(TEvent__Grp) )
      return (*this)[TEvent__Grp].c_str();
    return _T("");
}

//
//  Fetches composition P2Pevent description
//
//  Returns:     LPCTSTR
//               Message text
//
const CString&
P2Pevent::GetMessage ( )
{
    if ( m_csDescription.IsEmpty()  &&
         P3PmsgNode::Exists(TEvent__Dsc)     )
    {
      P3PmsgList& oList = dynamic_cast<P3PmsgList&>((*this)[TEvent__Dsc]);
      UINT aEntry = oList.GetHeadPos();
      while ( aEntry )
      {
        if ( !m_csDescription.IsEmpty() )
          m_csDescription += _T("\n");
        P3PmsgData& oEntry = oList.GetNext ( aEntry );
        m_csDescription += oEntry.c_str();
      }
    }
    return m_csDescription;
} 

//
//  Fetches P2Pevent advice
//  NOTES: Refer Advice() for accumulating P2Pevent advice
//
//  Returns:     CString&
//               Advice text
//
const CString&
P2Pevent::GetAdvice ( )
{
    if ( m_csAdvice.IsEmpty()       &&
         P3PmsgNode::Exists(TEvent__Adv)    )
    {
      P3PmsgList& oList = dynamic_cast<P3PmsgList&>((*this)[TEvent__Adv]);
      UINT aEntry = oList.GetHeadPos();
      while ( aEntry )
      {
        if ( m_csAdvice.IsEmpty() )
          m_csAdvice += _T("ADVICE\t: ");
        else
          m_csAdvice += _T("\n\t: ");
        P3PmsgData& oEntry = oList.GetNext ( aEntry );
        m_csAdvice += oEntry.c_str();
      }
    }
    return m_csAdvice;
}

//
//  Fetches P2Pevent module
//  NOTES: Refer Module() for setting P2Pevent module name
//
//  Returns:     LPCTSTR
//               Name of precipitating module
//
LPCTSTR
P2Pevent::GetModule ( )
{
    if ( P3PmsgNode::Exists(TEvent__Fnc) )
      return (*this)[TEvent__Fnc].c_str();
    return _T("");
}

BOOL
P2Pevent::EmptyModule ( )
{
    if ( P3PmsgNode::Exists(TEvent__Fnc) )
      return TRUE;
    return FALSE;
}

//
//  Fetches P2Pevent HRESULT property
//  NOTES: Synomym for win32 error code
//       : Refer HResult() for setting P2Pevent HRESULT property
//
HRESULT
P2Pevent::GetHRESULT ( )
{
    return ((P2PeventNode *)c_vBlob())->hr;
}
LPCTSTR
P2Pevent::GetHRESULText ( )
{
    if ( P3PmsgNode::Exists(TEvent__Sys) )
      return (*this)[TEvent__Sys].c_str();
    return _T("");
}

//
//  Fetches P2Pevent object creation time
//  NOTES: Due consideration should be given to the domain in which
//         the event was created.  Unrelated P2Pevents may have the
//         same creation time
//       : Auto-assigned in MakeEvent()
//
//  Returns:     time_t
//               Epoch at which event was created
//
time_t
P2Pevent::GetTime ( )
{
    return ((P2PeventNode *)c_vBlob()) -> dwEpoch;
}

//
//  Fetches P2Pevent event number
//  NOTES: Auto-assigned in MakeEvent()
//
//  Returns:     DWORD
//               Internally allocated event number
//
DWORD
P2Pevent::GetEvent ( )
{
    return ((P2PeventNode *)c_vBlob()) -> nEventNo;
}

///////////////////////////////////////////////////////////////////////
//  P2Pevent sink notification callbacks
//  NOTES: CreateP2PeventSink() compatible

//
//  Posts P2Pevent to nominated window
//  NOTES: Window MUST intercept posted P2Pevent and release
//         associated resources
//
//  Parameters: P2PeventSinkID nSinkID
//              Notification sink
//
//              DWORD dwCBKey
//              Callback key, effectively handle to window
//
//              const P2Pevent& oEvent
//              Reference to notification event
void WINAPI
P2PeventCB_HWND ( P2PeventSinkID nSinkID, DWORD dwCBKey
                , const P2Pevent& oEvent )
{
    // Window notifications
    // NOTES: Copy asynchonously posted
    if ( IsWindow((HWND)dwCBKey) ) 
      ::PostMessage ( (HWND)dwCBKey, WM_P2PeventNOTN
                    , (WPARAM)new P2Pevent(oEvent), nSinkID );
}

//
//  Posts P2Pevent to nominated thread
//  NOTES: Thread MUST intercept posted P2Pevent and release
//         associated resources
//
//  Parameters: P2PeventSinkID nSinkID
//              Notification sink
//
//              DWORD dwCBKey
//              Callbak key, effectively handle to window
//
//              const P2Pevent& oEvent
//              Reference to notification event
void
P2PeventCB_Thread ( P2PeventSinkID nSinkID, DWORD dwCBKey
                  , const P2Pevent& oEvent )
{
    // Thread notifications
    // NOTES: Copy asynchonously posted
    if ( IsWindow((HWND)dwCBKey) ) 
      ::PostThreadMessage ( dwCBKey, WM_P2PeventNOTN
                          , (WPARAM)new P2Pevent(oEvent), nSinkID );
}

///////////////////////////////////////////////////////////////////////
//  Catch block interceptions
//  NOTES: Utilised in catch handlers to translate exception object
//         into P2Pevent object.  Usually extracts Message() and
//         HResult() from message/environment
//
P2Pevent*
P2Pevent_catch ( CException *pEx, bool bDeleteEx )
{
    TCHAR szError[512];
    pEx -> GetErrorMessage ( szError, sizeof(szError)/sizeof(TCHAR) );
    if ( bDeleteEx )
      pEx -> Delete ();
    P2Pevent *pEVT = EVERR
                  -> Message( szError )
                  -> HResult( GetLastError() );
    return pEVT;
}

