//--------------------------------------------------------------------
// Copyright (C)1998 Microsoft Corporation, All Rights Reserved.
//
// io.cpp
//
// Author:
//
//   Edward Reus (edwardr)     02-27-98   Initial coding.
//
//--------------------------------------------------------------------

#include "precomp.h"

#ifdef DBG_MEM
static LONG g_lCIoPacketCount = 0;
#endif

//--------------------------------------------------------------------
// CIOPACKET::CIOPACKET()
//
//--------------------------------------------------------------------
CIOPACKET::CIOPACKET()
    {
    m_dwKind = PACKET_KIND_LISTEN;
    m_hIoCompletionPort = INVALID_HANDLE_VALUE;
    m_ListenSocket = INVALID_SOCKET;
    m_Socket = INVALID_SOCKET;
    m_hFile = INVALID_HANDLE_VALUE;
    m_pLocalAddr = 0;
    m_pFromAddr = 0;
    m_pAcceptBuffer = 0;
    m_pReadBuffer = 0;
    m_pvWritePdu = 0;
    m_dwReadBufferSize = 0;
    }

//--------------------------------------------------------------------
// CIOPACKET::~CIOPACKET()
//
//--------------------------------------------------------------------
CIOPACKET::~CIOPACKET()
    {
    // NOTE: Don't free m_pLocalAddr or m_pFromAddr, they just point
    // into m_pAcceptBuffer.

    if (m_pAcceptBuffer)
       {
       FreeMemory(m_pAcceptBuffer);
       }

    if (m_pReadBuffer)
       {
       FreeMemory(m_pReadBuffer);
       }

    // NOTE: Don't delete the write PDU (m_pvWritePdu), its free'd by 
    // somebody else (when the IO completes)...
    }

//------------------------------------------------------------------------
//  CIOPACKET::operator new()
//
//------------------------------------------------------------------------
void *CIOPACKET::operator new( IN size_t Size )
    {
    void *pObj = AllocateMemory(Size);

    #ifdef DBG_MEM
    if (pObj)
        {
        InterlockedIncrement(&g_lCIoPacketCount);
        }

    DbgPrint("new CIOPACKET: Count: %d\n",g_lCIoPacketCount);
    #endif

    return pObj;
    }

//------------------------------------------------------------------------
//  CIOPACKET::operator delete()
//
//------------------------------------------------------------------------
void CIOPACKET::operator delete( IN void *pObj,
                                 IN size_t Size )
    {
    if (pObj)
        {
        DWORD dwStatus = FreeMemory(pObj);

        #ifdef DBG_MEM
        if (dwStatus)
            {
            DbgPrint("IrXfer: IrTran-P: CIOPACKET::delete: FreeMemory Failed: %d\n",dwStatus);
            }

        InterlockedDecrement(&g_lCIoPacketCount);

        if (g_lCIoPacketCount < 0)
            {
            DbgPrint("IrXfer: IrTran-P: CIOPACKET::delete Count: %d\n",
                     g_lCIoPacketCount);
            }
        #endif
        }
    }

