//
//  Copyright (c) 2001, Reson, Inc. All Rights Reserved.
//
//  Filename:   SocketServer.h
//
//  Project:    6046.
//
//  Author(s):  W. Arcus
//
//  Purpose:    
//
//  Notes:
//

#if !defined(AFX_SOCKETSERVER_H__6F89206F_A46A_4859_9C3C_1182452B9952__INCLUDED_)
#define AFX_SOCKETSERVER_H__6F89206F_A46A_4859_9C3C_1182452B9952__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#pragma warning( push, 3 )                                          // Microsoft's STL list implementation is dirty at warning level 4.
#include <list>
#include <algorithm>
#pragma warning( pop )

#include "ThreadedSocket.h"

template <class tProtocol>
class EXPORT_DLL CSocketServer : public CAsyncSocket
{
    //DECLARE_DYNAMIC( CSocketServer )

public:

    // Construction and destruction.

                    CSocketServer       (   PFN_RECORD_READY        pfnDataReady,           // Data ready callback.
                                            unsigned long           ulMaxMessageLength,     // Maximum message length.
                                            unsigned int            uiPort );               // Server listen socket port number.

    virtual        ~CSocketServer       (   void );                                         // Destructor.

    bool            IsInitialized       (   void ) const;                                   // Returns the construction success/failure status.

    bool            Send                (   BYTE                   *pbyStream,
                                            unsigned long           ulNumBytes,
                                            int                     iSocketIndex );

    void            DestroySocket       (   const int              &riSocketIndex );

    DWORD           GetServerThreadID   (   void ) const;

private:

    // Definitions.

    typedef struct tagDESCRIPTOR
    {
        int                         m_iIndex;
        CThreadedSocket<tProtocol>  *m_pSocket;

        tagDESCRIPTOR(  const int                           &riIndex  = -1,
                        CThreadedSocket<tProtocol>  * const &rpSocket = NULL )
        {
            m_iIndex  = riIndex;
            m_pSocket = rpSocket;
        }

        virtual ~tagDESCRIPTOR( void )
        {
            m_iIndex  = -1;
            m_pSocket = NULL;
        }

        void Destroy( void )
        {
            if ( IsValid() )
            {
                delete m_pSocket;
                m_pSocket = NULL;

                m_iIndex = -1;
            }
        }

        bool IsValid( void ) const
        {
            return ( ( m_iIndex != -1 ) && ( m_pSocket != NULL ) );
        }

        bool operator < ( const tagDESCRIPTOR & rRhs ) const
        {
            return ( m_iIndex < rRhs.m_iIndex );
        }

        bool operator == ( const tagDESCRIPTOR & rRhs ) const
        {
            return ( m_iIndex == rRhs.m_iIndex );
        }

    }
    SOCKETDESCRIPTOR;

    typedef std::list<SOCKETDESCRIPTOR>     SocketList_t;
    typedef SocketList_t::iterator          SocketIterator_t;
    typedef SocketList_t::const_iterator    ConstantSocketIterator_t;

    // Attributes.

    bool                            m_bInitialized;

    int                             m_iSocketType;
    unsigned int                    m_uiPort;
    unsigned long                   m_ulMaxMessageLength;
    DWORD                           m_dwServerThreadID;

    PFN_RECORD_READY                m_pfnCallback;
    SocketList_t                    m_SocketList;

    // Services.

    bool                            InitiateSocketServer    (   void );
                        
    int                             GetAvailableIndex       (   void );
                        
    CThreadedSocket<tProtocol> *    GetSocket               (   const int           &riSocketIndex );
                        
    bool                            PushSocketOnList        (   const int                       &riIndex,
                                                                CThreadedSocket<tProtocol>  *   &rpSocket );

    // Overrides for CAsyncSocket's virtual notification handlers.

    void                            OnClose                 (   int                 iErrorCode );
    void                            OnAccept                (   int                 iErrorCode );

