//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2002-2009
//               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.
//
//
//  Description: P2PeerioBSTR base class implementation
//               NOTES: Implements P2PeerHub connections
//

#include "StdAfx.h"
#include "P2PeerioBSTR.h"
#include "P2PeerCon.h"

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

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

P2PeerioBSTR::~P2PeerioBSTR ( )
{ }

void
P2PeerioBSTR::RenderThisSafe()
{ }

P2Peerio*
P2PeerioBSTR::Clone ( )
{   return new P2PeerioBSTR ( ); }

///////////////////////////////////////////////////////////////////////
//  IO
//  NOTES: Completion Port transmission and receipt
//       : BSTR protocol implementation
//

//
//  Sends P2PeerMsg
//
//
//  Parameters:  HANDLE hFile
//               Send P2PeerMsg file handle
//
//               P2PeerMsg *pMsg
//               Message to be sent
//
//               OVERLAPPEDcon *pOVERLAPPEDsend
//               Pointer to an OVERLAPPEDcon send buffer
//
DWORD
P2PeerioBSTR::SendP2PeerMsg ( HANDLE hFile
                            , P2PeerMsg *pMsg
                            , OVERLAPPEDcon *pOVERLAPPEDsend )
{
    // Introduce locals
    const void   *pvSendMsg;
    DWORD          nSendMsgSize;
    DWORD         dwResult;

    // To be sure, to be sure
    // NOTES: Confirm buffer not already in use
    ASSERT(!pOVERLAPPEDsend->bQueued);
    if ( pOVERLAPPEDsend->bQueued )
      EVERR->Module (__FUNCTION__"(%s-%s)"
                    , (P2PaddrSTR)m_pCon->GetP2PaddrHub()
                    , (P2PaddrSTR)m_pCon->GetP2Paddress() )
           ->Message("OVERLAPPEDsend buffer already in use" )
           ->Advice ("Internal conflict (Bug)" )
           ->Group("P2P")->Throw();
    //pMsg -> Defragment ( );

    // Prepare BSTR type message exchanges
    // NOTES: Whole message size in bytes prefixes the data.  P2PeerMsg
    //        header is ommitted.
    nSendMsgSize = sizeof(P2Psize_t) + pMsg->DataSize();
    if ( nSendMsgSize > pOVERLAPPEDsend->dwBytesMax )
    {
      delete [] pOVERLAPPEDsend->pBuffer;
                pOVERLAPPEDsend->pBuffer    = new char [nSendMsgSize];
                pOVERLAPPEDsend->dwBytesMax = nSendMsgSize;
    }
    pvSendMsg = pOVERLAPPEDsend->pBuffer;
                pOVERLAPPEDsend->dwBytes    = nSendMsgSize;
    memcpy ( pOVERLAPPEDsend->pBuffer
           , &nSendMsgSize, sizeof(nSendMsgSize) );
    memcpy (&pOVERLAPPEDsend->pBuffer[sizeof(nSendMsgSize)]
           ,  pMsg->Data(), pMsg->DataSize() );

    // Send message
    if ( Send(hFile,pvSendMsg,nSendMsgSize,pOVERLAPPEDsend) )
    {
      dwResult = GetLastError();
      if ( dwResult != ERROR_IO_PENDING )
        EVERR->Module (__FUNCTION__"(%s-%s)"
                      , (P2PaddrSTR)m_pCon->GetP2PaddrHub()
                      , (P2PaddrSTR)m_pCon->GetP2Paddress() )
             ->Message("Send(%i bytes) failed\n"
                      , nSendMsgSize )
             ->HResult( dwResult )->Throw();
    }

    // Tidy up and
    return 0;
}

