//
//  Copyright © 2001 - 2002, RESON Inc. All Rights Reserved.
//
//  No part of this file may be reproduced or transmitted in any form or by
//  any means, electronic or mechanical, including photocopy, recording, or
//  information storage or retrieval system, without permission in writing
//  from RESON Inc.
//
//  Filename:   SocketServer.cpp
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    Implements a socket server to support multiple client connections.
//
//  Notes:
//

#include "StdAfx.h"
#include "SocketServer.h"

//////////////////////////////////////////////////////////////////////
// CSocketServer class implementation.

IMPLEMENT_DYNAMIC( CSocketServer, CAsyncSocket )                                // Needed by CAsyncSocket base class as it's a CObject derived class.

CSocketServer::CSocketServer(   PFN_RECORD_READY        pfnDataReady,           // Data ready callback.
                                void                   *pvParam,
                                unsigned long           ulMaxMessageLength,     // Maximum message length.
                                unsigned int            uiPort )                // Server listen socket port number.
              :CAsyncSocket(),

               m_dwResumeFailCode( (DWORD) -1 )
{
    m_bInitialized         = false;                                             // Assume failure until we know otherwise.

    // Local copy of parameters.

    m_uiPort               = uiPort;
    m_iSocketType          = SOCK_STREAM;

    m_ulMaxMessageLength   = ulMaxMessageLength;

    // Callback routine and parameter.

    m_pfnCallback          = pfnDataReady;
    m_pvParam              = pvParam;

    m_dwServerThreadID     = ::AfxGetThread()->m_nThreadID;                     // Main server thread ID to post messages to.

    // Clear the socket list initially.

    m_SocketList.clear();

    // Initialte the socket server by creating the listening socket ready for clients to connect.

    if ( ! InitiateSocketServer() )
    {
        TRACE( _T( "CSocketServer::CSocketServer(), InitiateSocketServer() failure\n" ) );
        return;
    }

    // Only now is construction successfull.

    m_bInitialized = true;
}

CSocketServer::~CSocketServer( void )
{
    // Note: CAsyncSocket base class destructor will invoke Close() on our server listening socket.

    // Destroy remaining components starting with the connected sockets.

    __TRY
    {
        for ( SocketIterator_t pSocket = m_SocketList.begin(); pSocket != m_SocketList.end(); pSocket++ )
        {
            pSocket->Destroy();
        }

        // Remove all socket descriptors from the list.

        m_SocketList.clear();

    }
    __FINALLY
    {
        // And reset all remaining state data.

        m_uiPort                = 0;
        m_iSocketType           = SOCK_STREAM;
        m_ulMaxMessageLength    = 0UL;
        m_dwServerThreadID      = 0;
        m_pfnCallback           = NULL;
        m_pvParam               = NULL;
        m_bInitialized          = false;
    }
    __ENDFINALLY

}

bool CSocketServer::IsInitialized( void ) const
{
    return m_bInitialized;
}

void CSocketServer::OnAccept( int iErrorCode )
{
    bool bSuccess = false;      //  Assume failure for now.

    if ( iErrorCode != NO_ERROR )
    {
        TRACE( _T( "CSocketServer::OnAccept(), with error code %d\n" ), iErrorCode );
    }
    else
    {
        int                 iIndex  = -1;
        C7kThreadedSocket  *pSocket = NULL;

        try
        {
            pSocket = new C7kThreadedSocket(    m_pfnCallback,              // pfnCallback
                                                m_pvParam,                  // lpvParam
                                                NULL,                       // pszAddress
                                                m_uiPort,                   // uiPort
                                                m_ulMaxMessageLength,       // ulMaxMessageLength
                                                m_iSocketType );            // iSocketType

            if ( pSocket == NULL )
            {
                throw socketErrorInvalidObject;
            }

            Sleep( 200 );

            iIndex = GetAvailableIndex();

            // Construct our client socket descriptor and push it on our list of clients.

            PushSocketOnList( iIndex, pSocket );

            // Accept the connection from the listening socket.

            if ( ! Accept( pSocket->GetSocket() ) )
            {
                throw socketErrorSocketAccept;
            }

            // Set the server socket's options.

            if ( ! pSocket->SetServerSocketOptions( true, iIndex ) )
            {
                throw socketErrorSocketMode;
            }

            // Release our suspended socket read thread (i.e. make it schedulable).

            if ( pSocket->ResumeThread() == m_dwResumeFailCode )
            {
                throw socketErrorThreadError;
            }

            // All must be ok.

            bSuccess = true;
        }
        catch ( const ESOCKETERRORCODES &reErrorCode )
        {
            bSuccess = false;
            TRACE( _T( "CSocketServer::Accept(), Error code %d ...!\n" ), reErrorCode );
        }
        catch ( ... )
        {
            bSuccess = false;
            TRACE( _T( "CSocketServer::Accept(), Unknown exception caught...!\n" ) );
        }

        // If we failed, then remove the queued threaded socket object from the list.

        if ( ! bSuccess )
        {
            __TRY
            {
                if ( pSocket != NULL )
                {
                    delete pSocket;
                    pSocket = NULL;
                }
            }
            __FINALLY
            {
                ASSERT( pSocket == NULL );
                pSocket = NULL;
            }
            __ENDFINALLY
        }
    }
}