    // Make the default and copy constructors and the assignment operator private so that we get a 
    // compile error if the compiler uses them behind our back.

                                    CSocketServer           (   void );
                                    CSocketServer           (   const CSocketServer<tProtocol> &rRhs );
    CSocketServer &                 operator =              (   const CSocketServer<tProtocol> &rRhs );


};

//////////////////////////////////////////////////////////////////////
// CSocketServer class implementation.

//IMPLEMENT_DYNAMIC( CSocketServer, CAsyncSocket )                                // Needed by CAsyncSocket base class as it's a CObject derived class.

template <class tProtocol>
CSocketServer<tProtocol>::CSocketServer(    PFN_RECORD_READY        pfnDataReady,           // Data ready callback.
                                            unsigned long           ulMaxMessageLength,     // Maximum message length.
                                            unsigned int            uiPort )                // Server listen socket port number.

              :CAsyncSocket()
{
    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_dwServerThreadID     = ::AfxGetThread()->m_nThreadID;                     // Main server thread ID to post messages to.

    // Empty the socket list initially.

    m_SocketList.empty();

    // Initialte the socket server by creating the listening socket ready for clients to connect.

    if ( ! InitiateSocketServer() )
    {
        TRACE( _T( "CSocketServer<tProtocol>::CSocketServer(), InitiateSocketServer() failure\n" ) );
        return;
    }

    // Only now is construction successfull.

    m_bInitialized = true;
}

template <class tProtocol>
CSocketServer<tProtocol>::CSocketServer( void )                            // Not implemented.
{
    ASSERT( false );        // Force an assertion failure.

    m_bInitialized          = false;

    m_uiPort                = 0;
    m_iSocketType           = SOCK_STREAM;

    m_ulMaxMessageLength    = 0UL;

    m_dwServerThreadID      = 0;

    m_pfnCallback           = NULL;
}

template <class tProtocol>
CSocketServer<tProtocol>::~CSocketServer( void )
{
    // Note: CAsyncSocket base class destructor will invoke Close() on our server listening socket.

    // Destroy remaining components starting with the connected sockets.

    for ( SocketIterator_t pSocket = m_SocketList.begin(); pSocket != m_SocketList.end(); pSocket++ )
    {
        pSocket->Destroy();
    }

    // Remove all socket descriptors from the list.

    m_SocketList.clear();

    // And reset all remaining state data.

    m_uiPort                = 0;
    m_iSocketType           = SOCK_STREAM;

    m_ulMaxMessageLength    = 0UL;

    m_dwServerThreadID      = 0;

    m_pfnCallback           = NULL;
    m_bInitialized          = false;
}

template <class tProtocol>
bool CSocketServer<tProtocol>::IsInitialized( void ) const
{
    return m_bInitialized;
}

template <class tProtocol>
void CSocketServer<tProtocol>::OnAccept( int iErrorCode )
{
    if ( iErrorCode != NO_ERROR )
    {
        TRACE( _T( "CSocketServer<tProtocol>::OnAccept(), with error code %d\n" ), iErrorCode );
        return;
    }

    bool    bSuccess = false;      //  Assume failure for now.
    int     iIndex = -1;

    try
    {
        CThreadedSocket<tProtocol> *pSocket = new CThreadedSocket<tProtocol>(   m_pfnCallback,              // pfnCallback
                                                                                this,                       // lpvParam
                                                                                NULL,                       // pszAddress
                                                                                m_uiPort,                   // uiPort
                                                                                m_ulMaxMessageLength,       // ulMaxMessageLength
                                                                                m_iSocketType );            // iSocketType

        if ( pSocket == NULL )
        {
            throw socketErrorInvalidObject;
        }

#pragma CompileMessage_m( "TODO: Investigate more deterministic way of waiting and correct time value otherwise" )

        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() )
        {
            throw socketErrorSocketMode;
        }

        // Release our suspended socket read thread (i.e. make it schedulable).

        if ( pSocket->ResumeThread() == 0xffffffff )
        {
            throw socketErrorThreadError;
        }

        // All must be ok.

        bSuccess = true;
    }
    catch ( const ESOCKETERRORCODES &reErrorCode )
    {
        bSuccess = false;
        TRACE( _T( "CSocketServer<tProtocol>::Accept(), Error code %d ...!\n" ), reErrorCode );
    }
    catch ( ... )
    {
        bSuccess = false;
        TRACE( _T( "CSocketServer<tProtocol>::Accept(), Unknown exception caught...!\n" ) );
    }

    // If we failed, then remove the queued threaded socket object from the list.

    if ( ( ! bSuccess ) && ( iIndex != -1 ) )
    {
        delete pSocket;
        pSocket = NULL;
    }
}

