////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2019 (c) Baical                                                        /
//                                                                             /
// This library is free software; you can redistribute it and/or               /
// modify it under the terms of the GNU Lesser General Public                  /
// License as published by the Free Software Foundation; either                /
// version 3.0 of the License, or (at your option) any later version.          /
//                                                                             /
// This library is distributed in the hope that it will be useful,             /
// but WITHOUT ANY WARRANTY; without even the implied warranty of              /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU           /
// Lesser General Public License for more details.                             /
//                                                                             /
// You should have received a copy of the GNU Lesser General Public            /
// License along with this library.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#include "CommonServer.h"
//#include <WinBase.h>

#if (JOURNAL_MODULE != NULL)
    #pragma message ("ERROR: Journal module is already defined")
#else
    #undef JOURNAL_MODULE
#endif

#define JOURNAL_MODULE m_hLogMod

#define DELETE_CONNECTION_EVENT                               (MEVENT_SIGNAL_0)
#define THREAD_EXIT_EVENT                                     (MEVENT_SIGNAL_0)

////////////////////////////////////////////////////////////////////////////////
//Uncomment this macro to activate perofrmance monitoring of communication thread
//It will generate telemetry per each important action.
//Yes, I'm debugging P7 using P7:
//------------------------------------------------------------------------------
//#define PERFORMANCE_MONITORING

#if defined(PERFORMANCE_MONITORING)
    #include "P7_Client.h"
    #include "P7_Trace.h"
    #include "P7_Telemetry.h"
    #include "P7_Extensions.h"
    #define PERFORMANCE_MONITOR_ADD(i_bCounter, i_llValue) l_iP7_Tel->Add(i_bCounter, i_llValue);

#else
    #define PERFORMANCE_MONITOR_ADD(i_bCounter, i_llValue)
#endif


////////////////////////////////////////////////////////////////////////////////
//CSAddress
CSAddress::CSAddress(IJournal         *i_pLog, 
                     CBuffers_Pools   *i_pBPool,
                     sockaddr_storage *i_pAddress,
                     tUINT32           i_dwID)
    : m_lBytesIn(0)
    , m_lBytesOut(0)
    , m_eStatus(ESERVER_STATUS_OK)
    , m_pSocket(NULL)
    , m_pLog(i_pLog)
    , m_hLogMod(NULL)
    , m_pConnections(NULL)
    , m_pConnections_New(NULL)
    , m_pConnections_Active(NULL)
    , m_pBPool(i_pBPool)
    , m_dwID(i_dwID)
    , m_dwConn_ID(0)
    , m_hThread(0) //NULL
    , m_bIsThread(FALSE)
    , m_pDel_Connection(NULL)
    , m_pList_Cell(NULL)
{
    if (m_pLog)
    {
        m_pLog->Add_Ref();
        m_pLog->Register_Module(TM("Srv/Addr"), &m_hLogMod);
    }

    LOCK_CREATE(m_tCS);

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        if (FALSE == m_cDel_Event.Init(1, EMEVENT_SINGLE_MANUAL))
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("[%X] Event creation failed"), m_dwID);
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        if (FALSE == m_cExit_Event.Init(1, EMEVENT_SINGLE_AUTO))
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("[%X] Event creation failed"), m_dwID);
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        m_pSocket = new CUDP_Socket(m_pLog, (sockaddr*)i_pAddress, TRUE);
        if (    (NULL == m_pSocket)
             || (FALSE == m_pSocket->Initialized())
           )
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("[%X] Socket intialization failed"), m_dwID);
        }
        else
        {
            JOURNAL_INFO(m_pLog, TM("[%X] Socket send buffer size %d, receive %d"),
                         m_dwID, m_pSocket->GetSendBufferSize(), m_pSocket->GetRecvBufferSize());
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        m_pConnections = new CBList<CSConnection*>;
        if (NULL == m_pConnections)
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("[%X] Connections list creation failed"), m_dwID);
        }
        else
        {
            //fill list by preallocated cells
            for (tUINT32 l_dwI = 0; l_dwI < 4096; l_dwI++)
            {
                m_pConnections->Add_After(m_pConnections->Get_Last(), NULL);
            }

            //build the index
            (*m_pConnections)[0];
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        m_pConnections_New = new CBList<CSConnection*>;
        if (NULL == m_pConnections_New)
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("[%X] New Connections list creation failed"), m_dwID);
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        m_pConnections_Active = new CBList<CSConnection*>;
        if (NULL == m_pConnections_Active)
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("[%X] Active Connections list creation failed"), m_dwID);
        }
    }
    
    if (ESERVER_STATUS_OK == m_eStatus)
    {
        if (FALSE == CThShell::Create(&Static_Routine, 
                                      this,
                                      &m_hThread,
                                      TM("Srv/Addr")               
                                     )
           )
        {
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
            JOURNAL_ERROR(m_pLog, TM("Thread wasn't created !"));
        }
        else
        {
            m_bIsThread = TRUE;
        }
    }
}//CSAddress