//
//  Receives next P2PeerMsg
//  NOTES: Handles receipt of following message types P2PeerMsg
//         images, including headers BSTR images, headers manufactured
//       : Override for other message formats
//
//
//  Parameters: HANDLE hFile
//              Receive P2PeerMsg file handle
//
//              OVERLAPPEDcon *pOVERLAPPEDrecv
//              Pointer to an OVERLAPPEDcon receive buffer
//
//  Returns:    P2PeerMsg*
//              Received P2PeerMsg.  Client assumes control over
//              life cycle
//                0.. Incomplete, queued IO completion port receive
//
P2PeerMsg*
P2PeerioBSTR::RecvP2PeerMsg ( HANDLE hFile
                            , OVERLAPPEDcon *pOVERLAPPEDrecv )
{
    // Introduce locals
    DWORD     dwResult;
    DWORD     dwBytes    = pOVERLAPPEDrecv->dwBytes;
    DWORD     dwBytesMax = pOVERLAPPEDrecv->dwBytesMax;
    char      *pBuffer   = pOVERLAPPEDrecv->pBuffer;
    P2Psize_t *pMsgSize  = (P2Psize_t *)pOVERLAPPEDrecv->pBuffer;
    const int   S        = sizeof(P2Psize_t);

    // Stage 1 - Fetch message size
    if ( dwBytes < sizeof(P2Psize_t) )
    {
      //TODO: FIXME m_iRecvTimeout = (_time64(0)+10) * 1000;
      dwResult = Recv ( hFile
                      ,&pBuffer[dwBytes]
                      , S-dwBytes, pOVERLAPPEDrecv );
      if ( dwResult )
      {
        if ( dwResult != ERROR_IO_PENDING )
          EVERR->Module (__FUNCTION__"(%s-%s)"
                        , (P2PaddrSTR)m_pCon->GetP2PaddrHub()
                        , (P2PaddrSTR)m_pCon->GetP2Paddress() )
               ->Message("Recv(%i bytes) failed"
                        , S-dwBytes )
               ->HResult( dwResult )->Throw();
      }
    }

    // Stage 2 - Fetch message data
    if ( dwBytes >=   S       &&
         dwBytes <  *pMsgSize    )
    {
      // To be sure, to be sure
      if ( *pMsgSize > pOVERLAPPEDrecv->dwBytesMax )
        EVERR->Module (__FUNCTION__"(%s-%s)"
                      , (P2PaddrSTR)m_pCon->GetP2PaddrHub()
                      , (P2PaddrSTR)m_pCon->GetP2Paddress() )
             ->Message("Recv(%i vs %i) bytes failed\n"
                       "Attempted buffer overrun"
                      , *pMsgSize, dwBytesMax )
             ->Group("P2P")->Throw();

      dwResult = Recv ( hFile
                      ,&pOVERLAPPEDrecv->pBuffer[dwBytes]
                      ,*pMsgSize-dwBytes, pOVERLAPPEDrecv );
      if ( dwResult )
      {
        if ( dwResult != ERROR_IO_PENDING )
          EVERR->Module (__FUNCTION__"(%s-%s)"
                        , (P2PaddrSTR)m_pCon->GetP2PaddrHub()
                        , (P2PaddrSTR)m_pCon->GetP2Paddress() )
               ->Message("Overlapped Recv(%i bytes) failed\n"
                        ,*pMsgSize-dwBytes )
               ->HResult( dwResult )->Throw();
      }
    }

    // Stage 3 - Construct P2PeerMsg
    if ( dwBytes >    S       &&
         dwBytes >= *pMsgSize     )
    {
      //TODO FIXME m_iRecvTimeout = 0;
      ASSERT(dwBytes==*pMsgSize);
      P2PeerMsg *pMsg;
      pMsg = new P2PeerMsg ( m_pCon->GetP2Paddress()
                           , m_pCon->GetP2PaddrHub()
                           , P2Pmsg_3rdParty
                           , (void *)&pBuffer[S], *pMsgSize-S );

      // Buffer management
      if ( *pMsgSize < dwBytes )
        memcpy ( pOVERLAPPEDrecv->pBuffer
               ,&pOVERLAPPEDrecv->pBuffer[*pMsgSize]
               , *pMsgSize-dwBytes );
      pOVERLAPPEDrecv -> dwBytes -= dwBytes;

      // Tidy up, and
      return pMsg;
    }

    // Tidy up and
    return (P2PeerMsg *)0;
}

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

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

    // TODO: Additional validation
}
