////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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 BUFPOOL_H
#define BUFPOOL_H

#if defined(_MSC_VER)
    #pragma intrinsic(_BitScanReverse)
#endif

////////////////////////////////////////////////////////////////////////////////
//CBuffers_Pools

//values: 512, 1024, 2048, 4096, 8192, 16384, 32768, 65535
#define BUFFERS_POOLS_COUNT                                                  (8)
#define BUFFERS_DYNAMIC_POOL_ID                                       (0xFFFFFF)

class CBuffers_Pools
{
    CBuffers_Pool    *m_pPools[BUFFERS_POOLS_COUNT];
    tBOOL             m_bInitialized;
    IJournal         *m_pLog;
    IJournal::hModule m_hLogMod;
    volatile tUINT32  m_dwSize;
    volatile tUINT32  m_dwMaxSize;
public:
    ////////////////////////////////////////////////////////////////////////////
    //CBuffers_Pools::CBuffers_Pools
    CBuffers_Pools(IJournal *i_pLog,
                   tUINT32   i_dwMaxmum_Pool_Size
                  )
       : m_bInitialized(TRUE)
       , m_pLog(i_pLog)
       , m_hLogMod(0)
       , m_dwSize(0)
       , m_dwMaxSize(i_dwMaxmum_Pool_Size)
    {
        if (m_pLog)
        {
            m_pLog->Add_Ref();
            m_pLog->Register_Module(TM("Srv/Buf"), &m_hLogMod);
        }

        memset(m_pPools, 0, sizeof(CBuffers_Pool*)*BUFFERS_POOLS_COUNT);

        tUINT32 l_dwSize = 512;
        for (tUINT32 l_dwIDX = 0; l_dwIDX < BUFFERS_POOLS_COUNT; l_dwIDX++)
        {
            m_pPools[l_dwIDX] = new CBuffers_Pool(i_pLog,
                                                  0x400000, //4 mb.
                                                  i_dwMaxmum_Pool_Size,
                                                  l_dwSize,
                                                  l_dwIDX
                                                 );
            if (    (NULL  == m_pPools[l_dwIDX])
                 || (FALSE == m_pPools[l_dwIDX]->Get_Initialized())
               )
            {
                JOURNAL_ERROR(m_pLog, TM("Initialization failed."));
                m_bInitialized = FALSE;
                break;
            }
            else
            {
                m_pPools[l_dwIDX]->Inc_Reference_Count();
                //no common size, every pool has 
                //m_pPools[l_dwIDX]->Set_External_Size(&m_dwSize);
            }

            l_dwSize <<= 1;
        }
    } //CBuffers_Pools::CBuffers_Pools


    ////////////////////////////////////////////////////////////////////////////
    //CBuffers_Pools::~CBuffers_Pools
    ~CBuffers_Pools()
    {
        for (tUINT32 l_dwIDX = 0; l_dwIDX < BUFFERS_POOLS_COUNT; l_dwIDX++)
        {
            if (m_pPools[l_dwIDX])
            {
                m_pPools[l_dwIDX]->Dec_Reference_Count();
                delete m_pPools[l_dwIDX];
                m_pPools[l_dwIDX] = NULL; 
            }
        }

        if (m_pLog)
        {
            m_pLog->Release();
            m_pLog = NULL;
        }
    }//CBuffers_Pools::~CBuffers_Pools


    ////////////////////////////////////////////////////////////////////////////
    //CBuffers_Pools::Pull_Buffer
    CTPacket *Pull_Buffer(tUINT32 i_dwBuffer_Size)
    {
        tINT32 l_lIDX = 0;

        // if (i_dwBuffer_Size >= m_dwMaxSize)
        // {
        //     JOURNAL_ERROR(m_pLog,  
        //                     TM("Failed, Packet Size = %d, Memory = %d, Max Memory = %d"),
        //                     (DWORD)i_dwBuffer_Size,
        //                     (DWORD)m_dwSize,
        //                     (DWORD)m_dwMaxSize
        //                     );
        //         
        //     return NULL;
        // }
        if (!i_dwBuffer_Size)
        {
            i_dwBuffer_Size = 1u;
        }

    #if defined(__GNUC__)
        l_lIDX = 32 - (__builtin_clz(i_dwBuffer_Size) + 1);
    #elif defined(_MSC_VER)
        _BitScanReverse((DWORD*)&l_lIDX, i_dwBuffer_Size);
    #else
        l_lIDX = 32;
        while ((!(i_dwBuffer_Size & (1u << (--l_lIDX)))) && (l_lIDX));
    #endif


        if (9 <= l_lIDX) //buffer size is >= 512
        {
           l_lIDX -= 8;
        }
        else
        {
           l_lIDX = 0;
        }

        if (    (BUFFERS_POOLS_COUNT > l_lIDX)
             && (m_pPools[l_lIDX])
           )
        {
            return m_pPools[l_lIDX]->Pull_Buffer();
        }

        return NULL;
    }//CBuffers_Pools::Pull_Buffer


    ////////////////////////////////////////////////////////////////////////////
    //CBuffers_Pools::Push_Buffer
    void Push_Buffer(CTPacket *i_pBuffer)
    {
        if (NULL == i_pBuffer)
        {
            JOURNAL_ERROR(m_pLog, TM("Wrong parameters."));
            return;
        }

        tUINT32 l_dwPool_ID = i_pBuffer->Get_Pool_ID();

        if (    (BUFFERS_POOLS_COUNT <= l_dwPool_ID)
             || (NULL == m_pPools[l_dwPool_ID])
           )
        {
            if (BUFFERS_DYNAMIC_POOL_ID == l_dwPool_ID)
            {
                delete i_pBuffer;
            }
            else
            {
                JOURNAL_ERROR(m_pLog, TM("Buffer pool ID is worng %d Memory leak ?!"), (tUINT32)l_dwPool_ID);
            }

            return;
        }

        m_pPools[l_dwPool_ID]->Push_Buffer(i_pBuffer);
    }//CBuffers_Pools::Push_Buffer

    ////////////////////////////////////////////////////////////////////////////
    //CBuffers_Pools::Get_Dynamic_Pool_Id
    tUINT32 Get_Dynamic_Pool_Id()
    {
        return BUFFERS_DYNAMIC_POOL_ID;
    }

    ////////////////////////////////////////////////////////////////////////////
    //CBuffers_Pools::Get_Memory_Info
    void Get_Memory_Info(size_t &o_rBusy, size_t &o_rFree, size_t &o_rAllocated)
    {
        o_rBusy      = 0;
        o_rFree      = 0;
        o_rAllocated = 0;

        for (tUINT32 l_dwIDX = 0; l_dwIDX < BUFFERS_POOLS_COUNT; l_dwIDX++)
        {
            if (m_pPools[l_dwIDX])
            {
                tUINT32 l_dwUsed  = 0;
                tUINT32 l_dwFree  = 0;
                tUINT32 l_dwAlloc = 0;
                m_pPools[l_dwIDX]->Get_Memory_Info(&l_dwUsed, &l_dwFree, &l_dwAlloc);
                o_rBusy      += l_dwUsed;
                o_rFree      += l_dwFree;
                o_rAllocated += l_dwAlloc;
            }
        }
    }//CBuffers_Pools::Get_Memory_Info
};

#endif //BUFPOOL_H
