////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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"

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

#define JOURNAL_MODULE m_hLogMod

extern "C" 
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
P7_EXPORT IP7_Server * __cdecl P7_Create_Server(IJournal *i_pLog, CProperty *i_pProp)
{
    CServer *l_pReturn = new CServer(i_pLog, i_pProp);

    //if not initialized - remove
    if (    (l_pReturn)
         &&  (ESERVER_STATUS_OK != l_pReturn->Get_Status())
       )
    {
        delete l_pReturn;
        l_pReturn = NULL;
    }

    return static_cast<IP7_Server *>(l_pReturn);
}//P7_Create_Server

}//extern "C"


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CServer
CServer::CServer(IJournal *i_pLog, CProperty *i_pProp)
    : m_lReference(1)
    , m_pLog(i_pLog)
    , m_hLogMod(NULL)
    , m_pBPool(NULL)
    , m_pAddresses(NULL)
    , m_eStatus(ESERVER_STATUS_OK)
    , m_bIs_Winsock(FALSE)
{
    if (m_pLog)
    {
        m_pLog->Add_Ref();
        m_pLog->Register_Module(TM("Srv/Core"), &m_hLogMod);
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        tINT64 l_llMemory = 0;//0x8000000ll; //132 mb
        if (i_pProp)
        {
            CProperty *l_pMemory = NULL;
            i_pProp->Get_SubItem(TM("Memory limit (MB)"), l_pMemory);
            if (l_pMemory)
            {
                l_pMemory->Get_Value(l_llMemory);
                l_llMemory *= 0x100000ll;
                l_pMemory->Release();
            }
        }

        if (l_llMemory < 0x8000000ll) //132 mb
        {
            l_llMemory = 0x8000000ll;
        }

        m_pBPool = new CBuffers_Pools(m_pLog, (tUINT32)l_llMemory); 
        if (NULL == m_pBPool)
        {
            JOURNAL_CRITICAL(m_pLog, TM("Buffers pools wan't created!"));
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {
        m_pAddresses = new CBList<CSAddress*>;

        if (NULL == m_pAddresses)
        {
            JOURNAL_CRITICAL(m_pLog, TM("Addresses list wan't created!"));
            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
        }
    }

    if (ESERVER_STATUS_OK == m_eStatus)
    {

        if (FALSE == WSA_Init())
        {
            JOURNAL_CRITICAL(m_pLog, 
                             TM("Socket initialization fail !"));

            m_eStatus = ESERVER_STATUS_INTERNAL_ERROR;
        }
        else
        {
            m_bIs_Winsock = TRUE;
        }
    }


    if (ESERVER_STATUS_OK == m_eStatus)
    {
        CBList<sockaddr_storage*> l_cIpList;

        if (PEnumIps(&l_cIpList))
        {
            Create_Addresses(&l_cIpList, i_pProp);
        }

        l_cIpList.Clear(TRUE);
    } // if (ESERVER_STATUS_OK == m_eStatus)
} //CServer


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

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

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

    if (m_bIs_Winsock)
    {
        WSA_UnInit();
    }

}//~CServer


////////////////////////////////////////////////////////////////////////////////
//Get_Next_Address
IP7S_Address  *CServer::Get_Next_Address(IP7S_Address *i_pAddress)
{
    IP7S_Address *l_pResult = NULL;
    pAList_Cell   l_pCell   = NULL;

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

    l_pCell = m_pAddresses->Get_Next( (i_pAddress) ?
                                      (static_cast<CSAddress*>(i_pAddress))->Get_Cell() 
                                     : NULL
                                    );
        
    if (l_pCell)
    {
        l_pResult = m_pAddresses->Get_Data(l_pCell);
    }

    return l_pResult;
}//Get_Next_Address


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


////////////////////////////////////////////////////////////////////////////////
//Get_MemStat
void CServer::Get_MemStat(size_t &o_rBusy, size_t &o_rAllocated)
{
    size_t l_szFree = 0;
    o_rBusy      = 0;
    o_rAllocated = 0;
    if (m_pBPool)
    {
        m_pBPool->Get_Memory_Info(o_rBusy, l_szFree, o_rAllocated);
    }
}//Get_MemStat


////////////////////////////////////////////////////////////////////////////////
//Create_Addresses
tUINT32 CServer::Create_Addresses(CBList<sockaddr_storage*> *io_pList, CProperty *i_pProp)
{
    if (NULL == io_pList)
    {
        return 0;
    }
    tUINT32     l_dwReturn = 0;
    pAList_Cell l_pEl      = NULL;

    tXCHAR     l_pIpTxt[128];

    while (NULL != (l_pEl = io_pList->Get_Next(l_pEl)))
    {
        sockaddr_storage *l_pAddr = io_pList->Get_Data(l_pEl);

        if (l_pAddr)
        {
                tBOOL l_bAdd = TRUE;

                if (i_pProp)
                {
                    sockaddr_storage l_cStorage;
                    CProperty       *l_pPropAddrGr = NULL;
                    tUINT16          l_wPort       = P7_SERVER_DEFAULT_PORT;

                        //clear port info to let function Print_SAddr print address
                        //correctly
                    if (AF_INET == l_pAddr->ss_family)
                    {
                        memcpy(&l_cStorage, l_pAddr, sizeof(sockaddr_in));
                        ((sockaddr_in*)&l_cStorage)->sin_port = htons(0);
                    }
                    else if (AF_INET6 == l_pAddr->ss_family)
                    {
                        memcpy(&l_cStorage, l_pAddr, sizeof(sockaddr_in6));
                        ((sockaddr_in6*)&l_cStorage)->sin6_port = htons(0);
                    }

                    Print_SAddr((sockaddr*)&l_cStorage, l_pIpTxt, LENGTH(l_pIpTxt));

                    i_pProp->Get_SubItem(l_pIpTxt, l_pPropAddrGr);
                    if (l_pPropAddrGr)
                    {
                        CProperty *l_pPropTemp = NULL;

                        l_pPropAddrGr->Get_SubItem(P7_PROP_ENABLE, l_pPropTemp);
                        if (l_pPropTemp)
                        {
                            tINT32 l_iIndex = 0;
                            l_pPropTemp->Get_Current_Index(l_iIndex);
                            l_bAdd = (0 == l_iIndex) ? FALSE : TRUE;
                            l_pPropTemp->Release();
                            l_pPropTemp = NULL;
                        }

                        l_pPropAddrGr->Get_SubItem(P7_PROP_PORT, l_pPropTemp);
                        if (l_pPropTemp)
                        {
                            tINT64 l_llValue = 0;
                            l_pPropTemp->Get_Value(l_llValue);
                            l_wPort = (tUINT16)l_llValue;
                            l_pPropTemp->Release();
                            l_pPropTemp = NULL;
                        }

                        l_pPropAddrGr->Release();
                        l_pPropAddrGr = NULL;
                    }
                    else
                    {
                        CProperty::Create(i_pProp, CProperty::eGroup, l_pIpTxt, l_pPropAddrGr);
                        if (l_pPropAddrGr)
                        {
                            CProperty *l_pPropTemp = NULL;
                            CProperty::Create(l_pPropAddrGr, CProperty::eEnum, P7_PROP_ENABLE, l_pPropTemp);
                            if (l_pPropTemp)
                            {
                                l_pPropTemp->Add_Item(TM("Off"), 0);
                                l_pPropTemp->Add_Item(TM("On"), 1);
                                l_pPropTemp->Set_Current_Index(1);
                                l_pPropTemp->Set_Desc(TM("Interface enable flag\nApplication restart is required"));
                                l_pPropTemp->Release();
                                l_pPropTemp = NULL;
                            }

                            CProperty::Create(l_pPropAddrGr, CProperty::eInt, P7_PROP_PORT, l_pPropTemp);
                            if (l_pPropTemp)
                            {
                                l_pPropTemp->Set_Value(P7_SERVER_DEFAULT_PORT);
                                l_pPropTemp->Set_Desc(TM("Interface port\nApplication restart is required"));
                                l_pPropTemp->Set_Range(1025, 65535);
                                l_pPropTemp->Release();
                                l_pPropTemp = NULL;
                            }

                            l_pPropAddrGr->Release();
                            l_pPropAddrGr = NULL;
                        }
                    }

                        //apply port value
                    if (AF_INET == l_pAddr->ss_family)
                    {
                        ((sockaddr_in*)l_pAddr)->sin_port = htons(l_wPort);
                    }
                    else if (AF_INET6 == l_pAddr->ss_family)
                    {
                        ((sockaddr_in6*)l_pAddr)->sin6_port = htons(l_wPort);
                    }
                }//if (i_pProp)


                if (l_bAdd)
                {
                    CSAddress *l_pAddress = new CSAddress(m_pLog, 
                                                          m_pBPool, 
                                                          l_pAddr,
                                                          l_dwReturn
                                                         );

                    if (    (l_pAddress)
                         && (ESERVER_STATUS_OK != l_pAddress->Get_Status())
                       )
                    {
                        JOURNAL_ERROR(m_pLog, TM("Address class is not created !"));

                        delete l_pAddress;
                        l_pAddress = NULL;
                    }

                    if (l_pAddress)
                    {
                        l_pAddress->Set_Cell(m_pAddresses->Add_After(m_pAddresses->Get_Last(), l_pAddress));
                        l_dwReturn ++;
                    }
                }
        }// if (l_pAddr)
    }//while()

    return l_dwReturn;
}//Create_Addresses
