////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2017 (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.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// This header file provide functionality to deliver data to baical server     /
// over UDP                                                                    /
////////////////////////////////////////////////////////////////////////////////

#ifndef CLBAICAL_H
#define CLBAICAL_H


class CClBaical:
    public CClient
{
    tINT32 volatile      m_lReject_Mem;
    tINT32 volatile      m_lReject_Con;
    tINT32 volatile      m_lReject_Int;

    tBOOL                m_bIs_Winsock;
    CUDP_Socket         *m_pSocket;
    tUINT16              m_wClient_ID;
    CBuffers_Pool       *m_pBPool;

    CBList<CTPacket*>   *m_pData_Wnd;
    pAList_Cell          m_pData_Wnd_Cell;
    tUINT32              m_dwData_Wnd_Max_Count;
    tUINT32              m_dwData_Wnd_Size;
    tUINT32              m_dwData_Wnd_TimeStamp;
    tUINT32              m_dwData_Wnd_First_ID;
    tUINT32              m_dwData_Wnd_Last_ID;
    tBOOL                m_bData_Resending;

    tUINT32              m_dwDelivery_Fails;
    tBOOL                m_bIs_Response_Waiting;

    CBList<CTPacket*>   *m_pData_Queue_Out;
    //need to synchronize updating m_pData_Queue by user and requests from internal threads
    tLOCK                m_hCS_Data_Out; 

    CBList<CTPacket*>   *m_pData_Queue_In;
    tLOCK                m_hCS_Data_In; 

    tUINT32              m_dwLast_Packet_ID;
    tUINT32              m_dwServiceTimeStamp;

    tUINT32              m_dwExit_TimeOut;

    //permanent packets, they do not consume a lot of memory and more 
    //practical have them always instead of taking them from pool
    CTPacket            *m_pPacket_Control;
    CTPClient_Hello      m_cPacket_Hello;
    CTPData_Window       m_cPacket_Data_Report;
    CTPClient_Ping       m_cPacket_Alive;
    CTPClient_Bye        m_cPacket_Bye;
    tBOOL                m_bBig_Endian;

    CMEvent              m_cConn_Event;
    tBOOL                m_bConn_Event;

    CMEvent              m_cComm_Event;
    tBOOL                m_bComm_Thread;
    CThShell::tTHREAD    m_hComm_Thread;

    CMEvent              m_cChnl_Event;
    tBOOL                m_bChnl_Thread;
    CThShell::tTHREAD    m_hChnl_Thread;

public:
    CClBaical(tXCHAR **i_pArgs,
              tINT32   i_iCount
             );
    virtual ~CClBaical();

private:
    eClient_Status Init_Base(tXCHAR **i_pArgs,
                             tINT32   i_iCount
                            );
    eClient_Status Init_Log(tXCHAR **i_pArgs,
                            tINT32   i_iCount
                           );
    eClient_Status Init_Sockets(tXCHAR **i_pArgs,
                                tINT32   i_iCount
                               );
    eClient_Status Init_Pool(tXCHAR **i_pArgs,
                             tINT32   i_iCount
                            );
    eClient_Status Init_Members(tXCHAR **i_pArgs,
                                tINT32   i_iCount
                               );
    //eClient_Status Init_Threads(tXCHAR **i_pArgs,
    //                            tINT32   i_iCount
    //                           );

    void           Inc_Packet_ID(tUINT32 * o_pPacketID);

    tBOOL          Process_Incoming_Packet(CTPacket *i_pPacket);
    CTPacket      *Get_Delivered_Packet();
    tBOOL          Is_Ready_To_Exit();

    void           Set_Connected(tBOOL i_bConnected);

    void           Reset_Connetion();
    CTPacket      *Create_Data_Wnd_Report();
    CTPacket      *Pull_Firt_Data_Packet();
    CTPacket      *Pull_Last_Data_Packet();
    tBOOL          Push_Last_Data_Packet(CTPacket *i_pPacket);

    void           Update_Channels_Status(tBOOL i_bConnected, tUINT32 i_dwResets);

    void           Comm_Routine();
    void           Chnl_Routine();

public:
    tBOOL          Connection_Wait(tUINT32 i_dwMilliseconds);

    eClient_Status Sent(tUINT32            i_dwChannel_ID,
                        sP7C_Data_Chunk   *i_pChunks, 
                        tUINT32            i_dwCount,
                        tUINT32            i_dwSize
                       );

    tBOOL          Get_Info(sP7C_Info *o_pInfo);
    tBOOL          Flush();

private:
    static THSHELL_RET_TYPE THSHELL_CALL_TYPE Static_Comm_Routine(void *i_pContext)
    {
        CClBaical *l_pRoutine = static_cast<CClBaical *>(i_pContext);
        if (l_pRoutine)
        {
            l_pRoutine->Comm_Routine();
        }

        CThShell::Cleanup();
        return THSHELL_RET_OK;
    } 

    static THSHELL_RET_TYPE THSHELL_CALL_TYPE Static_Chnl_Routine(void *i_pContext)
    {
        CClBaical *l_pRoutine = static_cast<CClBaical *>(i_pContext);
        if (l_pRoutine)
        {
            l_pRoutine->Chnl_Routine();
        }

        CThShell::Cleanup();
        return THSHELL_RET_OK;
    } 
};


#endif //CLBAICAL_H
