////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#ifndef P7_CONNECTION_H
#define P7_CONNECTION_H

#define SERVER_CONNECTION_TIMEOUT                                         (3000)
#define SERVER_EXTRA_PER_CONNECTION                                         (10)

class CSConnection: 
    public IP7S_Connnection
{
    enum eAckAction
    {
        eAckAddData         = 0x00000001,
        eAckAddExtraSrvInfo = 0x00000002
    };

    //put volatile variables at the top, to obtain 32 bit alignment. 
    //Project has 8 bytes alignment by default
    tUINT32 volatile       m_dwReceive_Time;

    tUINT16                m_wClient_ID;
    tUINT32                m_dwID;
    IJournal              *m_pLog;
    IJournal::hModule      m_hLogMod;
    sockaddr_storage       m_sAddress;
    tUINT32                m_dwAddress_Size;

    sP7S_Connection_Info   m_sInfo;

    tBOOL                  m_bClosed;
    tBOOL                  m_bReinitialized;
    tBOOL                  m_bExtensionsSupport;

    CBuffers_Pools        *m_pBPool;
    CBList<CTPacket*>     *m_pData_Queue;
    CBList<CTPacket*>     *m_pData_Wnd;
    CTPacket              *m_pPacket_Control_Last;
    CTPacket              *m_pPacket_Control;
    CTPAcknowledgment     *m_cPacket_Ack;
    CTPData_Window_Report  m_cPacket_Data_Report;

    tUINT32                m_dwLast_Sent_ID;
    tUINT32                m_dwLast_Recv_ID;
    tUINT32                m_dwData_Offset;

    void                  *m_pUser_Data;

    tLOCK                  m_hCS_Data; 
    tLOCK                  m_hCS_User; 

    eServer_Status         m_eStatus;

    tUINT32                m_dwCreation_Time;
    tBOOL                  m_bInitialized;

    pAList_Cell            m_pList_Cell;

    tUINT32                m_dwOut_Packet_Size;
    tUINT32                m_dwOut_Packet_Used;
    tUINT8                *m_pOut_Packet_Data;
    sH_User_Data          *m_pOut_Packet_Header;

    CBList<CTPacket*>      m_cExtra;
    tLOCK                  m_hCS_Extra; 

    tUINT64                m_qwPull_Count;

    tBOOL                  m_bBigEndian;
    tINT32                 m_iSocketBufferSize;
public:
    CSConnection(IJournal         *i_pLog, 
                 CBuffers_Pools   *i_pBPool,
                 sockaddr_storage *i_pAddress,
                 tUINT32           i_dwID,
                 tBOOL             i_bBigEndian,
                 tINT32            i_iSocketBufferSize
                );
    virtual ~CSConnection();

    eServer_Status    Get_Status();

    sockaddr_storage *Get_Address();
    tUINT32           Get_Address_Size();

    tBOOL        Is_Address(sockaddr_storage *i_pAddress);

    tBOOL        Is_BigEndian();

    tBOOL        Process_Incoming_Packet(CTPacket *i_pPacket);
    const tUINT8*Get_Output_Packet(tUINT32 &o_rSize);

    tBOOL        Get_Info(sP7S_Connection_Info *o_pInfo);
    tBOOL        Get_Status(sP7S_Connection_Status *o_pStatus);

    void         Set_User_Data(void *i_pData);
    void        *Get_User_Data();

    tBOOL        Push_Data(tUINT32 i_dwChannel_ID, const tUINT8 *i_pData, tUINT32 i_dwSize);

    CTPacket    *Pull_Packet();
    void         Release_Packet(CTPacket *i_pPacket);

    void         Set_Cell(pAList_Cell i_pCell);
    pAList_Cell  Get_Cell();

    tUINT32      Get_ID();

    tUINT16      Get_Client_ID();
    void         Set_Client_ID(tUINT16 i_wClient_ID);

    void         Print();

private:
    CTPacket    *Pull_Firt_Data_Packet();
    tBOOL        Push_First_Data_Packet(CTPacket *i_pPacket);
    tBOOL        Push_Last_Data_Packet(CTPacket *i_pPacket);

    void         Inc_Thread_Safe(tUINT32 *io_pValue, tUINT32 i_dwIncrement);
    void         Inc_Packet_ID(tUINT32 *i_pPacketID);

    CTPacket    *Create_Ack(tUINT32    i_dwSource_ID,
                            tUINT32    i_dwPacket_ID, 
                            tUINT16    i_wResult,
                            eAckAction i_eAction
                           );
};

#endif //P7_CONNECTION_H