////////////////////////////////////////////////////////////////////////////////
//~CSAddress
CSAddress::~CSAddress()
{
    if (m_bIsThread)
    {
        m_cExit_Event.Set(THREAD_EXIT_EVENT);
        if (TRUE == CThShell::Close(m_hThread, 15000))
        {
            m_hThread   = 0;//NULL;
            m_bIsThread = FALSE;
        }
        else
        {
            JOURNAL_CRITICAL(m_pLog, TM("Can't close channels thread !"));
        }
    }

    if (m_pConnections_New)
    {
        m_pConnections_New->Clear(FALSE);
        delete m_pConnections_New;
        m_pConnections_New = NULL;
    }

    if (m_pConnections_Active)
    {
        m_pConnections_Active->Clear(FALSE);
        delete m_pConnections_Active;
        m_pConnections_Active = NULL;
    }

    if (m_pConnections)
    {
        m_pConnections->Clear(TRUE);
        delete m_pConnections;
        m_pConnections = NULL;
    }

    if (m_pSocket)
    {
        delete m_pSocket;
        m_pSocket = NULL;
    }

    if (m_pLog)
    {
        m_pLog->Release();
        m_pLog = NULL;
    }

    LOCK_DESTROY(m_tCS);
}//CSAddress


////////////////////////////////////////////////////////////////////////////////
//Get_Status
eServer_Status CSAddress::Get_Status()
{
    return m_eStatus;
}//Get_Status


////////////////////////////////////////////////////////////////////////////////
//Get_Socket_Address
sockaddr_storage *CSAddress::Get_Socket_Address()
{
    if (    (m_pSocket)
         && (m_pSocket->Initialized())
       )
    {
        return m_pSocket->Get_Address();
    }

    return NULL;
}//Get_Socket_Address


////////////////////////////////////////////////////////////////////////////////
//Get_Stat
tBOOL CSAddress::Get_Stat(tUINT32 &o_rBytesIn, tUINT32 &o_rBytesOut)
{
    o_rBytesIn  = m_lBytesIn;
    o_rBytesOut = m_lBytesOut;

    ATOMIC_SET(&m_lBytesIn, 0);
    ATOMIC_SET(&m_lBytesOut, 0);

    return TRUE;
}//Get_Stat