//--------------------------------------------------------------------
// CIOPACKET::Initialize()
//
//--------------------------------------------------------------------
DWORD CIOPACKET::Initialize( IN DWORD  dwKind,
                             IN SOCKET ListenSocket,
                             IN SOCKET Socket,
                             IN HANDLE hIoCP )
    {
    DWORD  dwStatus = NO_ERROR;

    m_dwKind = dwKind;
    m_hIoCompletionPort = hIoCP;

    if (dwKind == PACKET_KIND_LISTEN)
        {
        // The accept buffer needs to be large enough to hold
        // the "from" and "to" addresses:
        m_pAcceptBuffer = AllocateMemory(2*(16+sizeof(SOCKADDR_IRDA)));
        if (!m_pAcceptBuffer)
            {
            return ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        }

    m_ListenSocket = ListenSocket;
    m_Socket = Socket;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CIOPACKET::PostIoListen()
//
//--------------------------------------------------------------------
DWORD CIOPACKET::PostIoListen()
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwBytes;
    HANDLE hIoCP;


    m_Socket = WSASocketW( AF_IRDA,
                           SOCK_STREAM,
                           IPPROTO_IP,
                           0, 
                           0,
                           WSA_FLAG_OVERLAPPED );

    if (m_Socket == INVALID_SOCKET)
        {
        dwStatus = WSAGetLastError();
        return dwStatus;
        }

    hIoCP = CreateIoCompletionPort( (void*)m_Socket,
                                    m_hIoCompletionPort,
                                    m_Socket,
                                    0 );
    if (!hIoCP)
        {
        dwStatus = GetLastError();
        #ifdef DBG_ERROR
        DbgPrint("CIOPACKET::PostIoListen(): CreateIoCompletionPort() failed: %d\n",dwStatus);
        #endif
        closesocket(m_Socket);
        m_Socket = INVALID_SOCKET;
        return dwStatus;
        }

    memset(&m_Overlapped,0,sizeof(m_Overlapped));

    if (!AcceptEx(m_ListenSocket,
                  m_Socket,
                  m_pAcceptBuffer,
                  0,
                  16 + sizeof(SOCKADDR_IRDA),
                  16 + sizeof(SOCKADDR_IRDA),
                  &dwBytes,  // Never actually used in this case...
                  &m_Overlapped ))
        {
        // This is the normal execution path, with dwStatus == ERROR_IO_PENDING
        dwStatus = WSAGetLastError();
        if (dwStatus == ERROR_IO_PENDING)
            {
            dwStatus = NO_ERROR;
            }
        }
    else
        {
        // Should get here only if a client is trying to connect just as
        // the AcceptEx() is called...
        dwStatus = NO_ERROR;
        }

    #ifdef DBG_IO
    DbgPrint("CIOPACKET::PostIoListen(): AcceptEx(): Socket: %d\n",
             m_ListenSocket );
    #endif

    return dwStatus;
    }

//--------------------------------------------------------------------
// CIOPACKET::PostIoRead()
//
//--------------------------------------------------------------------
DWORD CIOPACKET::PostIoRead()
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwBytes;

    if (!m_pReadBuffer)
        {
        m_pReadBuffer = AllocateMemory(DEFAULT_READ_BUFFER_SIZE);
        m_dwReadBufferSize = DEFAULT_READ_BUFFER_SIZE;

        if (!m_pReadBuffer)
            {
            return ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        }

    memset(&m_Overlapped,0,sizeof(m_Overlapped));

    BOOL b = ReadFile( (HANDLE)m_Socket,
                       m_pReadBuffer,
                       m_dwReadBufferSize,
                       0,  // Can be zero for overlapped IO.
                       &m_Overlapped );

    if (!b)
        {
        dwStatus = GetLastError();
        if ((dwStatus == ERROR_HANDLE_EOF)||(dwStatus == ERROR_IO_PENDING))
            {
            dwStatus = NO_ERROR;
            }
        }

    #ifdef DBG_IO
    DbgPrint("CIOPACKET::PostIoListen(): ReadFile(): Socket: %d\n",
             m_Socket );
    #endif

    return dwStatus;
    }

//--------------------------------------------------------------------
// CIOPACKET::PostIoWrite()
//
//--------------------------------------------------------------------
DWORD CIOPACKET::PostIoWrite( IN void  *pvBuffer,
                              IN DWORD  dwBufferSize,
                              IN DWORD  dwOffset      )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwBytes;
    HANDLE hFile;

    memset(&m_Overlapped,0,sizeof(m_Overlapped));

    if (m_dwKind == PACKET_KIND_WRITE_SOCKET)
        {
        hFile = (HANDLE)m_Socket;
        }
    else if (m_dwKind == PACKET_KIND_WRITE_FILE)
        {
        hFile = m_hFile;
        m_Overlapped.Offset = dwOffset;
        }
    else
        {
        #ifdef DBG_ERROR
        DbgPrint("CIOPACKET::PostIoWrite(): Invalid m_dwKind: %d.\n",m_dwKind);
        #endif
        dwStatus = ERROR_INVALID_PARAMETER;
        }

    BOOL b = WriteFile( hFile,
                        pvBuffer,
                        dwBufferSize,
                        0,  // Can be zero for overlapped IO.
                        &m_Overlapped );

    if (!b)
        {
        dwStatus = GetLastError();
        if (dwStatus == ERROR_IO_PENDING)
            {
            dwStatus = NO_ERROR;
            }
        }

    #ifdef DBG_IO
    DbgPrint("CIOPACKET::PostIoWrite(): WriteFile(): Handle: %d Bytes: %d\n",
             hFile, dwBufferSize );
    #endif

    return dwStatus;
    }

//--------------------------------------------------------------------
// CIOPACKET::PostIo()
//
//--------------------------------------------------------------------
DWORD CIOPACKET::PostIo()
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwBytes;

    if (m_dwKind == PACKET_KIND_LISTEN)
        {
        dwStatus = PostIoListen();
        }
    else if (m_dwKind == PACKET_KIND_READ)
        {
        dwStatus = PostIoRead();
        }
    else
        {
        // Packet writes back to the camera (via socket) and writes to
        // the image (jpeg) file are posted only when data is ready to
        // send...
        ASSERT(  (m_dwKind == PACKET_KIND_WRITE_SOCKET)
                 || (m_dwKind == PACKET_KIND_WRITE_FILE) );
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CIOPACKET::GetSockAddrs()
//
// NOTE: Don't free the memory addresses returned, they point into
//       m_AcceptBuffer.
//--------------------------------------------------------------------
void CIOPACKET::GetSockAddrs( OUT SOCKADDR_IRDA **ppLocalAddr,
                              OUT SOCKADDR_IRDA **ppFromAddr )
    {
    int  iLocalAddrSize;
    int  iFromAddrSize;

    if (!m_pLocalAddr)
        {
        GetAcceptExSockaddrs(m_pAcceptBuffer,
                             0,
                             16 + sizeof(SOCKADDR_IRDA),
                             16 + sizeof(SOCKADDR_IRDA),
                             (struct sockaddr **)&m_pLocalAddr,
                             &iLocalAddrSize,
                             (struct sockaddr **)&m_pFromAddr,
                             &iFromAddrSize );
        }

    *ppLocalAddr = m_pLocalAddr;
    *ppFromAddr = m_pFromAddr;
    }