////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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 "common.h"

#include "P7Import.h"
#include "P7PExtra.h"

#include "CRC32.h"
#include "PCRC32.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tUINT64 ntohqw(tUINT64 i_qwX)
{
#if defined(_WIN32) || defined(_WIN64)
    return _byteswap_uint64(i_qwX);
#else
    i_qwX = (i_qwX & 0x00000000FFFFFFFFull) << 32 | (i_qwX & 0xFFFFFFFF00000000ull) >> 32;
    i_qwX = (i_qwX & 0x0000FFFF0000FFFFull) << 16 | (i_qwX & 0xFFFF0000FFFF0000ull) >> 16;
    return (i_qwX & 0x00FF00FF00FF00FFull) << 8  | (i_qwX & 0xFF00FF00FF00FF00ull) >> 8;
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ntohstr(tWCHAR *l_pBegin, tWCHAR *l_pEnd)
{
    while (    (*l_pBegin)
            && (l_pBegin < l_pEnd)
            )
    {
        *l_pBegin = ntohs(*l_pBegin);

        l_pBegin ++;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ntohstr(tWCHAR *l_pBegin)
{
    while (*l_pBegin)
    {
        *l_pBegin = ntohs(*l_pBegin);
        l_pBegin ++;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                GUIDs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// {0E90CC79-D5C6-4DFB-A9E1-A3FF1159F64E}
static const GUID g_sPlugin_GUID = { 0xe90cc79, 0xd5c6, 0x4dfb, { 0xa9, 0xe1, 0xa3, 0xff, 0x11, 0x59, 0xf6, 0x4e } };


static const GUID g_pType_GUID[] = 
{ 
    // TYPE : eP7User_Type::EP7USER_TYPE_TRACE
    BK_GUID_STREAM_TRACE,

    // TYPE : eP7User_Type::EP7USER_TYPE_TELEMETRY
    BK_GUID_STREAM_TELEMETRY_V1,
    BK_GUID_STREAM_TELEMETRY_V2

    //For feature needs
    // {24CE9629-5339-4da7-B11F-3BD871AB7880}
    //{   0x24ce9629, 0x5339, 0x4da7, 
    //    { 0xb1, 0x1f, 0x3b, 0xd8, 0x71, 0xab, 0x78, 0x80 } 
    //}
};



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                         Plugin functions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


extern "C" 
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Plugin_Info
P7_EXPORT Bk::eResult __cdecl Get_Plugin_Info(Bk::stPluginInfo *o_pInfo)
{
    Bk::eResult l_eResult = Bk::eOk;

    if (NULL == o_pInfo)
    {
        return Bk::eErrorWrongInput;
    }

    memcpy(&o_pInfo->sGUID, &g_sPlugin_GUID, sizeof(GUID));

    o_pInfo->eType         = Bk::ePluginProvider;
    o_pInfo->dwAPI_Version = BK_PLUGIN_API_VERSION;
    o_pInfo->qwVersion     = 0; //will be filled outside;
    o_pInfo->pFormats      = TM("P7 import file (*") BK_STORAGE_DUMP_FILE_EXT TM(")");

    PStrCpy(o_pInfo->pName, LENGTH(o_pInfo->pName), TM("Provider: P7 dump"));

    return l_eResult;
}//Get_Plugin_Info

//compiler check than function match the prototype
static const fnBkGetPluginInfo g_pGetPluginInfo = Get_Plugin_Info;


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Create_Provider
P7_EXPORT Bk::eResult __cdecl Create_Provider(Bk::IUnknown   *i_pCore,    //[Inp] Core interface
                                              Bk::IBNode     *i_pNode,    //[Inp] stored configuration node
                                              CProperty      *i_pProp,    //[Inp] properties
                                              Bk::IProvider **o_pProvider //[Out] result provider interface
                                             )
{
    UNUSED_ARG(i_pProp);
    Bk::eResult l_eReturn = Bk::eOk;

    if (NULL == o_pProvider)
    {
        return Bk::eErrorWrongInput;
    }

    CP7Importer *l_pP7Shell = new CP7Importer(i_pCore, i_pNode, i_pProp);

    if (    (l_pP7Shell)
         && (FALSE == l_pP7Shell->Get_Initialized())
       )
    {
        delete l_pP7Shell;
        l_pP7Shell = NULL;
    }

    *o_pProvider = l_pP7Shell; 

    return l_eReturn;
}//Create_Provider

//compiler check than function match the prototype
static const fnBkCreateProvider g_pBkCreateProvider = Create_Provider;
}//extern "C"



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CStreamP7::CStreamP7()
    : m_iStorare(NULL)
    , m_pStreamEx(NULL)
    , m_pFirst(NULL)
    , m_pLast(NULL)
    , m_bError(FALSE)
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CStreamP7::~CStreamP7()
{
    Uninit();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CStreamP7::Uninit()
{
    memset(&m_sClose, 0, sizeof(m_sClose));

    if (m_pStreamEx)
    {
        m_pStreamEx->Deactivate();
        m_pStreamEx->Release();
        m_pStreamEx = NULL;
    }

    if (m_iStorare)
    {
        m_iStorare->Uninit_Writer();
        m_iStorare->Release();
        m_iStorare = NULL;
    }

    m_bError = FALSE;
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDataBlock::CDataBlock(size_t i_szCount)
{
    m_szCount = i_szCount;

    if (i_szCount)
    {
        tUINT8 *l_pData = (tUINT8*)malloc(m_szCount * sizeof(Bk::stDataChunk));

        if (l_pData)
        {
            m_pData = l_pData;
            memset(l_pData, 0, m_szCount * sizeof(Bk::stDataChunk));

            m_pFirst = (Bk::stDataChunk*)l_pData;
            m_pLast  = m_pFirst;

            while (--i_szCount)
            {
                m_pLast = (Bk::stDataChunk*)l_pData;
                l_pData += sizeof(Bk::stDataChunk);
                m_pLast->pNext = (Bk::stDataChunk*)(l_pData);
            }
            m_pLast->pNext = NULL;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CDataBlock::~CDataBlock()
{
    if (m_pData)
    {
        free(m_pData);
        m_pData = NULL;
    }

    m_szCount = 0;
}




GASSERT(CP7Importer::eThreadMainCountSignal == 2);
//"m_cExit_Event wrong initialization");
//Update if (FALSE == m_cThreadEvent.Init((tUINT8)eThreadMainCountSignal, EMEVENT_SINGLE_AUTO, EMEVENT_MULTI))

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                               CP7Importer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CP7Importer::CP7Importer(Bk::IUnknown   *i_pCore, 
                         Bk::IBNode     *i_pNode, 
                         CProperty      *i_pProp)
    : m_lReference(1)

    , m_pCore(NULL)
    , m_hThread(0)
    , m_bIsThread(FALSE)

    , m_bInitialized(FALSE)

    , m_iP7_Trace(NULL)
    , m_hP7_TraceModule(NULL)
    , m_iP7_Tel(NULL)

    , m_pData(NULL)
    , m_szData_Max(0)
    , m_szData_Size(0)
    , m_szData_Offs(0)
    , m_qwFile_Offs(0)
    , m_qwFile_Size(0)

    , m_bLittleEndian(TRUE)

    , m_pFirstChunk(NULL)
{
    UNUSED_ARG(i_pNode);
    UNUSED_ARG(i_pProp);

    memset(m_pChannels, 0, sizeof(m_pChannels));
    memset(&m_sFile_Header, 0, sizeof(m_sFile_Header));

    m_iP7_Trace = P7_Get_Shared_Trace(BK_SHARED_TRACE);
    m_iP7_Tel   = P7_Get_Shared_Telemetry(BK_SHARED_TELEMETRY);

    if (m_iP7_Trace)
    {
        m_iP7_Trace->Register_Module(TM("Prv/Imp"), &m_hP7_TraceModule);
    }

    if (i_pCore)
    {
        if (Bk::eOk == i_pCore->Query_Interface(Bk::eInterfaceCore, (void*&)m_pCore))
        {
            m_bInitialized = TRUE;
        }
    }

    if (m_bInitialized)
    {
        if (FALSE == m_cThreadEvent.Init((tUINT8)eThreadMainCountSignal, EMEVENT_SINGLE_AUTO, EMEVENT_MULTI))
        {
            m_bInitialized = FALSE;
            LOG_ERROR(TM("[0x%08p] Exit event wasn't created !"), this);
        }
    }

    if (m_bInitialized)
    {
        if (FALSE == CThShell::Create(&Static_Routine, 
                                      this,
                                      &m_hThread,
                                      TM("Prov/Mon")               
                                     )
           )
        {
            m_bInitialized = FALSE;
            LOG_ERROR(TM("[0x%08p] Thread wasn't created !"), this);
        }
        else
        {
            m_bIsThread = TRUE;
        }
    }
}//CP7Importer


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~CP7Importer
CP7Importer::~CP7Importer()
{
    if (m_bIsThread)
    {
        m_cThreadEvent.Set(eThreadMainExit);
        if (TRUE == CThShell::Close(m_hThread, 15000))
        {
            m_hThread   = 0;//NULL;
            m_bIsThread = FALSE;
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] stat thread timeout expire !"), this);
        }
    }


    if (m_pData)
    {
        free(m_pData);
        m_pData = NULL;
    }

    m_cFile.Close(FALSE);

    m_cFiles_List.Clear(TRUE);

    m_szData_Max  = 0;
    m_szData_Size = 0;
    m_szData_Offs = 0;
    m_qwFile_Offs = 0ULL;
    m_qwFile_Size = 0ULL;

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

    if (m_iP7_Tel)
    {
        m_iP7_Tel->Release();
        m_iP7_Tel = NULL;
    }
} //~CP7Importer


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Add_Source
Bk::eResult CP7Importer::Add_Source(const tXCHAR *i_pSource, void *&o_rToken)
{
    UNUSED_ARG(o_rToken);
    CLock l_cLock(&m_cLock);

    if (NULL == i_pSource)
    {
        return Bk::eErrorWrongInput;;
    }

    tUINT32 l_uSourceLen = PStrLen(i_pSource);
    tUINT32 l_uExtLen    = PStrLen(BK_STORAGE_DUMP_FILE_EXT);

    if (    (l_uExtLen >= l_uSourceLen)
         || (0 != PStrICmp(i_pSource + (l_uSourceLen - l_uExtLen), BK_STORAGE_DUMP_FILE_EXT))
       )
    {
        return Bk::eErrorWrongInput;;
    }
    
    LOG_INFO(TM("[0x%08p] Add source {%s}"), this, i_pSource);

    m_cFiles_List.Add_After(m_cFiles_List.Get_Last(), new CWString(i_pSource));
    m_cThreadEvent.Set(eThreadMainFile);
    return Bk::eOk;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Del_Source
Bk::eResult CP7Importer::Del_Source(const void *i_pToken)
{
    UNUSED_ARG(i_pToken);
    return Bk::eErrorNotImplemented;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Progress
Bk::eResult CP7Importer::Get_Progress(tUINT32 &o_rProgress)
{
    UNUSED_ARG(o_rProgress);
    return Bk::eErrorNotImplemented;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Exceptional_Shutdown
Bk::eResult CP7Importer::Exceptional_Shutdown()
{
    return Bk::eErrorNotImplemented;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Add_Ref
tINT32 CP7Importer::Add_Ref()
{
    return ATOMIC_INC(&m_lReference);
} //Add_Ref


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Release
tINT32 CP7Importer::Release()
{
    tINT32 l_lResult = ATOMIC_DEC(&m_lReference);
    if ( 0 >= l_lResult )
    {
        delete this;
    }

    return l_lResult;
} //Release


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Query_Interface
Bk::eResult CP7Importer::Query_Interface(Bk::eInterface  i_eId, void *&o_rUnknown)
{
    UNUSED_ARG(i_eId);
    o_rUnknown = NULL;
    return Bk::eErrorNotSupported;
}//Query_Interface


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Open_File
tBOOL CP7Importer::Open_File(const tXCHAR *i_pFile)
{
    tBOOL l_bReturn = TRUE;

    Close_File();

    if (NULL == i_pFile)
    {
        LOG_ERROR(TM("[0x%08p] Wrong input !"), this);
        l_bReturn    = FALSE;
        goto l_lblExit;
    }

    if (FALSE == CFSYS::File_Exists(i_pFile))
    {
        LOG_ERROR(TM("[0x%08p] File is not exists:%s"), this, i_pFile);
        l_bReturn    = FALSE;
        goto l_lblExit;
    }

    if (NULL == m_pData)
    {
        m_szData_Max  = 1024*1024;
        m_pData = (tUINT8*)malloc(m_szData_Max);
        if (NULL == m_pData)
        {
            m_szData_Max = 0;
            l_bReturn    = FALSE;
            goto l_lblExit;
        }
    }

    LOG_INFO(TM("[0x%08p] Opening P7 dump file: %s"), this, i_pFile);

    if (FALSE == m_cFile.Open(i_pFile,   IFile::EOPEN
                                       | IFile::EACCESS_READ 
                                       | IFile::ESHARE_READ
                             )
       )
    {
        LOG_ERROR(TM("[0x%08p] Can't open file: %s"), this, i_pFile);
        l_bReturn    = FALSE;
        goto l_lblExit;
    }

    m_szData_Size = m_cFile.Read(m_pData, m_szData_Max);

    if (sizeof(sP7File_Header) >= m_szData_Size)
    {
        LOG_ERROR(TM("[0x%08p] File size less than header size should be:%d"), 
                  this,
                  (tUINT32)m_szData_Size
                 );
        l_bReturn    = FALSE;
        goto l_lblExit;
    }

    memcpy(&m_sFile_Header, m_pData, sizeof(m_sFile_Header));

    m_bLittleEndian = TRUE;

    if (P7_DAMP_FILE_MARKER_V1 != m_sFile_Header.qwMarker)
    {
        if (ntohqw(P7_DAMP_FILE_MARKER_V1) == m_sFile_Header.qwMarker)
        {
            m_bLittleEndian = FALSE;
            m_sFile_Header.dwProcess_ID            = ntohl(m_sFile_Header.dwProcess_ID);
            m_sFile_Header.dwProcess_Start_Time_Hi = ntohl(m_sFile_Header.dwProcess_Start_Time_Hi);
            m_sFile_Header.dwProcess_Start_Time_Lo = ntohl(m_sFile_Header.dwProcess_Start_Time_Lo);
            m_sFile_Header.qwMarker                = ntohqw(m_sFile_Header.qwMarker);
            ntohstr(m_sFile_Header.pHost_Name);
            ntohstr(m_sFile_Header.pProcess_Name);
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Header is corrupted !"), this);
            l_bReturn    = FALSE;
            goto l_lblExit;
        }
    }

    m_szData_Offs  = sizeof(sP7File_Header);
    m_qwFile_Offs += m_szData_Size;
    m_qwFile_Size  = m_cFile.Get_Size();
 

l_lblExit:
    return l_bReturn;
}//Open_File


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Close_File
tBOOL CP7Importer::Close_File()
{
    if (m_qwFile_Size)
    {
        LOG_INFO(TM("[0x%08p] Close dump file"), this);
    }

    m_cFile.Close(FALSE);

    m_szData_Offs = 0;
    m_szData_Size = 0;
    m_qwFile_Offs = 0ULL;
    m_qwFile_Size = 0ULL;


    memset(&m_sFile_Header, 0, sizeof(m_sFile_Header));

    return TRUE;
}//Close_File


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Bk::stDataChunk *CP7Importer::Pull_Free_Chunk()
{
    Bk::stDataChunk *l_pReturn = NULL;

    if (m_pFirstChunk)
    {
        l_pReturn = m_pFirstChunk;
        m_pFirstChunk = m_pFirstChunk->pNext;
    }
    else
    {
        CDataBlock *l_pDataBlock = new CDataBlock();
        m_pFirstChunk = l_pDataBlock->Get_First();
        m_cDataBlocks.Add_After(m_cDataBlocks.Get_Last(), l_pDataBlock);
        l_pReturn = m_pFirstChunk;
        m_pFirstChunk = m_pFirstChunk->pNext;
    }

    return l_pReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CP7Importer::Push_Free_Chunks(Bk::stDataChunk *i_pFirst)
{
    if (i_pFirst)
    {
        i_pFirst->szBuffer = 0;
        i_pFirst->pBuffer  = NULL;

        i_pFirst->pNext    = m_pFirstChunk;
        m_pFirstChunk      = i_pFirst;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tBOOL CP7Importer::CreateStorage(CStreamP7 *i_pStream, size_t i_szIndex)
{
    UNUSED_ARG(i_szIndex);

    sP7Ext_Header        l_stHdrExt   = {}; 
    sP7Ext_Raw          *l_pHdrExtRaw = (sP7Ext_Raw*)&l_stHdrExt;
    eP7User_Type         l_eUserType  = EP7USER_TYPE_MAX;
    Bk::stStreamInfo     l_sStrInfo   = { 0 };

    l_stHdrExt = *(sP7Ext_Header*)(i_pStream->m_pFirst->pBuffer);

    if (!m_bLittleEndian)
    {
        l_pHdrExtRaw->dwBits = ntohl(l_pHdrExtRaw->dwBits);
    }

    l_eUserType = (eP7User_Type)l_stHdrExt.dwType;


    if (l_eUserType >= LENGTH(g_pType_GUID))
    {
        LOG_ERROR(TM("[0x%08p] Unexpected user type %d"), this, l_eUserType);
        return FALSE;
    }

    i_pStream->m_sClose.dwSize = sizeof(sP7Ext_Header);
    if (EP7USER_TYPE_TRACE == l_eUserType)
    {
        i_pStream->m_sClose.dwType    = EP7USER_TYPE_TRACE;
        i_pStream->m_sClose.dwSubType = EP7TRACE_TYPE_CLOSE;
    }
    else if (EP7USER_TYPE_TELEMETRY_V1 == l_eUserType)
    {
        i_pStream->m_sClose.dwType    = EP7USER_TYPE_TELEMETRY_V1;
        i_pStream->m_sClose.dwSubType = EP7TEL_TYPE_CLOSE;
    }
    else if (EP7USER_TYPE_TELEMETRY_V2 == l_eUserType)
    {
        i_pStream->m_sClose.dwType    = EP7USER_TYPE_TELEMETRY_V2;
        i_pStream->m_sClose.dwSubType = EP7TEL_TYPE_CLOSE;
    }

    if (!m_bLittleEndian)
    {
        l_pHdrExtRaw = (sP7Ext_Raw*)&i_pStream->m_sClose;
        l_pHdrExtRaw->dwBits = ntohl(l_pHdrExtRaw->dwBits);
    }

    l_sStrInfo.dwProcess_ID      = m_sFile_Header.dwProcess_ID;
    l_sStrInfo.wProtocol_Version = CLIENT_PROTOCOL_VERSION;
    l_sStrInfo.bIs_BigEndian     = !m_bLittleEndian;

    memset(&l_sStrInfo.sAddress, 0, sizeof(l_sStrInfo.sAddress));

    //Fill process name & Host name
#ifdef UTF8_ENCODING
    Convert_UTF16_To_UTF8(m_sFile_Header.pHost_Name,
                          l_sStrInfo.pNode,
                          LENGTH(l_sStrInfo.pNode)
                         );

    Convert_UTF16_To_UTF8(m_sFile_Header.pProcess_Name,
                          l_sStrInfo.pProcess_Name,
                          LENGTH(l_sStrInfo.pProcess_Name)
                         );
#else
    PStrCpy(l_sStrInfo.pNode, LENGTH(l_sStrInfo.pNode), m_sFile_Header.pHost_Name);
    PStrCpy(l_sStrInfo.pProcess_Name, LENGTH(l_sStrInfo.pProcess_Name), m_sFile_Header.pProcess_Name);
#endif

    //Fill channel type GUID  
    memcpy(&l_sStrInfo.sType_GUID, &g_pType_GUID[l_eUserType], sizeof(GUID));

    l_sStrInfo.sProcess_Time.dwHighDateTime = m_sFile_Header.dwProcess_Start_Time_Hi;
    l_sStrInfo.sProcess_Time.dwLowDateTime = m_sFile_Header.dwProcess_Start_Time_Lo;

    if (Bk::eOk != m_pCore->Create_Storage(&l_sStrInfo, i_pStream->m_iStorare))
    {
        LOG_ERROR(TM("[0x%08p] Create storage failed"), this);
        return FALSE;
    }

    i_pStream->m_pStreamEx = new CP7Stream_Ex(this);

    i_pStream->m_iStorare->Put_Stream_Ex(i_pStream->m_pStreamEx);

    return TRUE;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CP7Importer::RegisterStreams()
{
    pAList_Cell l_pEl = NULL;
    while ((l_pEl = m_cTmpStreams.Get_Next(l_pEl)))
    {
        Bk::stStorageInfo l_stInfo;
        CStreamP7 *l_pStream = m_cTmpStreams.Get_Data(l_pEl);
        if (    (l_pStream)
             && (l_pStream->m_iStorare)
           )
        {
            if (Bk::eOk == l_pStream->m_iStorare->Get_Info(&l_stInfo))
            {
                Bk::IStorageReader *l_pReader = NULL;
                if (Bk::eOk == l_pStream->m_iStorare->Query_Interface(Bk::eInterfaceReader, (void*&)l_pReader))
                {
                    m_pCore->Register_New_Stream(NULL, l_pReader, l_pStream->m_pStreamEx);
                    l_pReader->Release();
                }
                else
                {
                    LOG_ERROR(TM("[0x%08p] Can't retrieve IStorageReader interface"), this);
                }

                pAList_Cell l_pElPrev = m_cTmpStreams.Get_Prev(l_pEl);
                m_cTmpStreams.Del(l_pEl, FALSE);
                l_pEl = l_pElPrev;
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Stream list is corrupted!"), this);
            pAList_Cell l_pElPrev = m_cTmpStreams.Get_Prev(l_pEl);
            m_cTmpStreams.Del(l_pEl, FALSE);
            l_pEl = l_pElPrev;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CP7Importer::RegisterAndRemoveStream(CStreamP7 *i_pStream)
{
    pAList_Cell l_pEl = NULL;
    while ((l_pEl = m_cTmpStreams.Get_Next(l_pEl)))
    {
        Bk::stStorageInfo l_stInfo;
        CStreamP7 *l_pStream = m_cTmpStreams.Get_Data(l_pEl);
        if (    (l_pStream)
             && (l_pStream->m_iStorare)
           )
        {
            if (i_pStream == l_pStream)
            {
                if (Bk::eOk == l_pStream->m_iStorare->Get_Info(&l_stInfo))
                {
                    Bk::IStorageReader *l_pReader = NULL;
                    if (Bk::eOk == l_pStream->m_iStorare->Query_Interface(Bk::eInterfaceReader, (void*&)l_pReader))
                    {
                        m_pCore->Register_New_Stream(NULL, l_pReader, l_pStream->m_pStreamEx);
                        l_pReader->Release();
                    }
                    else
                    {
                        LOG_ERROR(TM("[0x%08p] Can't retrieve IStorageReader interface"), this);
                    }
                }

                pAList_Cell l_pElPrev = m_cTmpStreams.Get_Prev(l_pEl);
                m_cTmpStreams.Del(l_pEl, FALSE);
                l_pEl = l_pElPrev;
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Stream list is corrupted!"), this);
            pAList_Cell l_pElPrev = m_cTmpStreams.Get_Prev(l_pEl);
            m_cTmpStreams.Del(l_pEl, FALSE);
            l_pEl = l_pElPrev;
        }
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CP7Importer::Zero_Stream_Buffers(CStreamP7 *i_pStream)
{
    Bk::stDataChunk *l_pIter = i_pStream ? i_pStream->m_pFirst : NULL;
    while (l_pIter)
    {
        l_pIter->szBuffer = 0;
        l_pIter->pBuffer  = 0;
        l_pIter = l_pIter->pNext;
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//RoutineMain
void  CP7Importer::RoutineMain()
{
    eThreadMain        l_eWait        = eThreadMainCountSignal;
    tBOOL              l_bExit        = FALSE;
    tBOOL              l_bActive      = FALSE;
    tUINT32            l_uTimeOut     = 0xFFFFFFFF;
    CStreamP7         *l_pStream      = NULL;
    Bk::stDataChunk   *l_pDataChunk   = NULL;
    CBList<CWString*>  l_cFiles;

    while (!l_bExit)
    {
        l_eWait = (eThreadMain)m_cThreadEvent.Wait(l_uTimeOut);

        if (    (MEVENT_TIME_OUT == l_eWait)
             && (l_bActive)
           )
        {
            tBOOL l_bClose   = FALSE;
            tBOOL l_bParsing = TRUE;

            while (l_bParsing)
            {
                //Parsing data//////////////////////////////////////////////////////////////////////////////////////////
                sH_User_Data *l_pHeader     = (sH_User_Data *)(m_pData + m_szData_Offs);
                size_t        l_szRemainder = m_szData_Size - m_szData_Offs;
    
                if (!m_bLittleEndian)
                {
                    sH_User_Raw *l_pRaw = (sH_User_Raw *)l_pHeader;
                    l_pRaw->dwBits = ntohl(l_pRaw->dwBits);
                }
    
                if (    (sizeof(l_pHeader) <= l_szRemainder)
                     && (l_szRemainder >= l_pHeader->dwSize)
                   )
                {
                    if (LENGTH(m_pChannels) > l_pHeader->dwChannel_ID)
                    {
                        m_szData_Offs += l_pHeader->dwSize;
    
                        l_pDataChunk           = Pull_Free_Chunk();
                        l_pDataChunk->pBuffer  = (tUINT8*)l_pHeader + sizeof(sH_User_Data);
                        l_pDataChunk->szBuffer = l_pHeader->dwSize  - sizeof(sH_User_Data);
                        l_pDataChunk->pNext    = NULL;

                        if (!m_pChannels[l_pHeader->dwChannel_ID])
                        {
                            m_pChannels[l_pHeader->dwChannel_ID] = new CStreamP7();
                        }

                        l_pStream = m_pChannels[l_pHeader->dwChannel_ID];

                        if (l_pStream->m_pLast)
                        {
                            l_pStream->m_pLast->pNext = l_pDataChunk;
                        }
                        else
                        {
                            l_pStream->m_pFirst = l_pDataChunk;
                        }

                        l_pStream->m_pLast = l_pDataChunk;
                    }
                    else
                    {
                        LOG_CRITICAL(TM("[0x%08p] Data is corrupted at offset %I64d!"), 
                                     this,
                                     m_qwFile_Offs - (tUINT64)(m_szData_Size - m_szData_Offs)
                                    );
                        l_bClose = TRUE;
                    }
                }
                else
                {
                    l_bParsing = FALSE;
                }
            }

            //process chunks////////////////////////////////////////////////////////////////////////////////////
            for (size_t l_szI = 0; l_szI < USER_PACKET_CHANNEL_ID_MAX_SIZE; l_szI++)
            {
                l_pStream = m_pChannels[l_szI];
                if (l_pStream)
                {
                    //data delivering///////////////////////////////////////////////////////////////////////////
                    while (l_pStream->m_pFirst)
                    {
                        if (!l_pStream->m_bError)
                        {
                            if (!l_pStream->m_iStorare)
                            {
                                if (CreateStorage(l_pStream, l_szI))
                                {
                                    m_cTmpStreams.Add_After(m_cTmpStreams.Get_Last(), l_pStream);
                                }
                                else 
                                {
                                    Zero_Stream_Buffers(l_pStream);
                                    l_pStream->m_bError = TRUE;
                                    if (l_pStream->m_iStorare)
                                    {
                                        l_pStream->m_iStorare->Release();
                                        l_pStream->m_iStorare = NULL;
                                    }
                                }
                            }
            
                            if (l_pStream->m_iStorare)
                            {
                                Bk::eResult l_eResult = l_pStream->m_iStorare->Put_Packet(l_pStream->m_pFirst);
                                
                                if (Bk::eOk == l_eResult)
                                {
                                    //nothing to do
                                }
                                else if (    (Bk::eErrorClosed == l_eResult)
                                          || (Bk::eErrorMissmatch == l_eResult)
                                        )
                                {
                                    RegisterAndRemoveStream(l_pStream);
                                    l_pStream->Uninit();
                                }
                                else
                                {
                                    Zero_Stream_Buffers(l_pStream);
                                }
                            }
                        }
                        else //clean buffers
                        {
                            Zero_Stream_Buffers(l_pStream);
                        }
            
                        //put back to pool released chunks descriptors: only if size is 0
                        while (    (l_pStream->m_pFirst)
                                && (!l_pStream->m_pFirst->szBuffer)
                              )
                        {
                            Bk::stDataChunk *l_pNext = l_pStream->m_pFirst->pNext;
                            Push_Free_Chunks(l_pStream->m_pFirst);
                            l_pStream->m_pFirst = l_pNext;
                        }

                        if (!l_pStream->m_pFirst)
                        {
                            l_pStream->m_pLast = NULL;
                        }
                    } //while (l_pStream->m_pFirst)
                }
            }//for (size_t l_szI = 0; l_szI < USER_PACKET_CHANNEL_ID_MAX_SIZE; l_szI++)

            RegisterStreams();

            //read next data portion////////////////////////////////////////////////////////////////////////////////////
            if (m_qwFile_Offs < m_qwFile_Size)
            {
                m_qwFile_Offs = m_qwFile_Offs - (tUINT64)(m_szData_Size - m_szData_Offs);
            
                if (m_cFile.Set_Position(m_qwFile_Offs))
                {
                    m_szData_Size = m_cFile.Read(m_pData, m_szData_Max);
                    if (m_szData_Size)
                    {
                        m_szData_Offs  = 0;
                        m_qwFile_Offs += m_szData_Size;
                    }
                    else
                    {
                        LOG_INFO(TM("[0x%08p] End of the file at %I64d"), this, m_cFile.Get_Position());
                        l_bClose = TRUE;
                    }
                }
                else
                {
                    LOG_ERROR(TM("[0x%08p] Can't set position to %I64d"), this, m_qwFile_Offs);
                    l_bClose = TRUE;
                }
            }
            else //close still opened streams
            {
                LOG_INFO(TM("[0x%08p] End of the file"), this);
                l_bClose = TRUE;
            }

            if (l_bClose)
            {
                for (size_t l_szI = 0; l_szI < USER_PACKET_CHANNEL_ID_MAX_SIZE; l_szI++)
                {
                    if (m_pChannels[l_szI])
                    {
                        l_pStream = m_pChannels [l_szI];
            
                        if (l_pStream->m_iStorare)
                        {
                            if (!l_pStream->m_bError)
                            {
                                l_pDataChunk           = Pull_Free_Chunk();
                                l_pDataChunk->pBuffer  = (tUINT8*)&l_pStream->m_sClose;
                                l_pDataChunk->szBuffer = sizeof(l_pStream->m_sClose);
                                l_pDataChunk->pNext    = NULL;
                            
                                if (l_pStream->m_pLast)
                                {
                                    l_pStream->m_pLast->pNext = l_pDataChunk;
                                }
                                else
                                {
                                    l_pStream->m_pFirst = l_pDataChunk;
                                }
                            
                                l_pStream->m_pLast = l_pDataChunk;
                            
                                l_pStream->m_iStorare->Put_Packet(l_pStream->m_pFirst);
                            }
            
                            RegisterAndRemoveStream(l_pStream);
            
                            l_pStream->Uninit();
                                
                            //put back to pool released chunks descriptors: only if size is 0
                            while (l_pStream->m_pFirst)
                            {
                                Bk::stDataChunk *l_pNext = l_pStream->m_pFirst->pNext;
                                Push_Free_Chunks(l_pStream->m_pFirst);
                                l_pStream->m_pFirst = l_pNext;
                            }
                            
                            l_pStream->m_pLast = NULL;
                        }
            
                        delete m_pChannels[l_szI];
                        m_pChannels[l_szI] = NULL;
                    }
                }
            
                m_cTmpStreams.Clear(FALSE);
            
                Close_File();
            
                l_bActive  = FALSE;
                l_uTimeOut = 0xFFFFFFFF;
            
                while (    (l_cFiles.Count())
                        && (!l_bActive)
                      )
                {
                    CWString *l_pName = l_cFiles.Pull_First();
            
                    if (Open_File(l_pName->Get()))
                    {
                        l_bActive  = TRUE;
                        l_uTimeOut = 0;
                    }
                    delete l_pName;
                }
            }//if (l_bClose)
        }//if (l_bActive) && TIMEOUT
        else if (eThreadMainExit == l_eWait)
        {
            l_bExit = TRUE;
        }
        else if (eThreadMainFile == l_eWait)
        {
            CLock l_cLock(&m_cLock);
                
            if (m_cFiles_List.Count())
            {
                CWString *l_pName = m_cFiles_List.Pull_First();
                l_cFiles.Add_After(l_cFiles.Get_Last(), l_pName);
            }

            while (    (!l_bActive)
                    && (l_cFiles.Count())
                  )
            {
                CWString *l_pName = l_cFiles.Pull_First();

                if (Open_File(l_pName->Get()))
                {
                    l_bActive  = TRUE;
                    l_uTimeOut = 0;
                }

                delete l_pName;
            }
        }
    }// while (!l_bExit)

    for (size_t l_szI = 0; l_szI < USER_PACKET_CHANNEL_ID_MAX_SIZE; l_szI++)
    {
        if (m_pChannels[l_szI])
        {
            delete m_pChannels[l_szI];
            m_pChannels[l_szI] = NULL;
        }
    }

    Close_File();

    l_cFiles.Clear(TRUE);
    m_cTmpStreams.Clear(FALSE);
    m_cDataBlocks.Clear(TRUE);
}