////////////////////////////////////////////////////////////////////////////////
//Get_Next_Conn
IP7S_Connnection *CSAddress::Get_Next_Conn(IP7S_Connnection *i_pConn)
{
    IP7S_Connnection *l_pResult = NULL;
    pAList_Cell       l_pCell   = NULL;

    LOCK_ENTER(m_tCS); 

    //m_pConnections is combination or array & list, some cells can be NULL
    //and our main objective to find next NOT NULL cell
    l_pCell = m_pConnections->Get_Next( (i_pConn) ?
                                        (static_cast<CSConnection*>(i_pConn))->Get_Cell() 
                                       : NULL
                                      );
    while (l_pCell)
    {
        l_pResult = m_pConnections->Get_Data(l_pCell);
        if (NULL == l_pResult)
        {
            l_pCell = m_pConnections->Get_Next(l_pCell);
        }
        else
        {
            break;
        }
    }

    LOCK_EXIT(m_tCS); 

    return l_pResult;
}//Get_Next_Conn


////////////////////////////////////////////////////////////////////////////////
//Get_Next_Conn
IP7S_Connnection *CSAddress::Get_New_Conn()
{
    IP7S_Connnection *l_pResult = NULL;
    pAList_Cell       l_pCell   = NULL;

    LOCK_ENTER(m_tCS); 


    l_pCell = m_pConnections_New->Get_First();
        
    if (l_pCell)
    {
        l_pResult = m_pConnections_New->Get_Data(l_pCell);
        m_pConnections_New->Del(l_pCell, FALSE);
    }

    LOCK_EXIT(m_tCS); 

    return l_pResult;
}//Get_Next_Conn


////////////////////////////////////////////////////////////////////////////////
//Del_Conn
tBOOL CSAddress::Del_Conn(IP7S_Connnection *i_pConn)
{
    tBOOL   l_bResult   = FALSE;
    tUINT32 l_dwPause   = 5;
    tUINT32 l_dwWaiting = 0;

    if (NULL == i_pConn)
    {
        return l_bResult;
    }

    if (DELETE_CONNECTION_EVENT == m_cDel_Event.Wait(0u))
    {
        JOURNAL_ERROR(m_pLog, TM("[%X] Skipped %X, another connection is currently removing"),
                      m_dwID,
                      static_cast<CSConnection *>(m_pDel_Connection)->Get_ID()
                      );
        return l_bResult;
    }

    LOCK_ENTER(m_tCS);
    m_pDel_Connection = static_cast<CSConnection *>(i_pConn);
    JOURNAL_DEBUG(m_pLog,
                  TM("[%X] Deleting connection :%X"),
                  m_dwID,
                  m_pDel_Connection ? m_pDel_Connection->Get_ID() : 0xFFFFFFFF
                 );
    LOCK_EXIT(m_tCS);

    l_bResult = TRUE;
    m_cDel_Event.Set(DELETE_CONNECTION_EVENT);

    while (DELETE_CONNECTION_EVENT == m_cDel_Event.Wait(0))
    {
        CThShell::Sleep(l_dwPause);
        l_dwWaiting += l_dwPause;

        if (l_dwWaiting > 15000)
        {
            l_bResult = FALSE;
            JOURNAL_ERROR(m_pLog, TM("[%X] Deleting connection failed"), m_dwID);
            break;
        }
    }

    LOCK_ENTER(m_tCS); 
    if (    (l_bResult)
         && (m_pDel_Connection)
       )
    {
        JOURNAL_ERROR(m_pLog, TM("[%X] Deleting connection failed"), m_dwID);
        l_bResult = FALSE;
    }
    LOCK_EXIT(m_tCS); 
    
    return l_bResult;
}//Del_Conn


////////////////////////////////////////////////////////////////////////////////
//Set_Cell
void CSAddress::Set_Cell(pAList_Cell i_pCell)
{
    m_pList_Cell = i_pCell;
}//Set_Cell


////////////////////////////////////////////////////////////////////////////////
//Get_Cell
pAList_Cell CSAddress::Get_Cell()
{
    return m_pList_Cell;
}//Get_Cell