void CSocketServer::OnClose( int iErrorCode )
{
#ifdef _DEBUG

    TRACE( _T( "CSocketServer::OnClose error detected(%d)\n" ), iErrorCode );

#else

    UNREFERENCED_PARAMETER( iErrorCode );

#endif

    Close();                        // Close the server listening socket (different to the data socket).
}

bool CSocketServer::InitiateSocketServer( void )
{
    // Creates and binds the server listening socket.

    // NB: the only notifications we're interested in are read and close.

    bool        bSuccess = false;

    int         iLastError = socketErrorNone;

    const long  lServerSocketEvents = FD_CLOSE | FD_ACCEPT;

    BOOL bCreateStatus = Create( m_uiPort, m_iSocketType, lServerSocketEvents, NULL );

    if ( ! bCreateStatus )
    {
        iLastError = GetLastError();
    }

    if ( bCreateStatus || ( iLastError == WSAEWOULDBLOCK ) )
    {
        // Important !!!
        // Pause here otherwise the connection reports as ok but you will never see any socket data.
        // This is probably because we're in non-blocking mode and Create() is incomplete before 
        // Connect() is attempted.

        Sleep( 500 );

        // Listen for incoming connection requests.

        if ( Listen() )
        {
            bSuccess = TRUE;
        }
        else
        {
            TRACE( _T( "CSocketServer::InitiateSocketServer(), Listen() error(%d)\n" ), GetLastError() );
        }
    }
    else
    {
        TRACE( _T( "CSocketServer::InitiateSocketServer(), Create() error(%d)\n" ), iLastError );
    }

    return bSuccess;
}

void CSocketServer::DestroySocket( const int &riSocketIndex )
{
    for ( SocketIterator_t pSocket = m_SocketList.begin(); pSocket != m_SocketList.end(); pSocket++ )
    {
        if ( ( riSocketIndex      == pSocket->m_iIndex ) &&
             ( pSocket->m_pSocket != NULL )               )
        {
            // Destroy the descriptor along with its index and socket object.

            pSocket->Destroy();

            // Remove its descriptor from the linked list.

            m_SocketList.erase( pSocket );

            break;
        }
    }
}

DWORD CSocketServer::GetServerThreadID( void ) const
{
    return m_dwServerThreadID;
}

bool CSocketServer::Send( BYTE *pbyStream, unsigned long ulNumBytes, int iSocketIndex )
{
    bool bSuccess = false;

    try
    {
        C7kThreadedSocket *pSocket = GetSocket( iSocketIndex );

        ASSERT( pSocket != NULL );
        
        if ( pSocket != NULL )
        {
            bSuccess = pSocket->Send( pbyStream, ulNumBytes );
        }
    }
    catch( ... )
    {
        TRACE( _T( "CSocketServer::Send(), Exception caught\n" ) );
        bSuccess = false;
    }

    return bSuccess;
}

bool CSocketServer::PushSocketOnList(   const int               &riIndex,
                                        C7kThreadedSocket   *   &rpSocket )
{
    bool bSuccess = false;          // Assume failure for now.

    ASSERT( ( riIndex != -1 ) && ( rpSocket != NULL ) );

    SOCKETDESCRIPTOR sSocketDescriptor( riIndex, rpSocket );

    m_SocketList.push_back( sSocketDescriptor );

    return bSuccess;
}

C7kThreadedSocket * CSocketServer::GetSocket( const int &riSocketIndex )
{
    C7kThreadedSocket *pThreadedSocket = NULL;

    SocketIterator_t    pSocket = std::find( m_SocketList.begin(), m_SocketList.end(), riSocketIndex );

    if ( pSocket != m_SocketList.end() )
    {
        if ( pSocket->IsValid() && ( pSocket->m_iIndex == riSocketIndex ) )
        {
            pThreadedSocket = pSocket->m_pSocket;
        }
    }
    else
    {
        TRACE( _T( "CSocketServer::GetSocket(), Index %d not in list\n" ), riSocketIndex );
        ASSERT( false );
    }

    return pThreadedSocket;
}

int CSocketServer::GetAvailableIndex( void )
{
    int iIndex = -1;

    if ( m_SocketList.empty() )
    {
        iIndex = 0;
    }
    else
    {
        // Make a temprary list of indexes and sort these so that we don't affect
        // the original m_SocketList. We may wish to maintain iterator indexes into the list 
        // later on.

        typedef std::list<int>          IndexList_t;
        typedef IndexList_t::iterator   IndexIterator_t;

        IndexList_t IndexList;

        for ( SocketIterator_t pSocket = m_SocketList.begin(); pSocket != m_SocketList.end(); pSocket++ )
        {
            IndexList.push_back( pSocket->m_iIndex );
        }

        // Sort the list by index (ascending order).

        IndexList.sort();

        // Search the list and look for the first available index discontinuity and use that otherwise,
        // just use 1 past the last item in the sorted list.

        IndexIterator_t pIndex = IndexList.begin();

        while ( pIndex != IndexList.end() )
        {
            int iIndex1 = *pIndex;

            pIndex++;

            if ( pIndex != IndexList.end() )
            {
                if ( *pIndex > ( iIndex1 + 1 ) )
                {
                    iIndex = iIndex1 + 1;
                    break;
                }
            }
            else
            {
                iIndex = iIndex1 + 1;
                break;
            }
        }

        IndexList.clear();
    }

    return iIndex;
}