template <class tProtocol>
void CSocketServer<tProtocol>::OnClose( int iErrorCode )
{
    TRACE( _T( "CSocketServer<tProtocol>::OnClose error detected(%d)\n" ), iErrorCode );

    Close();                        // Close the server listening socket (different to the data socket).
}

template <class tProtocol>
bool CSocketServer<tProtocol>::InitiateSocketServer( void )
{
    // Creates and binds the server listening socket.
    // Returns false if an error occured otherwise true.

    // The only notifications we are 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.

#pragma CompileMessage_m( "TODO: Investigate ways to correctly wait w/o using Sleep() or similar." )

        Sleep( 500 );

        // Listen for incoming connection requests.

        if ( Listen() )
        {
            bSuccess = TRUE;
        }
        else
        {
            TRACE( _T( "CSocketServer<tProtocol>::InitiateSocketServer(), Listen() error(%d)\n" ), GetLastError() );
        }
    }
    else
    {
        TRACE( _T( "CSocketServer<tProtocol>::InitiateSocketServer(), Create() error(%d)\n" ), iLastError );
    }

    return bSuccess;
}

template <class tProtocol>
void CSocketServer<tProtocol>::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;
        }
    }
}

template <class tProtocol>
DWORD CSocketServer<tProtocol>::GetServerThreadID( void ) const
{
    return m_dwServerThreadID;
}

template <class tProtocol>
bool CSocketServer<tProtocol>::Send( BYTE *pbyStream, unsigned long ulNumBytes, int iSocketIndex )
{
    bool bSuccess = false;

    try
    {
        CThreadedSocket *pSocket;
        
        if ( ( pSocket = GetSocket( iSocketIndex ) ) != NULL )
        {
            bSuccess = pSocket->Send( pbyStream, ulNumBytes );
        }
    }
    catch( ... )
    {
        TRACE( _T( "CSocketServer<tProtocol>::Send(), Exception caught\n" ) );
        bSuccess = false;
    }

    return bSuccess;
}

template <class tProtocol>
bool CSocketServer<tProtocol>::PushSocketOnList(    const int                     &riIndex,
                                                    CThreadedSocket<tProtocol>  * &rpSocket )
{
    bool bSuccess = false;          // Assume failure for now.

    ASSERT( ( riIndex != -1 ) && ( rpSocket != NULL ) );

    SOCKETDESCRIPTOR sSocketDescriptor( riIndex, rpSocket );

    m_SocketList.push_back( sSocketDescriptor );

    return bSuccess;
}

template <class tProtocol>
CThreadedSocket<tProtocol> * CSocketServer<tProtocol>::GetSocket( const int &riSocketIndex )
{
    CThreadedSocket    *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<tProtocol>::GetSocket(), Index %d not in list\n" ), riSocketIndex );
        ASSERT( false );
    }

    return pThreadedSocket;
}

template <class tProtocol>
int CSocketServer<tProtocol>::GetAvailableIndex( void )
{
    int iIndex = -1;

    if ( ! m_SocketList.empty() )
    {
        // 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;
}

#endif // !defined(AFX_SOCKETSERVER_H__6F89206F_A46A_4859_9C3C_1182452B9952__INCLUDED_)