////////////////////////////////////////////////////////////////////////////////
//Add_Conn
CSConnection *CSAddress::Add_Conn(sockaddr_storage *i_pAddress, tBOOL i_bBigEndian, tINT32 i_iSocketBufferSize)
{
    CSConnection *l_pResult = NULL;

    if (NULL == i_pAddress)
    {
        return l_pResult;
    }

    m_dwConn_ID ++;

    l_pResult = new CSConnection(m_pLog, 
                                 m_pBPool, 
                                 i_pAddress, 
                                 (m_dwID << 24) + m_dwConn_ID,
                                 i_bBigEndian,
                                 i_iSocketBufferSize
                                );

    if (    (l_pResult)
         && (ESERVER_STATUS_OK != l_pResult->Get_Status())
       )
    {
        delete l_pResult;
        l_pResult = NULL;
    }

    if (l_pResult)
    {
        LOCK_ENTER(m_tCS); 

        //add to new connections list
        m_pConnections_New->Add_After(m_pConnections_New->Get_Last(), l_pResult);
        //add to active connections list
        m_pConnections_Active->Add_After(m_pConnections_Active->Get_Last(), l_pResult);

        //try to find empty cell for connection
        pAList_Cell l_pEl   = NULL;
        tUINT16     l_wIDX  = 0;
        tBOOL       l_bFind = FALSE;

        while ((l_pEl = m_pConnections->Get_Next(l_pEl)))
        {
            if (NULL == m_pConnections->Get_Data(l_pEl))
            {
                m_pConnections->Put_Data(l_pEl, l_pResult, TRUE);
                l_pResult->Set_Client_ID(l_wIDX);
                l_pResult->Set_Cell(l_pEl);
                l_bFind = TRUE;
                break;
            }
            else
            {
                l_wIDX ++;
            }
        }

        //if empty cell was not found, we create new one 
        if (FALSE == l_bFind)
        {
            l_pResult->Set_Cell(m_pConnections->Add_After(m_pConnections->Get_Last(), l_pResult));
            l_pResult->Set_Client_ID((tUINT16)(m_pConnections->Count() - 1));
        }

        JOURNAL_INFO(m_pLog, 
                     TM("[%X] Add connection %X, Client ID = %u, List length %u"), 
                     (tUINT32)m_dwID,
                     (tUINT32)l_pResult->Get_ID(),
                     (tUINT32)l_pResult->Get_Client_ID(),
                     (tUINT32)m_pConnections_Active->Count()
                    );

        LOCK_EXIT(m_tCS); 
    }
    else
    {
        JOURNAL_ERROR(m_pLog, TM("[%X] Connection creation failed"), m_dwID);
    }

    return l_pResult;
} //Add_Conn


////////////////////////////////////////////////////////////////////////////////
//Print_Connections
void CSAddress::Print_Connections()
{
    if (    (NULL == m_pLog)
         || (IJournal::eLEVEL_INFO < m_pLog->Get_Verbosity())  //MXU: doesn't work with P7 proxy or at all
         || (0 == m_pConnections_Active->Count())
       )
    {
        return;
    }

    JOURNAL_INFO(m_pLog,
                 TM("[%X] Count of active connections : %u"), 
                 (tUINT32)m_dwID,
                 (tUINT32)m_pConnections_Active->Count()
                );

    CSConnection *l_pConnection = NULL;
    pAList_Cell   l_pCell       = NULL;

    l_pCell = NULL;
    while ((l_pCell = m_pConnections_Active->Get_Next(l_pCell)) != NULL)
    {
        l_pConnection = m_pConnections_Active->Get_Data(l_pCell);
        if (l_pConnection)
        {
            l_pConnection->Print();
        }
    }

}//Print_Connections


////////////////////////////////////////////////////////////////////////////////
//Routine
void CSAddress::Routine() 
{
    sockaddr_storage l_tSenderAddr     = {};
    tUINT32          l_dwReceived      = 0;
    pAList_Cell      l_pConnecton_Cell = NULL;
    CSConnection    *l_pConnection     = NULL;
    CTPacket        *l_pPacket_Recv    = NULL;
    const tUINT8    *l_pPacket_Sent    = NULL;
    tUINT32          l_uPacket_Size    = 0;
    tBOOL            l_bExit           = FALSE;
    sH_Common        l_sHeader         = {};
    tUINT32          l_dwPrint_Tick    = GetTickCount();
    tBOOL            l_bBigEndian      = 0;

#if defined(PERFORMANCE_MONITORING)
    tUINT16          l_bTidGlobal      = 255;
    tUINT16          l_bTidDelete      = 255;
    tUINT16          l_bTidRecvReady   = 255;
    tUINT16          l_bTidPick        = 255;
    tUINT16          l_bTidRecv        = 255;
    tUINT16          l_bTidSend        = 255;
    IP7_Telemetry   *l_iP7_Tel         = P7_Get_Shared_Telemetry(TM("BK:Tl"));
    tXCHAR           l_pCName[128];

    sprintf(l_pCName, "Addr[%d]/1.Global", m_dwID);
    l_iP7_Tel->Create(l_pCName, 0.0, 0.0, 1.0, 2.0, 1.0, &l_bTidGlobal);

    sprintf(l_pCName, "Addr[%d]/2.Delete", m_dwID);
    l_iP7_Tel->Create(l_pCName, 0.0, 0.0, 1.0, 2.0, 1.0, &l_bTidDelete);

    sprintf(l_pCName, "Addr[%d]/3.Receive Ready", m_dwID);
    l_iP7_Tel->Create(l_pCName, 0.0, 0.0, 1.0, 2.0, 1.0, &l_bTidRecvReady);

    sprintf(l_pCName, "Addr[%d]/4.Pick", m_dwID);
    l_iP7_Tel->Create(l_pCName, 0.0, 0.0, 1.0, 2.0, 1.0, &l_bTidPick);

    sprintf(l_pCName, "Addr[%d]/5.Receive", m_dwID);
    l_iP7_Tel->Create(l_pCName, 0.0, 0.0, 1.0, 2.0, 1.0, &l_bTidRecv);

    sprintf(l_pCName, "Addr[%d]/6.Send", m_dwID);
    l_iP7_Tel->Create(l_pCName, 0.0, 0.0, 1.0, 2.0, 1.0, &l_bTidSend);
#endif

    if (m_pLog)
    {
        m_pLog->Register_Thread(TM("SrvAddr"), 0);
    }

    JOURNAL_INFO(m_pLog, TM("[%X] Start ..."), m_dwID);

    while (FALSE == l_bExit)
    {   
        PERFORMANCE_MONITOR_ADD(l_bTidGlobal, 0);
        if (THREAD_EXIT_EVENT == m_cExit_Event.Wait(0)) //server is optimized for data exchange, wait on receive
        {
            l_bExit = TRUE;
        }
        PERFORMANCE_MONITOR_ADD(l_bTidGlobal, 1);

        PERFORMANCE_MONITOR_ADD(l_bTidDelete, 1);
        if (DELETE_CONNECTION_EVENT == m_cDel_Event.Wait(0)) //server is optimized for data exchange, wait on receive
        {
            if (m_pConnections)
            {
                LOCK_ENTER(m_tCS); 
                if (m_pDel_Connection)
                {
                    pAList_Cell l_pCell = NULL;
                    while ((l_pCell = m_pConnections_New->Get_Next(l_pCell)) != NULL)
                    {
                        if (m_pConnections_New->Get_Data(l_pCell) == m_pDel_Connection)
                        {
                            JOURNAL_INFO(m_pLog,
                                         TM("[%X] Delete New connection %X"),
                                         m_dwID,
                                         m_pDel_Connection->Get_ID()
                                        );
                            m_pConnections_New->Del(l_pCell, FALSE);
                            break;
                        }
                    }

                    l_pCell = NULL;
                    while ((l_pCell = m_pConnections_Active->Get_Next(l_pCell)) != NULL)
                    {
                        if (m_pConnections_Active->Get_Data(l_pCell) == m_pDel_Connection)
                        {
                            JOURNAL_INFO(m_pLog,
                                         TM("[%X] Delete Active connection %X"),
                                         m_dwID,
                                         m_pDel_Connection->Get_ID()
                                        );
                            m_pConnections_Active->Del(l_pCell, FALSE);
                            break;
                        }
                    }

                    JOURNAL_INFO(m_pLog,
                                 TM("[%X] Delete connection %X, Client ID = %d, List length %d"),
                                 (tUINT32)m_dwID,
                                 (tUINT32)m_pDel_Connection->Get_ID(),
                                 (tUINT32)m_pDel_Connection->Get_Client_ID(),
                                 (tUINT32)m_pConnections_Active->Count()
                                );

                    m_pConnections->Put_Data(m_pDel_Connection->Get_Cell(), NULL, TRUE);

                    m_pDel_Connection = NULL;
                }
                else
                {
                    JOURNAL_ERROR(m_pLog,
                                 TM("[%X] Delete connection signal with NULL connection!"),
                                 m_dwID
                                );
                }

                LOCK_EXIT(m_tCS); 
            }

            m_cDel_Event.Clr(DELETE_CONNECTION_EVENT);
        }
        PERFORMANCE_MONITOR_ADD(l_bTidDelete, 0);

        PERFORMANCE_MONITOR_ADD(l_bTidRecvReady, 1);
        if (UDP_SOCKET_OK == m_pSocket->Is_Ready(FD_TYPE_READ, 25))
        {
            l_bBigEndian    = FALSE;
            l_sHeader.wSize = 0;

            PERFORMANCE_MONITOR_ADD(l_bTidPick, 1);

            if (UDP_SOCKET_OK == m_pSocket->Peek((char*)&l_sHeader, sizeof(l_sHeader)))
            {
                if ((l_sHeader.wBits >> TPACKET_FLAGS_OFFSET) & TPACKET_FLAG_BIG_ENDIAN_SRV)
                {
                    l_bBigEndian = TRUE;
                    l_sHeader.wSize = ntohs(l_sHeader.wSize);
                }

                if (l_sHeader.wSize)
                {
                    l_pPacket_Recv = m_pBPool->Pull_Buffer(l_sHeader.wSize);
                }
                else
                {
                    l_pPacket_Recv = m_pBPool->Pull_Buffer(0xFFFF); //max UDP packet size
                }
            }

            PERFORMANCE_MONITOR_ADD(l_bTidPick, 0);

            if (l_pPacket_Recv)
            {
                PERFORMANCE_MONITOR_ADD(l_bTidRecv, 1);

                l_dwReceived = 0;
                if (UDP_SOCKET_OK == m_pSocket->Recv(&l_tSenderAddr, 
                                                     (char*)l_pPacket_Recv->Get_Buffer(), 
                                                     l_pPacket_Recv->Get_Buffer_Size(), 
                                                     (tUINT32 *)&l_dwReceived
                                                    )
                   )
                {
                    if (    (l_dwReceived)
                         && (l_dwReceived == (tUINT32)l_sHeader.wSize)
                         && (FALSE == l_pPacket_Recv->Is_Damaged(l_bBigEndian)) 
                       )
                    {
                        if (l_bBigEndian)
                        {
                            l_pPacket_Recv->Chage_Endianness(); //only header
                        }

                        //get connection by IT ID
                        l_pConnection = (*m_pConnections)[l_pPacket_Recv->Get_Client_ID()];

                        //if connection is not exist or address is wrong - create new connection
                        if (    (NULL  == l_pConnection)
                             || (FALSE == l_pConnection->Is_Address(&l_tSenderAddr)) 
                           )
                        {
                            JOURNAL_INFO(m_pLog, 
                                         TM("[%X] New client, ID = %d, Prev. connection addr = %X"), 
                                         m_dwID,
                                         (tUINT32)l_pPacket_Recv->Get_Client_ID(),
                                         l_pConnection
                                        );

                            l_pConnection = Add_Conn(&l_tSenderAddr, l_bBigEndian, m_pSocket->GetRecvBufferSize());
                        }
                        
                        //l_pReceived_Packet now is head pain of Process_Incoming_Packet()
                        if (l_pConnection)
                        {
                            PACKET_PRINT(m_pLog, TM("[INP]"), l_pPacket_Recv);

                            l_pConnection->Process_Incoming_Packet(l_pPacket_Recv);
                            l_pPacket_Recv = NULL;
                        }
                        else
                        {
                            JOURNAL_ERROR(m_pLog, 
                                        TM("[%X] Connection for packet is not found, size %d"), 
                                        m_dwID,
                                        l_dwReceived
                                        );
                        }
                    }
                    else
                    {
                        JOURNAL_ERROR(m_pLog, 
                                    TM("[%X] Packet size mismatch %d %d, or CRC calculation error"), 
                                    m_dwID,
                                    l_dwReceived,
                                    l_pPacket_Recv->Get_Size()
                                    );
                    }
                }

                ATOMIC_ADD(&m_lBytesIn, (tINT32)l_dwReceived); 

                if (l_pPacket_Recv)
                {
                    m_pBPool->Push_Buffer(l_pPacket_Recv);
                    l_pPacket_Recv = NULL;
                }

                PERFORMANCE_MONITOR_ADD(l_bTidRecv, 0);
            } //if (l_pReceived_Packet)
            else
            {
                JOURNAL_WARNING(m_pLog,
                                TM("[%X] Packet is null"),
                                m_dwID
                               );
            }


            PERFORMANCE_MONITOR_ADD(l_bTidSend, 1);

            l_pConnecton_Cell = NULL;
            l_pConnection = NULL;
            while ((l_pConnecton_Cell = m_pConnections_Active->Get_Next(l_pConnecton_Cell)) != NULL)
            {
                l_pConnection = m_pConnections_Active->Get_Data(l_pConnecton_Cell);
                if (l_pConnection)
                {
                    l_pPacket_Sent = l_pConnection->Get_Output_Packet(l_uPacket_Size);
                    if (l_pPacket_Sent)
                    {
                        if  (UDP_SOCKET_OK == m_pSocket->Send((sockaddr*)l_pConnection->Get_Address(),
                                                               l_pConnection->Get_Address_Size(), 
                                                               (const char*)l_pPacket_Sent, 
                                                               l_uPacket_Size
                                                              )
                           )
                        {
                            ATOMIC_ADD(&m_lBytesOut, (tINT32)l_uPacket_Size);
                        }
                        else
                        {
                            JOURNAL_ERROR(m_pLog, TM("[%X] Sending error"), m_dwID)
                        }
                    }
                } //if (l_pStream)
            } //while ((l_pStreamEl = m_pConnections_Active->Get_Next(l_pStreamEl)) != NULL)
            PERFORMANCE_MONITOR_ADD(l_bTidSend, 0);
        } //if (UDP_SOCKET_OK == m_pSocket->Is_Ready(FD_TYPE_READ, 25))
        PERFORMANCE_MONITOR_ADD(l_bTidRecvReady, 0);

        if (CTicks::Difference(GetTickCount(), l_dwPrint_Tick) > 30000)
        {
            Print_Connections();
            l_dwPrint_Tick = GetTickCount();
        }
    } //while

    Print_Connections();

    JOURNAL_INFO(m_pLog, TM("[%X] Stop"), m_dwID);

#if defined(PERFORMANCE_MONITORING)
    if (l_iP7_Tel)
    {
        l_iP7_Tel->Release();
        l_iP7_Tel = NULL;
    }
#endif

    if (m_pLog)
    {
        m_pLog->Unregister_Thread(0);
    }
}//Routine



