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

#if defined(_WIN32) || defined(_WIN64)
    #pragma warning(disable : 4995)
    #pragma warning(disable : 4996)
#endif

static const GUID g_sPlugin_GUID = { 0xC5206AEB, 0xD40A, 0xBC07, { 0x65, 0xC6, 0x2A, 0xB9, 0xFB, 0x8C, 0x99, 0x2D } };

#if !defined(MAXUINT64)
    #define MAXUINT64   ((tUINT64)~((tUINT64)0))
#endif


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 ++;
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma region ENTRY
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::ePluginStorage;
    o_pInfo->dwAPI_Version = BK_PLUGIN_API_VERSION;
    o_pInfo->qwVersion     = 0; //will be filled outside;
    o_pInfo->pFormats      = TM("Trace storage (*") BK_STORAGE_TRACE_FILE_EXT TM(")");
                                              
    PStrCpy(o_pInfo->pName, LENGTH(o_pInfo->pName), TM("Storage: P7 Trace"));

    return l_eResult;
}//Get_Plugin_Info

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


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Is_Stream_Supported
P7_EXPORT Bk::eResult __cdecl Is_Stream_Supported(const GUID *i_pGUID)
{
    Bk::eResult l_eReturn = Bk::eErrorNotSupported;
    GUID l_sGUID          = BK_GUID_STREAM_TRACE;

    if (    (i_pGUID)
         && (0 == memcmp(i_pGUID, &l_sGUID, sizeof(l_sGUID)))
       )
    {
        l_eReturn = Bk::eOk;
    }

    return l_eReturn;
}//Is_Stream_Supported

//compiler check than function match the prototype
static const fnBkIsStreamSupported g_pIsStreamSupported = Is_Stream_Supported;


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Create_Storage
P7_EXPORT Bk::eResult __cdecl Create_Storage(Bk::IStorage       **o_pStorage,
                                             Bk::stStreamInfo    *i_pInfo,
                                             Bk::IBNode          *i_pNode,
                                             CProperty           *i_pProp
                                            )
{
    Bk::eResult l_eReturn = Bk::eOk;

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

    CTrace_File *l_pStorage = new CTrace_File(i_pNode, i_pProp);

    if (NULL == l_pStorage)
    {
        l_eReturn = Bk::eErrorInternal;
    }

    if (Bk::eOk == l_eReturn)
    {
        l_eReturn = l_pStorage->Init_Writer(i_pInfo);
    }

    if (    (Bk::eOk == l_eReturn)
         && (TRUE == l_pStorage->Is_Failed())
       )
    {
        l_eReturn = Bk::eErrorInternal;
    }

    if (    (Bk::eOk != l_eReturn)
         && (l_pStorage)
       )
    {
        delete l_pStorage;
        l_pStorage = NULL;
    }

    *o_pStorage = dynamic_cast<Bk::IStorage*>(l_pStorage); 

    return l_eReturn;
}//Create_Storage

//compiler check than function match the prototype
static const fnBkCreateStorage g_pBkCreateStorage = Create_Storage;


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Open_Storage
P7_EXPORT Bk::eResult __cdecl Open_Storage(Bk::IStorageReader **o_pReader,
                                           Bk::IBNode          *i_pNode,
                                           CProperty           *i_pProp,
                                           const tXCHAR        *i_pFile
                                          )
{
    Bk::eResult   l_eReturn    = Bk::eOk;
    CTrace_File  *l_pStorage   = NULL;
    const tXCHAR *l_pExtension = NULL;

    if (    (NULL == o_pReader)
         || (NULL == i_pFile)
         || (FALSE == CFSYS::File_Exists(i_pFile))
       )
    {
        return Bk::eErrorWrongInput;
    }

    *o_pReader = NULL;
    l_pExtension = PStrrChr(i_pFile, TM('.'));

    if (    (NULL == l_pExtension)
         || (0 != PStrICmp(l_pExtension, BK_STORAGE_TRACE_FILE_EXT))
       )
    {
        return Bk::eErrorWrongInput;
    }

    l_pStorage = new CTrace_File(i_pNode, i_pProp);

    if (NULL == l_pStorage)
    {
        l_eReturn = Bk::eErrorInternal;
    }

    if (Bk::eOk == l_eReturn)
    {
        l_eReturn = l_pStorage->Init_Reader(i_pFile);
    }

    if (    (Bk::eOk == l_eReturn)
         && (TRUE == l_pStorage->Is_Failed())
       )
    {
        l_eReturn = Bk::eErrorInternal;
    }

    if (    (Bk::eOk != l_eReturn)
         && (l_pStorage)
       )
    {
        delete l_pStorage;
        l_pStorage = NULL;
    }

    *o_pReader = dynamic_cast<Bk::IStorageReader*>(l_pStorage); 

    return l_eReturn;
}//Open_Storage

//compiler check than function match the prototype
static const fnBkOpenStorage g_pBkOpenStorage = Open_Storage;
}//extern "C"
#pragma endregion  ENTRY

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma region TRACE_DESC
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CTrace_Desc
sTrace_Desc::sTrace_Desc(sP7Trace_Format *i_pData)
    : m_pBuffer(NULL)
    , m_dwSize(0)

    , m_wLine(0)
    , m_pFormat(NULL)
    , m_pFile_Path(NULL)
    , m_pFile_Name(NULL)
    , m_pFunction(NULL)
    , m_dwModuleID(0)
    , m_pArgs(NULL)
    , m_dwArgs_Len(0)
{
    size_t l_szLen = 0;
    if (i_pData)
    {
        m_dwSize  = i_pData->sCommon.dwSize;
        m_pBuffer = new tUINT8[m_dwSize];
    }

    if (m_pBuffer)
    {
        memcpy(m_pBuffer, i_pData, m_dwSize);
        m_wLine       = i_pData->wLine;
        m_dwArgs_Len  = i_pData->wArgs_Len;
        m_dwModuleID  = i_pData->wModuleID;
        m_pArgs       = (sP7Trace_Arg*)(m_pBuffer + sizeof(sP7Trace_Format));


    #if defined(UTF8_ENCODING)
        tWCHAR *l_pFormat = (tWCHAR *)(m_pArgs + m_dwArgs_Len);
        l_szLen  = Get_UTF16_Length(l_pFormat) + 1;
        m_pFormat = new tXCHAR[l_szLen * 2];
        Convert_UTF16_To_UTF8(l_pFormat, m_pFormat, l_szLen * 2);

        m_pFile_Path = (char*)(l_pFormat + l_szLen);

        if ((m_pFile_Name = PStrrChr(m_pFile_Path, '/')))
        {
            m_pFile_Name ++;
        }
        else
        {
            m_pFile_Name = m_pFile_Path;
        };

        m_pFunction = (char*)(m_pFile_Path + strlen(m_pFile_Path) + 1);
    #else
        m_pFormat = (wchar_t *)(m_pArgs + m_dwArgs_Len);

        char  *l_pFile_Path = (char*)(m_pFormat + wcslen(m_pFormat) + 1);
        l_szLen = strlen(l_pFile_Path) + 1;
        char *l_pFunction = (char*)(l_pFile_Path + l_szLen);

        m_pFile_Path  = new wchar_t[l_szLen];
        if (m_pFile_Path)
        {
            Convert_UTF8_To_UTF16(l_pFile_Path, (tWCHAR*)m_pFile_Path, (tUINT32)l_szLen);
        }
        
        if (    (m_pFile_Name = wcsrchr(m_pFile_Path, L'\\'))
             || (m_pFile_Name = wcsrchr(m_pFile_Path, L'/'))
           )
        {
            m_pFile_Name ++;
        }
        else
        {
            m_pFile_Name = m_pFile_Path;
        };

        l_szLen      = strlen(l_pFunction) + 1;
        m_pFunction  = new wchar_t[l_szLen];

        if (m_pFunction)
        {
            Convert_UTF8_To_UTF16(l_pFunction, (tWCHAR*)m_pFunction, (tUINT32)l_szLen);
        }
    #endif
    }
}//CTrace_Desc


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~CTrace_Desc
sTrace_Desc::~sTrace_Desc()
{
    if (m_pBuffer)
    {
        delete [] m_pBuffer;
        m_pBuffer = NULL;
    }

#if defined(UTF8_ENCODING)
    if (m_pFormat)
    {
        delete [] m_pFormat;
        m_pFormat = NULL;
    }
#else
    if (m_pFile_Path)
    {
        delete [] m_pFile_Path;
        m_pFile_Path = NULL;
    }

    if (m_pFunction)
    {
        delete [] m_pFunction;
        m_pFunction = NULL;
    }
#endif

    m_pArgs     = NULL;
    m_pFormat   = NULL;
    m_pFunction = NULL;
}//~CTrace_Desc
#pragma endregion TRACE_DESC



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma region READER
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CTrace_File
CReader::CReader(CTrace_File *i_pParent, tBOOL &o_rError)
    : m_lReference(1)
    , m_pParent(i_pParent)
    , m_pData_Read(NULL)
    , m_dwData_Read_Used(0)
    , m_qwData_Read_Offset(MAXUINT64)
    , m_pIDX_Read(NULL)
    , m_dwIDX_Read_Used(0)
    , m_qwIDX_Read_Start(MAXUINT64)
{
    if (m_pParent)
    {
        m_pParent->Add_Ref();
    }

    o_rError             = FALSE;
    m_pData_Read         = (tUINT8*)malloc(sizeof(tUINT8) * TRACE_READ_DATA_BUFFER_LENGTH);
    m_pIDX_Read          = (tUINT64*)malloc(sizeof(tUINT64) * TRACE_READ_INDEX_BUFFER_LENGTH);
    m_dwData_Read_Used   = 0;
    m_dwIDX_Read_Used    = 0;
    m_qwIDX_Read_Start   = MAXUINT64;
    m_qwData_Read_Offset = MAXUINT64;

    if (    (NULL == m_pData_Read)
         || (NULL == m_pIDX_Read)
       )
    {
        o_rError = TRUE;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~CReader
CReader::~CReader()
{
    m_dwData_Read_Used   = 0;
    m_dwIDX_Read_Used    = 0;
    m_qwIDX_Read_Start   = MAXUINT64;
    m_qwData_Read_Offset = MAXUINT64;

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

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

    if (m_pParent)
    {
        m_pParent->Release();
    }
}//~CReader


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counters
Bk::eResult CReader::Get_Counters(tUINT64 **o_pCounters)
{
    return m_pParent->Get_Counters(o_pCounters);
}//Get_Counters


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counters
Bk::eResult CReader::Get_Count(tUINT64 *o_pCount)
{
    return m_pParent->Get_Count(o_pCount);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counters
Bk::eResult CReader::Get_Trace(tUINT64 i_qwIndex, Bk::stTrace *o_pItem, tBOOL i_bFormat)
{
    return m_pParent->Get_Trace(i_qwIndex, o_pItem, i_bFormat, this);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counters
Bk::eResult CReader::Set_Filter(Bk::ITraceFilter *i_pFilter, const tXCHAR *i_pPath, Bk::ITraceReader **o_pReader)
{
    return m_pParent->Set_Filter(i_pFilter, i_pPath, o_pReader);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counters
Bk::eResult CReader::Get_Filter_Status(tINT32 *o_pPercent)
{
    return m_pParent->Get_Filter_Status(o_pPercent);
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Clr_Filter
Bk::eResult CReader::Clr_Filter(tBOOL i_bDelete)
{
    return m_pParent->Clr_Filter(i_bDelete);
}//Clr_Filter
#pragma endregion READER




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma region STORAGE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CTrace_File
CTrace_File::CTrace_File(Bk::IBNode *i_pNode, CProperty *i_pProp, const tXCHAR *i_pPath)
    : m_lReference(1)
    , m_pHeader(NULL)
    , m_pVarArgs(NULL)
    , m_pText(NULL)

    , m_bIsInfo(FALSE)
    , m_bReadOnly(FALSE)
    , m_bDelete(FALSE)

    , m_pFiltered(NULL)
    , m_lFilter_Progress(0)
    , m_bIsFilter_Thread(FALSE)
    , m_pFilter(NULL)

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

    , m_pData_Write(NULL)
    , m_dwData_Write_Used(0)
    , m_qwData_Write_Offset(TRACE_HEADER_RESERVED_SIZE)
    , m_qwData_Offset(TRACE_HEADER_RESERVED_SIZE)
    , m_pDesc(NULL)
    , m_pFmtBuffer(NULL)
    , m_cThreads(16)
    , m_cThreads_Tree()

    , m_pIDX_Write(NULL)
    , m_dwIDX_Write_Used(0)
    , m_qwIDX_Write_Start(0)

    , m_pNode(i_pNode)
    , m_pProp(i_pProp)
    , m_bSuccess(TRUE)
    , m_bInitialized(FALSE)
    , m_bIs_LittleEndian(TRUE)
    , m_bIs_Extension(FALSE)

    , m_pExtra(NULL)
{
    CProperty *l_pPropPath = NULL;
    CWString   l_cPath;

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

    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("Trc/Stg"), &m_hP7_TraceModule);
    }

    LOCK_CREATE(m_hCS);

    if (m_bSuccess)
    {
        m_pHeader  = (sFile_Header*)(new tUINT8[TRACE_HEADER_RESERVED_SIZE]);
        m_pDesc    = new CBList<sTrace_Desc*>;
        m_pVarArgs = new tUINT8[TRACE_VARARGS_LENGTH];
        m_pText    = new tXCHAR[TRACE_TEXT_LENGTH];

        if (    (NULL == m_pDesc)
             || (NULL == m_pHeader)
             || (NULL == m_pVarArgs)
             || (NULL == m_pText)
           )
        {
            LOG_ERROR(TM("[0x%08p] Initialization failed (memory allocation)"), this);
            m_bSuccess = FALSE;
        }

        memset(m_pHeader, 0, TRACE_HEADER_RESERVED_SIZE);
        memset(m_pVarArgs, 0, TRACE_VARARGS_LENGTH);
        memset(m_pText, 0, TRACE_TEXT_LENGTH);
    }


    if (m_bSuccess)
    {
        //fill list by empty elements, prepare data ...
        for (tUINT32 l_dwIDX = 0; l_dwIDX < 4096; l_dwIDX ++)
        {
            m_pDesc->Add_After(NULL, NULL);
            m_cFormatters.Add_After(NULL, NULL);
        }
        //trick to build index, this is new list and index is not build
        //when we try to access to element by index - we create internal index
        //table
        (*m_pDesc)[0]; 
        m_cFormatters[0];


        //fill list by empty elements, prepare data ...
        for (tUINT32 l_dwIDX = 0; l_dwIDX < 128; l_dwIDX ++)
        {
            m_cModules.Add_After(NULL, NULL);
        }

        //trick to build index, this is new list and index is not build
        //when we try to access to element by index - we create internal index
        //table
        m_cModules[0];
        
    }

    if (m_pNode)
    {
        m_pNode->Add_Ref();
    }

    if (m_pProp)
    {
        m_pProp->Add_Ref();

        m_pProp->Get_SubItem(TM("Path"), l_pPropPath);
        if (l_pPropPath)
        {
            const tXCHAR *l_pValue = NULL;
            l_pPropPath->Get_Value(l_pValue);

            if (l_pValue)
            {
                l_cPath.Set(l_pValue);
            }
        }
    }

    if (!Set_Directory(l_cPath.Get()))
    {
        CFSYS::GetUserDirectory(&l_cPath);
        l_cPath.Append(1, TM("/Baical/"));

        Set_Directory(l_cPath.Get());
        if (l_pPropPath)
        {
            l_pPropPath->Set_Value(l_cPath.Get());
        }

        LOG_WARNING(TM("[0x%08p] Can't access configuration path, reaplce by {%s}"), 
                    this, 
                    l_cPath.Get()
                   );
    }

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

    if (i_pPath)
    {
        m_cPath_File_Data.Set(i_pPath);
    }
    else
    {
        m_cPath_File_Data.Set(TM(""));
    }

    if (FALSE == m_cFilter_Exit_Event.Init(1, EMEVENT_SINGLE_AUTO))
    {
        LOG_ERROR(TM("[0x%08p] Exit event wasn't created !"), this);
    }
}//CTrace_File


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~CTrace_File
CTrace_File::~CTrace_File()
{
    Clr_Filter(TRUE);

    Uninit_Writer();

    m_cFile_Data.Close(TRUE);
    m_cFile_IDX.Close(TRUE);

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

    m_cFormatters.Clear(TRUE);
    m_cModules.Clear(TRUE);

    m_cThreads.Clear(TRUE);


    if (m_pFmtBuffer)
    {
        m_pFmtBuffer->Release();
    }

    if (m_pHeader)
    {
        //allocated as pointer to tUINT8
        tUINT8 *l_pBuffer = (tUINT8*)m_pHeader;
        delete [] l_pBuffer; 
        m_pHeader = NULL;
    }

    if (m_pVarArgs)
    {
        delete [] m_pVarArgs;
        m_pVarArgs = NULL;
    }

    if (m_pText)
    {
        delete [] m_pText;
        m_pText = NULL;
    }

    if (m_bDelete)
    {
        CFSYS::Delete_File(m_cPath_File_Data.Get());
        CFSYS::Delete_File(m_cPath_File_IDX.Get());
       
        if (m_cPath_Directory.Length())
        {
            tXCHAR   *l_pDir  = NULL;
            tXCHAR   *l_pDivA = NULL;
            tXCHAR   *l_pDivB = NULL;
            CWString  l_cDirCopy;

            l_cDirCopy.Set(m_cPath_Directory.Get());
            l_pDir  = l_cDirCopy.Get();
            l_pDivA = l_pDir + PStrLen(l_pDir) - 1;

            for (tUINT32 l_dwI = 0; l_dwI < 3; l_dwI++)
            {
                //removes directory only if it is empty !!!!
                if (CFSYS::Directory_Delete(l_pDir))
                {
                    l_pDivB = l_pDivA;

                    while ( 1 >= (l_pDivB - l_pDivA))
                    {
                        l_pDivB = l_pDivA;

                        l_pDivA = PStrrChr(l_pDir, TM('\\'));

                        if (NULL == l_pDivA)
                        {
                            l_pDivA = PStrrChr(l_pDir, TM('/'));
                        }

                        if (l_pDivA)
                        {
                            l_pDivA[0] = 0;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                else
                {
                    break;
                }
            }
        }
    }

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

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

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

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

    LOCK_DESTROY(m_hCS);
}//~CTrace_File


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Init_Reader
Bk::eResult CTrace_File::Init_Reader(const tXCHAR *i_pFile_Name)
{
    Bk::eResult l_eReturn = Bk::eOk;
    size_t      l_szData  = 64 * 1024;
    tUINT8     *l_pData   = NULL;

    if (    (NULL == i_pFile_Name)
         || (FALSE == CFSYS::File_Exists(i_pFile_Name))
       )
    {
        LOG_ERROR(TM("[0x%08p] Initialization failed - wrong input"), this);
        return Bk::eErrorWrongInput;
    }

    if (FALSE == m_bSuccess)
    {
        return Bk::eErrorInternal;
    }

    LOCK_ENTER(m_hCS); 

    if (Bk::eOk == l_eReturn)
    {
        l_pData = (tUINT8*)malloc(l_szData * sizeof(tUINT8));

        if (!l_pData)
        {
            LOG_ERROR(TM("[0x%08p] Memory allocation problem"), this);
            l_eReturn = Bk::eErrorNoBuffer;
        }
    }

    if (Bk::eOk == l_eReturn)
    {
        m_cPath_Directory.Set(i_pFile_Name);
        tXCHAR *l_pSlash = NULL;
        if (    (l_pSlash = PStrrChr(m_cPath_Directory.Get(), TM('\\')))
             || (l_pSlash = PStrrChr(m_cPath_Directory.Get(), TM('/')))
           )
        {
            l_pSlash++;
            (*l_pSlash) = 0;
        }
    }

    if (Bk::eOk == l_eReturn)
    {
        m_bReadOnly = TRUE;

        m_cPath_File_Data.Set(i_pFile_Name);

        if (!m_cFile_Data.Open(m_cPath_File_Data.Get(), 
                                  IFile::EOPEN
                                | IFile::ESHARE_READ 
                                | IFile::EACCESS_READ 
                                | IFile::EACCESS_WRITE
                              )
           )
        {                     
            LOG_ERROR(TM("[0x%08p] File was not possible to open %s, System Error = %d"), 
                      this, 
                      m_cPath_File_Data.Get(), 
                      GetLastError()
                     );

            m_cFile_Data.Close(FALSE);
            l_eReturn    = Bk::eErrorBlocked;
        }
    }

    if (Bk::eOk == l_eReturn)
    {
        m_cPath_File_IDX.Set(i_pFile_Name);
        m_cPath_File_IDX.Trim(m_cPath_File_IDX.Length() - 4);
        m_cPath_File_IDX.Append(1, TM(".p7i"));

        if (!m_cFile_IDX.Open(m_cPath_File_IDX.Get(), 
                                 IFile::EOPEN
                               | IFile::ESHARE_READ 
                               | IFile::EACCESS_READ 
                               | IFile::EACCESS_WRITE
                             )
           )
        {
            LOG_ERROR(TM("[0x%08p] File was not possible to open %s, System Error = %d"), 
                      this,
                      m_cPath_File_IDX.Get(), 
                      GetLastError()
                     );
            l_eReturn   = Bk::eErrorBlocked;
        }
    }

    //read header
    if (Bk::eOk == l_eReturn)
    {
        m_cFile_Data.Set_Position(0ull);
        size_t l_szRead = m_cFile_Data.Read((tUINT8*)m_pHeader, sizeof(sFile_Header));

        if (sizeof(sFile_Header) > l_szRead)
        {
            LOG_ERROR(TM("[0x%08p] Error header reading - size missmatch"), this);
            l_eReturn = Bk::eErrorInternal;
        }
    }

    //check header version and flags
    if (Bk::eOk == l_eReturn)
    {
        if (    (FILE_HEADER_MARKER != m_pHeader->qwMarker)
             || (FILE_HEADER_VERSION != m_pHeader->wHeader_Version)
           )
        {
            LOG_ERROR(TM("[0x%08p] File is not supported (marker (%I64d)/version (%d) are incorrect)"), 
                      this,
                      m_pHeader->qwMarker,
                      (tUINT32)m_pHeader->wHeader_Version
                     );
            l_eReturn = Bk::eErrorInternal;
        }
        else if (sizeof(sFile_Header) >= m_pHeader->qwDesc_Offset)
        {
            LOG_ERROR(TM("[0x%08p] File {%s} is corrupted"), this, i_pFile_Name);
            l_eReturn = Bk::eErrorNotSupported;
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //read descriptions
    if (Bk::eOk == l_eReturn)
    {
        size_t   l_szReaded      = 0;
        tUINT64  l_qwFile_Offset = m_pHeader->qwDesc_Offset;
        tINT64   l_llToRead      = (tINT64)m_pHeader->qwDesc_Size;

        if (FILE_HEADER_FLAG_TRACE_EXTENSION & m_pHeader->iFlags)
        {
            m_bIs_Extension = TRUE;
        }

        while (Read_Buffer(m_cFile_Data, 
                           l_qwFile_Offset, 
                           l_pData, 
                           (l_llToRead > (tINT64)l_szData) ? l_szData : (size_t)l_llToRead,
                           &l_szReaded)
              )
        {
            size_t l_szData_Offset = 0;

            while ( (l_szData_Offset + sizeof(sP7Trace_Format)) < l_szReaded)
            {
                sP7Trace_Format *i_pDesc = (sP7Trace_Format *)(l_pData + l_szData_Offset);
                if ( (l_szData_Offset + i_pDesc->sCommon.dwSize) <= l_szReaded)
                {
                    //fill by empty elements if ID is larger than elements count
                    while (m_pDesc->Count() <= i_pDesc->wID)
                    {
                        m_pDesc->Add_After(m_pDesc->Get_Last(), NULL);
                    }

                    if (NULL == (*m_pDesc)[i_pDesc->wID])
                    {
                        m_pDesc->Put_Data(m_pDesc->Get_ByIndex(i_pDesc->wID), 
                                            new sTrace_Desc(i_pDesc), 
                                            TRUE
                                            );
                    }

                    l_szData_Offset += i_pDesc->sCommon.dwSize;
                    l_llToRead      -= i_pDesc->sCommon.dwSize;
                }
                else
                {
                    break;
                }
            }

            l_qwFile_Offset += l_szData_Offset;
            if (0 >= l_llToRead)
            {
                break;
            }
            //sTrace_Desc *l_pDesc 
        }
    }//if (Bk::eOk == l_eReturn) read descriptions


    ////////////////////////////////////////////////////////////////////////////
    //read extra
    if (Bk::eOk == l_eReturn)
    {
        size_t        l_szRead   = 0;
        sExtra_Header l_sExtra   = {0};
        tUINT64       l_qwOffset = 0ull;

        l_qwOffset = m_pHeader->qwDesc_Offset + m_pHeader->qwDesc_Size;

        while (Read_Buffer(m_cFile_Data, 
                           l_qwOffset, 
                           (tUINT8*)&l_sExtra,
                           sizeof(l_sExtra), 
                           &l_szRead
                          )
             )
        {
            l_qwOffset += sizeof(l_sExtra); 

            if (sizeof(l_sExtra) != l_szRead)
            {
                LOG_INFO(TM("[0x%08p] no extra data (threads, modules)"), this);
                break;
            }

            if (FILE_HEADER_MARKER != l_sExtra.qwMarker)
            {
                LOG_ERROR(TM("[0x%08p] extra header is damaged, marker = %I64d"), 
                          this, 
                          l_sExtra.qwMarker
                         );
                break;
            }

            if (TRACE_FILE_EXTRA_THREADS == l_sExtra.dwType)
            {
                sThreads *l_pThreads = new sThreads(l_sExtra.dwCount);

                if (Read_Buffer(m_cFile_Data, 
                                l_qwOffset, 
                                (tUINT8*)l_pThreads->pThreads,
                                (size_t)l_sExtra.qwSize, 
                                &l_szRead
                               )
                   )
                {
                    l_pThreads->dwUsed = (tUINT32)(l_szRead / sizeof(sThread));
                    if (l_pThreads->dwUsed)
                    {
                        m_cThreads.Add_After(NULL, l_pThreads);

                        for (tUINT32 l_dwI = 0; l_dwI < l_pThreads->dwUsed; l_dwI++)
                        {
                            Update_Threads_Tree(&l_pThreads->pThreads[l_dwI]);
                        }
                    }
                    else
                    {
                        delete l_pThreads;
                        l_pThreads = NULL;

                    }
                }
            }
            else if (TRACE_FILE_EXTRA_MODULES == l_sExtra.dwType)
            {
                sP7Trace_Module *l_pModules = (sP7Trace_Module *)l_pData;
                sP7Trace_Module *l_pModule  = NULL;
                tBOOL            l_bFree    = FALSE;

                if (l_sExtra.qwSize > (tUINT64)l_szData)
                {
                    l_pModules = (sP7Trace_Module*)malloc((size_t)l_sExtra.qwSize); 
                    l_bFree    = TRUE;
                }

                if (l_pModules)
                {
                    if (Read_Buffer(m_cFile_Data, l_qwOffset, (tUINT8*)l_pModules, (size_t)l_sExtra.qwSize, &l_szRead))
                    {
                        l_pModule = l_pModules;
                        while (l_sExtra.dwCount)
                        {
                            //fill by empty elements if ID is larger than elements count
                            while (m_cModules.Count() <= l_pModule->wModuleID)
                            {
                                m_cModules.Add_After(m_cModules.Get_Last(), NULL);
                            }

                            if (NULL == m_cModules[l_pModule->wModuleID])
                            {
                                m_cModules.Put_Data(m_cModules.Get_ByIndex(l_pModule->wModuleID), 
                                                    new sP7Trace_Module(*l_pModule), 
                                                    TRUE
                                                   );
                            }

                            l_sExtra.dwCount --;
                            l_pModule++;
                        }
                    }

                    if (    (l_bFree)
                         && (l_pModules)
                       )
                    {
                        free(l_pModules);
                        l_pModules = NULL;
                    }
                }
            }

            l_qwOffset += l_sExtra.qwSize; 
        }
    }//if (Bk::eOk == l_eReturn) read extra


    if (Bk::eOk == l_eReturn)
    {
        ////////////////////////////////////////////////////////////////////////
        //Fill info
        GUID l_sGUID = BK_READER_GUID_TRACE;
        memcpy(&m_sInfo.sType, &l_sGUID, sizeof(GUID));

        //we do not store IP information, IP of the node can be changed since
        //last data receiving
        //memcpy(&m_sInfo.sAddress, &i_pInfo->sAddress, sizeof(sockaddr_storage));

    #if defined(UTF8_ENCODING)
        Convert_UTF16_To_UTF8(m_pHeader->pNode_Name, m_sInfo.pNode, LENGTH(m_sInfo.pNode));
        Convert_UTF16_To_UTF8(m_pHeader->pProcess_Name, m_sInfo.pProcess_Name, LENGTH(m_sInfo.pProcess_Name));
        Convert_UTF16_To_UTF8(m_pHeader->pStream_Name, m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name));
    #else
        wcscpy_s(m_sInfo.pNode, LENGTH(m_sInfo.pNode), m_pHeader->pNode_Name);
        wcscpy_s(m_sInfo.pProcess_Name, LENGTH(m_sInfo.pProcess_Name), m_pHeader->pProcess_Name);
        wcscpy_s(m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name), m_pHeader->pStream_Name); 
    #endif

        m_sInfo.dwProcess_ID  = m_pHeader->dwProcess_ID;
        m_sInfo.sProcess_Time = m_pHeader->sProcess_Time;
        m_sInfo.sStream_Time  = m_pHeader->sStream_Time; 

        m_bIsInfo = TRUE;
    }

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

    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Init_Reader


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Info
Bk::eResult CTrace_File::Get_Info(Bk::stStorageInfo *o_pInfo)
{
    if (FALSE == m_bSuccess)
    {
        return Bk::eErrorInternal;
    }

    Bk::eResult l_eReturn = Bk::eErrorInternal;

    LOCK_ENTER(m_hCS); 
    if (m_bIsInfo)
    {
        memcpy(o_pInfo, &m_sInfo, sizeof(m_sInfo));
        l_eReturn = Bk::eOk;
    }
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
} //Get_Info


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Path
Bk::eResult CTrace_File::Get_Path(tXCHAR **o_pPath)
{
    if (FALSE == m_bSuccess)
    {
        return Bk::eErrorInternal;
    }

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

    LOCK_ENTER(m_hCS); 

#if defined(UTF8_ENCODING)
    *o_pPath = strdup(m_cPath_Directory.Get());
#else
    *o_pPath = _wcsdup(m_cPath_Directory.Get());
#endif

    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
} //Get_Path


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Size
Bk::eResult CTrace_File::Get_Size(tUINT64 *o_pSize)
{
    if (FALSE == m_bSuccess)
    {
        return Bk::eErrorInternal;
    }

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

    LOCK_ENTER(m_hCS); 
    *o_pSize  = m_dwData_Write_Used + m_dwIDX_Write_Used;
    *o_pSize += m_cFile_Data.Get_Size();
    *o_pSize += m_cFile_IDX.Get_Size();
    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Get_Size


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Description
Bk::eResult  CTrace_File::Get_Description(Bk::stStorageDesc &io_rDesc)
{
    if (    (!io_rDesc.pText)
         || (64 > io_rDesc.szTextMax)
       )
    {
        return Bk::eErrorWrongInput;
    }

    PSPrint(io_rDesc.pText,
            io_rDesc.szTextMax,
            TM("Total %lld (W:%lld, E:%lld, C:%lld)"),
            (tUINT64)m_pHeader->qwTrace_Count,
            m_pHeader->pCounters[EP7TRACE_LEVEL_WARNING],
            m_pHeader->pCounters[EP7TRACE_LEVEL_ERROR],
            m_pHeader->pCounters[EP7TRACE_LEVEL_CRITICAL]
           );

    if (m_pHeader->pCounters[EP7TRACE_LEVEL_CRITICAL])
    {
        io_rDesc.eLevel = Bk::eStorageDescLevelCritical;
    }
    else if (m_pHeader->pCounters[EP7TRACE_LEVEL_ERROR])
    {
        io_rDesc.eLevel = Bk::eStorageDescLevelError;
    }
    else if (m_pHeader->pCounters[EP7TRACE_LEVEL_WARNING])
    {
        io_rDesc.eLevel = Bk::eStorageDescLevelWarning;
    }
    else
    {
        io_rDesc.eLevel = Bk::eStorageDescLevelNormal;
    }

    return Bk::eOk;
}//Get_Description


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Init_Writer
Bk::eResult CTrace_File::Init_Writer(Bk::stStreamInfo *i_pInfo)
{
    if (NULL == i_pInfo)
    {
        LOG_ERROR(TM("[0x%08p] Initialization failed (wrong arguments)"), this);
        m_bSuccess = FALSE;
    }

    if (m_bSuccess)
    {
        m_pData_Write  = new tUINT8[TRACE_WRITE_DATA_BUFFER_LENGTH];
        m_pIDX_Write   = new tUINT64[TRACE_WRITE_INDEX_BUFFER_LENGTH];

        if (    (NULL == m_pData_Write)
             || (NULL == m_pIDX_Write)
           )
        {
            LOG_ERROR(TM("[0x%08p] Initialization failed (memory allocation)"), this);
            m_bSuccess = FALSE;
        }
    }


    if (m_bSuccess)
    {
        ////////////////////////////////////////////////////////////////////////
        //Fill header
        memset(m_pHeader, 0, TRACE_HEADER_RESERVED_SIZE);
        m_pHeader->qwMarker        = FILE_HEADER_MARKER;
        m_pHeader->wHeader_Version = FILE_HEADER_VERSION;

    #if defined(UTF8_ENCODING)
        Convert_UTF8_To_UTF16(i_pInfo->pNode, m_pHeader->pNode_Name, MAX_NODE_NAME_LENGTH);
        Convert_UTF8_To_UTF16(i_pInfo->pProcess_Name, m_pHeader->pProcess_Name, MAX_PROCESS_NAME_LENGTH);
    #else
        wcscpy_s(m_pHeader->pNode_Name, LENGTH(m_pHeader->pNode_Name), i_pInfo->pNode);
        wcscpy_s(m_pHeader->pProcess_Name, LENGTH(m_pHeader->pProcess_Name), i_pInfo->pProcess_Name);
    #endif

        m_pHeader->wProtocol_Version  = i_pInfo->wProtocol_Version;
        m_pHeader->dwProcess_ID       = i_pInfo->dwProcess_ID;
        m_pHeader->sProcess_Time      = i_pInfo->sProcess_Time;

        ////////////////////////////////////////////////////////////////////////
        //Fill info
        m_bIs_LittleEndian   = !i_pInfo->bIs_BigEndian;

        m_sInfo.dwProcess_ID = i_pInfo->dwProcess_ID;

        PStrCpy(m_sInfo.pNode, LENGTH(m_sInfo.pNode), i_pInfo->pNode);
        PStrCpy(m_sInfo.pProcess_Name, LENGTH(m_sInfo.pProcess_Name), i_pInfo->pProcess_Name);

        memcpy(&m_sInfo.sAddress, &i_pInfo->sAddress, sizeof(sockaddr_storage));
        m_sInfo.sProcess_Time = i_pInfo->sProcess_Time;

        GUID l_sGUID = BK_READER_GUID_TRACE;
        memcpy(&m_sInfo.sType, &l_sGUID, sizeof(GUID));
    }

    return (m_bSuccess) ? Bk::eOk : Bk::eErrorInternal;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Uninit_Writer
Bk::eResult CTrace_File::Uninit_Writer()
{
    Bk::eResult l_eReturn = Bk::eOk;

    LOCK_ENTER(m_hCS); 

    if (    (TRUE  == m_bReadOnly)
         || (FALSE == m_bSuccess)
       )
    {
        goto l_lblExit;
        l_eReturn = Bk::eErrorNotSupported;
    }

    ////////////////////////////////////////////////////////////////////////////
    //write data & description
    if (m_cFile_Data.IsOpened())
    {
        if (m_bSuccess)
        {
            m_bSuccess = Write_Buffer(m_cFile_Data, m_pData_Write, m_dwData_Write_Used);
            m_dwData_Write_Used = 0;
            m_pHeader->qwDesc_Offset = m_cFile_Data.Get_Size();
        }

        ////////////////////////////////////////////////////////////////////////
        //dump description list
        if (m_pDesc)
        {
            pAList_Cell l_pCell = NULL;
            while (    (l_pCell = m_pDesc->Get_Next(l_pCell))
                    && (m_bSuccess)
                  )
            {
                sTrace_Desc *l_pDesc = m_pDesc->Get_Data(l_pCell);
                if (l_pDesc)
                {
                    m_pHeader->qwDesc_Size += l_pDesc->m_dwSize;

                    if (l_pDesc->m_dwSize > TRACE_WRITE_DATA_BUFFER_LENGTH)
                    {
                        m_bSuccess = Write_Buffer(m_cFile_Data, 
                                                  l_pDesc->m_pBuffer, 
                                                  l_pDesc->m_dwSize
                                                 );
                    }
                    else 
                    {
                        if ((l_pDesc->m_dwSize + m_dwData_Write_Used) >= TRACE_WRITE_DATA_BUFFER_LENGTH)
                        {
                            m_bSuccess = Write_Buffer(m_cFile_Data, m_pData_Write, m_dwData_Write_Used);
                            m_dwData_Write_Used = 0;
                        }

                        memcpy(m_pData_Write + m_dwData_Write_Used, 
                               l_pDesc->m_pBuffer, 
                               l_pDesc->m_dwSize
                              );
                        m_dwData_Write_Used += l_pDesc->m_dwSize;
                    }
                }
            } //while (    (l_pCell = m_pDesc->Get_Next(l_pCell))

            if (    (m_bSuccess)
                 && (m_dwData_Write_Used)
               )
            {
                m_bSuccess = Write_Buffer(m_cFile_Data, m_pData_Write, m_dwData_Write_Used);
                m_dwData_Write_Used = 0;
            }
        } //if (m_pDesc)


        ////////////////////////////////////////////////////////////////////////
        //Dump threads 
        if (    (m_bSuccess)
             && (m_cThreads.Count())
           )
        {
            pAList_Cell   l_pEl     = NULL;
            sExtra_Header l_sHeader = {0};
            l_sHeader.qwMarker = FILE_HEADER_MARKER;
            l_sHeader.dwType   = TRACE_FILE_EXTRA_THREADS;
            l_sHeader.dwCount  = 0ull;
            l_sHeader.qwSize   = 0ull;

            while ((l_pEl = m_cThreads.Get_Next(l_pEl)))
            {
                sThreads *l_pBlock = m_cThreads.Get_Data(l_pEl);
                if (l_pBlock)
                {
                    l_sHeader.dwCount += l_pBlock->dwUsed;
                    l_sHeader.qwSize  += l_pBlock->dwUsed * sizeof(sThread);
                }
            }

            if (l_sHeader.qwSize)
            {
                m_bSuccess = Write_Buffer(m_cFile_Data, (tUINT8*)&l_sHeader, sizeof(l_sHeader));

                l_pEl = NULL;
                while (    (m_bSuccess)
                        && (l_pEl = m_cThreads.Get_Next(l_pEl))
                      )
                {
                    sThreads *l_pBlock = m_cThreads.Get_Data(l_pEl);
                    if (    (l_pBlock)
                         && (l_pBlock->dwCount)
                       )
                    {
                        m_bSuccess = Write_Buffer(m_cFile_Data, (tUINT8*)l_pBlock->pThreads, l_pBlock->dwUsed * sizeof(sThread));
                    }
                }
            }
        }//if (m_cThreads.Count())


        ////////////////////////////////////////////////////////////////////////
        //Dump modules
        if (m_bSuccess)
        {
            pAList_Cell   l_pEl     = NULL;
            sExtra_Header l_sHeader = {0};
            l_sHeader.qwMarker = FILE_HEADER_MARKER;
            l_sHeader.dwType   = TRACE_FILE_EXTRA_MODULES;
            l_sHeader.dwCount  = 0ull;
            l_sHeader.qwSize   = 0ull;

            while ((l_pEl = m_cModules.Get_Next(l_pEl)))
            {
                sP7Trace_Module *l_pModule = m_cModules.Get_Data(l_pEl);
                if (l_pModule)
                {
                    l_sHeader.dwCount ++;
                }
            }

            l_sHeader.qwSize = l_sHeader.dwCount * sizeof(sP7Trace_Module);

            if (l_sHeader.qwSize)
            {
                m_bSuccess = Write_Buffer(m_cFile_Data, (tUINT8*)&l_sHeader, sizeof(l_sHeader));

                m_dwData_Write_Used = 0;
                l_pEl = NULL;

                while (    (l_pEl = m_cModules.Get_Next(l_pEl))
                        && (m_bSuccess)
                      )
                {
                    sP7Trace_Module *l_pModule = m_cModules.Get_Data(l_pEl);
                    if (l_pModule)
                    {
                        if (sizeof(sP7Trace_Module) > TRACE_WRITE_DATA_BUFFER_LENGTH)
                        {
                            m_bSuccess = Write_Buffer(m_cFile_Data, 
                                                      (tUINT8*)l_pModule, 
                                                      sizeof(sP7Trace_Module)
                                                     );
                        }
                        else 
                        {
                            if ((sizeof(sP7Trace_Module) + m_dwData_Write_Used) >= TRACE_WRITE_DATA_BUFFER_LENGTH)
                            {
                                m_bSuccess = Write_Buffer(m_cFile_Data, 
                                                          m_pData_Write, 
                                                          m_dwData_Write_Used
                                                         );
                                m_dwData_Write_Used = 0;
                            }

                            memcpy(m_pData_Write + m_dwData_Write_Used, 
                                   l_pModule, 
                                   sizeof(sP7Trace_Module)
                                  );
                            m_dwData_Write_Used += sizeof(sP7Trace_Module);
                        }
                    }
                } //while (    (l_pCell = m_pDesc->Get_Next(l_pCell))

                if (    (m_bSuccess)
                     && (m_dwData_Write_Used)
                   )
                {
                    m_bSuccess = Write_Buffer(m_cFile_Data, 
                                              m_pData_Write, 
                                              m_dwData_Write_Used
                                             );
                }
            }
        }//if (m_bSuccess)


        //rewrite header with updated information
        if (m_bSuccess)
        {
            m_bSuccess = Write_Buffer(m_cFile_Data, (tUINT8*)m_pHeader, sizeof(sFile_Header), FALSE);
        }

        ////////////////////////////////////////////////////////////////////////
        //reopen the file with different access rights
        m_cFile_Data.Close(TRUE);

        if (!m_cFile_Data.Open(m_cPath_File_Data.Get(),
                                 IFile::EOPEN
                               | IFile::ESHARE_READ 
                               | IFile::EACCESS_READ 
                               | IFile::EACCESS_WRITE
                              )
           )
        {
            LOG_ERROR(TM("[0x%08p] File was not possible to open %s, System Error = %d"), 
                      this, 
                      m_cPath_File_Data.Get(), 
                      GetLastError()
                     );
            l_eReturn    = Bk::eErrorBlocked;
            m_bSuccess   = FALSE;
        }
    }//if (m_cFile_Data.IsOpened())

    ////////////////////////////////////////////////////////////////////////////
    //write indexes
    if (    (m_cFile_IDX.IsOpened())
         && (m_bSuccess)
       )
    {
        m_bSuccess = Write_Buffer(m_cFile_IDX, 
                                  (tUINT8*)m_pIDX_Write, 
                                  m_dwIDX_Write_Used * sizeof(tUINT64)
                                 );
        m_dwIDX_Write_Used = 0;


        ////////////////////////////////////////////////////////////////////////
        //reopen the file with different access rights
        m_cFile_IDX.Close(TRUE);
        if (!m_cFile_IDX.Open(m_cPath_File_IDX.Get(),
                                 IFile::EOPEN
                               | IFile::ESHARE_READ 
                               | IFile::EACCESS_READ 
                               | IFile::EACCESS_WRITE
                              )
           )
        {
            LOG_ERROR(TM("[0x%08p] File was not possible to open %s, System Error = %d"), 
                      this, 
                      m_cPath_File_IDX.Get(), 
                      GetLastError()
                     );

            l_eReturn   = Bk::eErrorBlocked;
            m_bSuccess  = FALSE;
        }
    } // if (NULL != m_hFile_IDX) && ...


    if (m_pData_Write)
    {
        delete [] m_pData_Write;
        m_pData_Write = NULL;
    }


    if (m_pIDX_Write)
    {
        delete [] m_pIDX_Write;
        m_pIDX_Write = NULL;
    }

    m_bReadOnly = TRUE;

l_lblExit:
    if (m_pExtra)
    {
        m_pExtra->Release();
        m_pExtra = NULL;
    }

    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Uninit_Writer


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Put_Packet
Bk::eResult CTrace_File::Put_Packet(Bk::stDataChunk *i_pChunk)
{
    Bk::eResult l_eReturn = Bk::eOk;
    sP7Ext_Header *l_pHeader = NULL;

    if (    (NULL  == i_pChunk)
         || (FALSE == m_bSuccess)
         || (TRUE  == m_bReadOnly)
       )
    {
        return Bk::eErrorInternal;
    }

    LOCK_ENTER(m_hCS); 

    //if (m_bDelete)
    //{
    //    goto l_lblExit;
    //}

    while (    (i_pChunk)
            && (i_pChunk->szBuffer)
            && (Bk::eOk == l_eReturn)
          )
    {
        if (!m_bIs_LittleEndian)
        {
            ((sP7Ext_Raw *)i_pChunk->pBuffer)->dwBits = ntohl(((sP7Ext_Raw *)i_pChunk->pBuffer)->dwBits);
        }

        l_pHeader = (sP7Ext_Header *)i_pChunk->pBuffer;

        if (    (EP7USER_TYPE_TRACE == l_pHeader->dwType)
             || (i_pChunk->szBuffer >= l_pHeader->dwSize)
            )
        {
            l_eReturn = Process_Packet(l_pHeader);
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Unknown packet type = %d or wrong size %d:%d"),
                        this,
                        (tUINT32)l_pHeader->dwType,
                        (tUINT32)l_pHeader->dwSize,
                        (tUINT32)i_pChunk->szBuffer
                        );
        }

        if (Bk::eErrorMissmatch != l_eReturn)
        {
            i_pChunk->pBuffer += l_pHeader->dwSize;

            if (i_pChunk->szBuffer >= l_pHeader->dwSize)
            {
                i_pChunk->szBuffer -= l_pHeader->dwSize;
            }
            else 
            {
                i_pChunk->szBuffer = 0;
            }

            if (!i_pChunk->szBuffer)
            {
                i_pChunk = i_pChunk->pNext;
            }
        }
    }

//l_lblExit:
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Put_Packet


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Put_Stream_Ex
Bk::eResult CTrace_File::Put_Stream_Ex(Bk::IStreamEx *i_pEx)
{
    Bk::eResult l_eReturn = Bk::eErrorWrongInput;

    LOCK_ENTER(m_hCS); 

    if (i_pEx)
    {
        if (NULL == m_pExtra)
        {
            if (Bk::eOk != i_pEx->Query_Interface(Bk::eInterfaceStreamExTrace, (void*&)m_pExtra))
            {
                LOG_WARNING(TM("[0x%08p] Put_Stream_Ex() input parameter has unexpected type"), this);
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Put_Stream_Ex() second call"), this);
        }
    }
    else
    {
        LOG_WARNING(TM("[0x%08p] Put_Stream_Ex() Wrong input"), this);
    }

    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Put_Stream_Ex


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Query_Interface
Bk::eResult CTrace_File::Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown)
{
    Bk::eResult l_eReturn = Bk::eErrorNotSupported;

    o_rUnknown = NULL;

    if (Bk::eInterfaceTraceReader == i_eId)
    {
        tBOOL    l_bError  = FALSE;
        CReader *l_pReader = new CReader(this, l_bError);
        if (!l_bError)
        {
            o_rUnknown = dynamic_cast<Bk::ITraceReader*>(l_pReader);
            l_eReturn  = Bk::eOk;
        }
        else
        {
            delete l_pReader;
        }
    }
    else if (Bk::eInterfaceReader == i_eId)
    {
        o_rUnknown = dynamic_cast<Bk::IStorageReader*>(this);
        Add_Ref();
        l_eReturn  = Bk::eOk;
    }

    return l_eReturn;
}//Query_Interface


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Delete
Bk::eResult CTrace_File::Delete()
{
    LOCK_ENTER(m_hCS); 
    m_bDelete = TRUE;
    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Delete


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counters
Bk::eResult CTrace_File::Get_Counters(tUINT64 **o_pCounters)
{
    if (    (FALSE == m_bSuccess)
         || (NULL == (o_pCounters))
       )
    {
        return Bk::eErrorInternal;
    }

    LOCK_ENTER(m_hCS); 
    *o_pCounters = m_pHeader->pCounters;
    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Get_Counters


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Count
Bk::eResult CTrace_File::Get_Count(tUINT64 *o_pCount)
{
    Bk::eResult l_eReturn  = Bk::eOk;

    LOCK_ENTER(m_hCS); 

    if (    (FALSE == m_bSuccess)
         || (NULL == o_pCount)
       )
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    *o_pCount = m_pHeader->qwTrace_Count;

l_lblExit:
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Get_Count


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Trace
Bk::eResult CTrace_File::Get_Trace(tUINT64 i_qwIndex, Bk::stTrace *o_pItem, tBOOL i_bFormat, CReader *i_pReader)
{
    Bk::eResult l_eReturn  = Bk::eOk;

    LOCK_ENTER(m_hCS); 

    if (    (FALSE == m_bSuccess)
         || (NULL  == o_pItem)
       )
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    l_eReturn = Format_Data(Get_Raw_Trace(i_qwIndex, i_pReader), o_pItem, i_bFormat); 
                                   
l_lblExit:
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Get_Trace


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Set_Filter
Bk::eResult CTrace_File::Set_Filter(Bk::ITraceFilter *i_pFilter, const tXCHAR *i_pPath, Bk::ITraceReader **o_pReader)
{
    Bk::eResult       l_eReturn = Bk::eOk;
    Bk::ITraceReader *l_pReader = NULL;

    LOCK_ENTER(m_hCS); 

    if (    (FALSE == m_bSuccess)
         || (NULL  == i_pFilter)
         || (NULL  == o_pReader)
         || (NULL  != m_pFiltered)
       )
    {
        l_eReturn = Bk::eErrorNotSupported;
        goto l_lblExit;
    }

    m_pFiltered = new CTrace_File(m_pNode, m_pProp, i_pPath);

    if (    (NULL == m_pFiltered)
         || (TRUE == m_pFiltered->Is_Failed())
         || (Bk::eOk != m_pFiltered->Clone_Header(this))
       )
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    if (FALSE == CThShell::Create(&Static_Filtering, 
                                  this,
                                  &m_hFilter_Thread,
                                  TM("Trace/Filter")               
                                 )
        )
    {
        LOG_ERROR(TM("[0x%08p] Thread wasn't created !"), this);
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }
    else
    {
        m_bIsFilter_Thread = TRUE;
    }

l_lblExit:
    if (Bk::eOk == l_eReturn)
    {
        m_pFilter = i_pFilter;
        m_pFilter->Add_Ref();
        l_eReturn = m_pFiltered->Query_Interface(Bk::eInterfaceTraceReader, (void*&)l_pReader);
    }
    else if (m_pFiltered)
    {
        m_pFiltered->Delete();
        delete m_pFiltered;
        m_pFiltered = NULL;
    }

    *o_pReader = l_pReader;

    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Set_Filter


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Filter_Status
Bk::eResult CTrace_File::Get_Filter_Status(tINT32 *o_pPercent)
{
    Bk::eResult l_eReturn = Bk::eErrorNotActive;
    LOCK_ENTER(m_hCS); 
    if (m_pFiltered)
    {
        if (!m_pFiltered->Is_Failed())
        {
            l_eReturn = Bk::eOk;
            if (o_pPercent)
            {
                *o_pPercent = m_lFilter_Progress;
            }
        }
        else
        {
            l_eReturn = Bk::eErrorFileWrite;
        }
    }
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Get_Filter_Status


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Clr_Filter
Bk::eResult CTrace_File::Clr_Filter(tBOOL i_bDelete)
{
    if (m_bIsFilter_Thread)
    {
        m_cFilter_Exit_Event.Set(MEVENT_SIGNAL_0);

        if (TRUE == CThShell::Close(m_hFilter_Thread, 15000))
        {
            m_hFilter_Thread = 0;//NULL;
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] stop thread timeout expire !"), this);
        }

        m_bIsFilter_Thread = FALSE;
    }

    LOCK_ENTER(m_hCS); 

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

    if (m_pFiltered)
    {
        if (i_bDelete)
        {
            m_pFiltered->Delete();
        }
        delete m_pFiltered;
        m_pFiltered = NULL;
    }

   LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Clr_Filter


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Filtering()
void CTrace_File::Filtering()
{
    Bk::stTrace     l_sTrace  = {};
    sP7Trace_Data  *l_pData   = NULL;
    tBOOL           l_bExit   = FALSE;
    tUINT32         l_dwSleep = 0;
    tUINT64         l_qwCount = 0;
    tUINT64         l_qwIndex = 0;
    tBOOL           l_bText   = TRUE;
    CReader         l_cReader(this, l_bExit);

    ATOMIC_SET(&m_lFilter_Progress, 0);

    LOG_INFO(TM("[0x%08p] Enter to filtering thread"), this);
    Get_Count(&l_qwCount);

    LOCK_ENTER(m_hCS); 
    l_bText = m_pFilter->Is_Text_Search();
    LOCK_EXIT(m_hCS); 

    l_sTrace.pMessage  = m_pText;
    l_sTrace.szMessage = TRACE_TEXT_LENGTH;

    while (FALSE == l_bExit)
    {
        while (    (l_qwIndex < l_qwCount)
                && (FALSE == l_bExit)
              )
        {
            LOCK_ENTER(m_hCS); 

            l_pData = (sP7Trace_Data*)Get_Raw_Trace(l_qwIndex, &l_cReader);
            if (l_pData)
            {
                if (Bk::eOk == Format_Data((tUINT8*)l_pData, &l_sTrace, l_bText))
                {
                    if (Bk::eOk == m_pFilter->Check(&l_sTrace))
                    {
                        Bk::stDataChunk l_sChunk = {(tUINT8*)l_pData, l_pData->sCommon.dwSize, NULL};
                        m_pFiltered->Put_Packet(&l_sChunk);
                    }
                }
                else
                {
                    LOG_ERROR(TM("[0x%08p] Format_Data() failed, index = %I64d"), 
                              this, 
                              l_qwIndex
                             );
                    l_bExit = TRUE;
                }
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Get_Data(%I64d) failed"), this, l_qwIndex);
                l_bExit = TRUE;
            }

            LOCK_EXIT(m_hCS); 

            l_qwIndex++;

            if (0 == (l_qwIndex % 1000))
            {
                ATOMIC_SET(&m_lFilter_Progress, (((l_qwIndex + 1) * 100) / l_qwCount));
                break;
            }
        }//while (l_qwIndex < l_qwCount)

        l_dwSleep = 0;

        if (l_qwIndex >= l_qwCount)
        {
            Get_Count(&l_qwCount);

            if (l_qwIndex >= l_qwCount)
            {
                ATOMIC_SET(&m_lFilter_Progress, 100);
                l_dwSleep = 500;
            }
        }

        if (MEVENT_SIGNAL_0 == m_cFilter_Exit_Event.Wait(l_dwSleep))
        {
            l_bExit = TRUE;
        }
    }

    ATOMIC_SET(&m_lFilter_Progress, 100);

    LOG_INFO(TM("[0x%08p] Exit from filtering thread"), this);
}//Filtering()


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Set_Directory
tBOOL CTrace_File::Set_Directory(const tXCHAR *i_pDirectory)
{
    if (NULL == i_pDirectory)
    {
        return FALSE;
    }

    CFSYS::Directory_Create(i_pDirectory);

    if (TRUE == CFSYS::Directory_Exists(i_pDirectory)) 
    {
        m_cPath_Root.Set(i_pDirectory);
        return TRUE;
    }

    return FALSE;
}//Set_Directory


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Clone_Header
Bk::eResult CTrace_File::Clone_Header(CTrace_File *i_pFile)
{
    Bk::eResult      l_eReturn = Bk::eOk;
    Bk::stStreamInfo l_sInfo   = {0};
    sP7Trace_Info    l_sTInfo  = {0};
    pAList_Cell      l_pEl     = NULL;

    if (NULL == i_pFile)
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    l_sInfo.wProtocol_Version = i_pFile->m_pHeader->wProtocol_Version;
    l_sInfo.dwProcess_ID      = i_pFile->m_pHeader->dwProcess_ID;
    l_sInfo.sProcess_Time     = i_pFile->m_pHeader->sProcess_Time;
    l_sInfo.dwProcess_ID      = i_pFile->m_sInfo.dwProcess_ID;

#if defined(UTF8_ENCODING)
    Convert_UTF16_To_UTF8(i_pFile->m_pHeader->pNode_Name, l_sInfo.pNode, MAX_NODE_NAME_LENGTH);
    Convert_UTF16_To_UTF8(i_pFile->m_pHeader->pProcess_Name, l_sInfo.pProcess_Name, MAX_PROCESS_NAME_LENGTH);
#else
    wcscpy_s(l_sInfo.pNode, LENGTH(l_sInfo.pNode), i_pFile->m_pHeader->pNode_Name);
    wcscpy_s(l_sInfo.pProcess_Name, LENGTH(l_sInfo.pProcess_Name), i_pFile->m_pHeader->pProcess_Name);
#endif

    memcpy(&l_sInfo.sAddress, &i_pFile->m_sInfo.sAddress, sizeof(sockaddr_storage));
    l_sInfo.sProcess_Time = i_pFile->m_sInfo.sProcess_Time;

    l_sInfo.bIs_BigEndian = FALSE;

    if (Bk::eOk != Init_Writer(&l_sInfo))
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    l_sTInfo.sCommon.dwType    = EP7TRACE_TYPE_INFO;
    l_sTInfo.sCommon.dwSubType = EP7TRACE_TYPE_INFO;
    l_sTInfo.sCommon.dwSize    = sizeof(l_sTInfo);
    l_sTInfo.dwTime_Hi         = i_pFile->m_pHeader->sStream_Time.dwHighDateTime;
    l_sTInfo.dwTime_Lo         = i_pFile->m_pHeader->sStream_Time.dwLowDateTime;  
    l_sTInfo.qwTimer_Value     = i_pFile->m_pHeader->qwTimer_Value;               
    l_sTInfo.qwTimer_Frequency = i_pFile->m_pHeader->qwTimer_Frequency;           

    if (i_pFile->m_pHeader->iFlags & FILE_HEADER_FLAG_UNSORTED)
    {
        l_sTInfo.qwFlags |= P7TRACE_INFO_FLAG_UNSORTED; 
    }

    if (i_pFile->m_pHeader->iFlags & FILE_HEADER_FLAG_TRACE_EXTENSION)
    {
        l_sTInfo.qwFlags |= P7TRACE_INFO_FLAG_EXTENTION; 
    }

    PWStrCpy(l_sTInfo.pName, P7TRACE_NAME_LENGTH, i_pFile->m_pHeader->pStream_Name);

    //initialize new storage by data, files will be created automatically

    {
        Bk::stDataChunk l_sChunk = { (tUINT8*)&l_sTInfo, l_sTInfo.sCommon.dwSize, NULL };
        l_eReturn = Put_Packet(&l_sChunk);
        if (Bk::eOk != l_eReturn)
        {
            goto l_lblExit;
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //copy descriptions ...
    l_pEl = NULL;
    while ((l_pEl = i_pFile->m_pDesc->Get_Next(l_pEl)))
    {
        sTrace_Desc *l_pDesc = i_pFile->m_pDesc->Get_Data(l_pEl);
        if (l_pDesc)
        {
            Bk::stDataChunk l_sChunk = { l_pDesc->m_pBuffer, l_pDesc->m_dwSize, NULL };
            Put_Packet(&l_sChunk);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //copy threads
    l_pEl = NULL;
    while ((l_pEl = i_pFile->m_cThreads.Get_Next(l_pEl)))
    {
        sThreads *l_pBlock = i_pFile->m_cThreads.Get_Data(l_pEl);
        if (l_pBlock)
        {
            for (tUINT32 l_dwI = 0; l_dwI < l_pBlock->dwUsed; l_dwI++)
            {
                sP7Trace_Thread_Start l_sStart;

                l_sStart.sCommon.dwSize    = sizeof(sP7Trace_Thread_Start);
                l_sStart.sCommon.dwType    = EP7USER_TYPE_TRACE;
                l_sStart.sCommon.dwSubType = EP7TRACE_TYPE_THREAD_START;

                l_sStart.dwThreadID        = l_pBlock->pThreads[l_dwI].dwThreadID;
                l_sStart.qwTimer           = l_pBlock->pThreads[l_dwI].qwStart;
                
                strcpy(l_sStart.pName, l_pBlock->pThreads[l_dwI].pName); 

                Bk::stDataChunk l_sChunk = { (tUINT8*)&l_sStart, l_sStart.sCommon.dwSize, NULL };
                Put_Packet(&l_sChunk);

                if (MAXUINT64 != l_pBlock->pThreads[l_dwI].qwStop)
                {
                    sP7Trace_Thread_Stop l_sStop;
                    l_sStop.sCommon.dwSize    = sizeof(sP7Trace_Thread_Stop);
                    l_sStop.sCommon.dwType    = EP7USER_TYPE_TRACE;
                    l_sStop.sCommon.dwSubType = EP7TRACE_TYPE_THREAD_STOP;

                    l_sStop.dwThreadID        = l_pBlock->pThreads[l_dwI].dwThreadID;
                    l_sStop.qwTimer           = l_pBlock->pThreads[l_dwI].qwStop;

                    Bk::stDataChunk l_sChunk = { (tUINT8*)&l_sStop, l_sStop.sCommon.dwSize, NULL };
                    Put_Packet(&l_sChunk);
                }
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //copy modules ...
    l_pEl = NULL;
    while ((l_pEl = i_pFile->m_cModules.Get_Next(l_pEl)))
    {
        sP7Trace_Module *l_pModule = i_pFile->m_cModules.Get_Data(l_pEl);
        if (l_pModule)
        {
            Bk::stDataChunk l_sChunk = { (tUINT8*)l_pModule, sizeof(sP7Trace_Module), NULL };
            Put_Packet(&l_sChunk);
        }
    }


l_lblExit:

    return l_eReturn;
}//Clone_Header



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Raw_Trace
tUINT8 *CTrace_File::Get_Raw_Trace(tUINT64 i_qwIndex, CReader *i_pReader)
{
    tUINT64    l_qwOffset = MAXUINT64;
    tUINT8    *l_pPacket  = NULL;
    tUINT32    l_dwSzMax  = 0;

    if (FALSE == m_bSuccess)           
    {
        l_pPacket = NULL;
        goto l_lblExit;
    }

    ////////////////////////////////////////////////////////////////////////////
    //try to find data offset by using indexes

    //if index is located in write buffer
    if (    (i_qwIndex >= m_qwIDX_Write_Start)
         && (i_qwIndex < (m_qwIDX_Write_Start + m_dwIDX_Write_Used))
         && (m_pIDX_Write)
       )
    {
        l_qwOffset = m_pIDX_Write[i_qwIndex - m_qwIDX_Write_Start];
    }
    else if (    (i_qwIndex >= i_pReader->m_qwIDX_Read_Start)
              && (i_qwIndex < (i_pReader->m_qwIDX_Read_Start + i_pReader->m_dwIDX_Read_Used))
              && (i_pReader->m_pIDX_Read)
            )
    {
        l_qwOffset = i_pReader->m_pIDX_Read[i_qwIndex - i_pReader->m_qwIDX_Read_Start];
    }
    else 
    {
        tUINT64 l_qwRealIndex = i_qwIndex;

        //backward reading - shift window 
        if (i_qwIndex < i_pReader->m_qwIDX_Read_Start)
        {
            if (l_qwRealIndex > TRACE_READ_INDEX_BUFFER_WINDOW)
            {
                l_qwRealIndex -= TRACE_READ_INDEX_BUFFER_WINDOW;
            }
            else
            {
                l_qwRealIndex = 0ull;
            }
        }

        Read_Buffer(m_cFile_IDX, 
                    l_qwRealIndex * (tUINT64)sizeof(tUINT64),
                    (tUINT8 *)i_pReader->m_pIDX_Read,
                    TRACE_READ_INDEX_BUFFER_LENGTH * sizeof(tUINT64), 
                    &i_pReader->m_dwIDX_Read_Used
                   );

        i_pReader->m_dwIDX_Read_Used /= sizeof(tUINT64);

        if ((i_qwIndex - l_qwRealIndex) < i_pReader->m_dwIDX_Read_Used) //more or eq to needed length
        {
            i_pReader->m_qwIDX_Read_Start = l_qwRealIndex;
            l_qwOffset = i_pReader->m_pIDX_Read[i_qwIndex - l_qwRealIndex];
        }
        // else //print all the time when we reach end of the file
        // {
        //     LOG_ERROR(TM("[0x%08p] Can't read enough indexes, requested index=%I64d, index of buffer=%I64d, buffer length=%d"),  
        //                 this, 
        //                 i_qwIndex, 
        //                 l_qwRealIndex,
        //                 m_dwIDX_Read_Used
        //                 );
        // }
    }

    if (MAXUINT64 == l_qwOffset)
    {
        l_pPacket = NULL;
        goto l_lblExit;
    }

    ////////////////////////////////////////////////////////////////////////////
    //try to find data by using offset
    //if index is located in write buffer
    if (    (l_qwOffset >= m_qwData_Write_Offset)
         && (l_qwOffset < (m_qwData_Write_Offset + m_dwData_Write_Used))
         && (m_pData_Write)
       )
    {
        l_pPacket = m_pData_Write + (l_qwOffset - m_qwData_Write_Offset);
        l_dwSzMax = m_dwData_Write_Used - (tUINT32)(l_pPacket - m_pData_Write);
    }
    else if (    (l_qwOffset >= i_pReader->m_qwData_Read_Offset)
              && (l_qwOffset < (i_pReader->m_qwData_Read_Offset + i_pReader->m_dwData_Read_Used))
              && (i_pReader->m_pData_Read)
            )
    {
        l_pPacket = i_pReader->m_pData_Read + (l_qwOffset - i_pReader->m_qwData_Read_Offset);
        l_dwSzMax = (tUINT32)(i_pReader->m_dwData_Read_Used - (l_pPacket - i_pReader->m_pData_Read));
    }

    if (    (NULL == l_pPacket)
         || (sizeof(sP7Trace_Data) > l_dwSzMax)
         || (((sP7Trace_Data*)l_pPacket)->sCommon.dwSize > l_dwSzMax)
       )
    {
        tUINT64 l_qwRealOffs = l_qwOffset;
        if (l_qwRealOffs < i_pReader->m_qwData_Read_Offset)  //backward reading - shift window 
        {
            if (l_qwRealOffs >= TRACE_READ_DATA_BUFFER_WINDOW)
            {
                l_qwRealOffs -= TRACE_READ_DATA_BUFFER_WINDOW;
            }
            else
            {
                l_qwRealOffs = 0ull;
            }
        }

        l_pPacket = NULL;

        Read_Buffer(m_cFile_Data, 
                    l_qwRealOffs,
                    (tUINT8 *)i_pReader->m_pData_Read,
                    TRACE_READ_DATA_BUFFER_LENGTH, 
                    &i_pReader->m_dwData_Read_Used
                   );

        if (i_pReader->m_dwData_Read_Used)
        {
            size_t l_dwTailSize = i_pReader->m_dwData_Read_Used - (size_t)(l_qwOffset - l_qwRealOffs);
            i_pReader->m_qwData_Read_Offset = l_qwRealOffs;

            l_pPacket = i_pReader->m_pData_Read + (l_qwOffset - i_pReader->m_qwData_Read_Offset);

            if (    (sizeof(sP7Trace_Data) > l_dwTailSize)
                 || (((sP7Trace_Data*)l_pPacket)->sCommon.dwSize > l_dwTailSize)
               )
            {
                l_pPacket = NULL;
                LOG_ERROR(TM("[0x%08p] Can't read enough data, requested offset=%I64d, buffer offset=%I64d, buffer length=%zd"),  
                          this, 
                          l_qwOffset, 
                          l_qwRealOffs,
                          i_pReader->m_dwData_Read_Used
                         );
            }
        }
        else
        {
            i_pReader->m_qwData_Read_Offset = MAXUINT64;
            LOG_ERROR(TM("[0x%08p] Can't read enough data = end of the file"), this);
        }
    }

l_lblExit:
    return l_pPacket;
}//Get_Data


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Format_Data
Bk::eResult CTrace_File::Format_Data(tUINT8 *i_pPacket, Bk::stTrace *o_pItem, tBOOL i_bFormat)
{
    Bk::eResult     l_eReturn    = Bk::eOk;
    sP7Trace_Data  *l_pData      = NULL;
    sTrace_Desc    *l_pDesc      = NULL;
    sRbThread      *l_pRbThread  = NULL;
    CFormatter     *l_pFormatter = NULL;

    if (    (NULL == i_pPacket)
         || (NULL == o_pItem)
       )
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    l_pData = (sP7Trace_Data*)i_pPacket;
    l_pDesc = (*m_pDesc)[l_pData->wID];

    l_pFormatter = m_cFormatters[l_pData->wID];
    if (!l_pFormatter)
    {
        //fill by empty elements if ID is larger than elements count
        while (m_cFormatters.Count() <= l_pData->wID)
        {
            m_cFormatters.Add_After(m_cFormatters.Get_Last(), NULL);
        }

        if (!m_pFmtBuffer)
        {
            m_pFmtBuffer = new CFormatter::sBuffer(8192);
        }

        l_pFormatter = new CFormatter(l_pDesc->m_pFormat, l_pDesc->m_pArgs, (size_t)l_pDesc->m_dwArgs_Len, m_pFmtBuffer);
        m_cFormatters.Put_Data(m_cFormatters.Get_ByIndex(l_pData->wID), l_pFormatter, TRUE);
    }
    
    if (NULL == l_pDesc)
    {
        l_eReturn = Bk::eErrorInternal;
        goto l_lblExit;
    }

    o_pItem->dwThreadID  = l_pData->dwThreadID;
    o_pItem->eLevel      = (Bk::eTraceLevel)l_pData->bLevel;
    o_pItem->pFile_Path  = l_pDesc->m_pFile_Path;
    o_pItem->pFile_Name  = l_pDesc->m_pFile_Name;
    o_pItem->pFunction   = l_pDesc->m_pFunction;
    o_pItem->bProcessor  = l_pData->bProcessor;
    o_pItem->dwSequence  = l_pData->dwSequence;
    o_pItem->dwModuleID  = l_pDesc->m_dwModuleID;
    o_pItem->wID         = l_pData->wID;
    o_pItem->wLine       = l_pDesc->m_wLine;
    o_pItem->pThreadName = NULL;
    o_pItem->pModuleName = NULL;

    if (m_bIs_Extension)
    {
        tUINT8 *l_pExt = i_pPacket + l_pData->sCommon.dwSize - 1;
        tUINT8  l_bCount = l_pExt[0];
        while (l_bCount--)
        {
            --l_pExt;
            if (EP7TRACE_EXT_MODULE_ID == l_pExt[0])
            {
                l_pExt -= 2;
                o_pItem->dwModuleID = *(tUINT16*)l_pExt;
                --l_pExt;
            }
        }
    }

    //time offset in 100ns intervals.
    o_pItem->qwTime = (tUINT64)(   (tDOUBLE)(l_pData->qwTimer - m_pHeader->qwTimer_Value) * 10000000.0
                                 / (tDOUBLE)m_pHeader->qwTimer_Frequency
                               );

    //if formatting is not necessarry we scrip it
    if (FALSE == i_bFormat)
    {
        o_pItem->pMessage = NULL;
        goto l_lblExit;
    }

    if (0 < l_pFormatter->Format(o_pItem->pMessage, o_pItem->szMessage, i_pPacket + sizeof(sP7Trace_Data)))
    {
        tXCHAR *l_pIter = o_pItem->pMessage;
        while (*l_pIter)
        {
            if (    (10 == *l_pIter)
                 || (13 == *l_pIter)
               )
            {
                *l_pIter = TM(';');
            }

            ++l_pIter;
        }
    }
    else
    {
        PStrCpy(o_pItem->pMessage, o_pItem->szMessage, TM("Formatting error! Please check your format string"));
    }

    //Get thread name if it is ...
    l_pRbThread = m_cThreads_Tree.Find(l_pData->dwThreadID);

    if (l_pRbThread)
    {
        while (l_pRbThread)
        {
            if (    (l_pData->qwTimer >= l_pRbThread->pThread->qwStart)
                 && (l_pData->qwTimer <= l_pRbThread->pThread->qwStop)
               )
            {
                o_pItem->pThreadName = l_pRbThread->pThread->pName;
                break;
            }

            l_pRbThread = l_pRbThread->pNext;
        }
    }

    if (m_cModules[o_pItem->dwModuleID])
    {
        o_pItem->pModuleName = m_cModules[o_pItem->dwModuleID]->pName;
    }

                                   
l_lblExit:

    return l_eReturn;
}//Format_Data


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Create_Storage
tBOOL CTrace_File::Create_Files()
{
    tBOOL l_bReturn = TRUE;

    if (!m_cPath_File_Data.Length())
    {
        CWString l_cDirectory;
        CWString l_cFile_Name;
        tXCHAR   l_pDate[32]      = TM("");
        tUINT64  l_qwTime         = 0ull;
        tUINT32  l_uiYear         = 0;
        tUINT32  l_uiMonth        = 0;
        tUINT32  l_uiDay          = 0;
        tUINT32  l_uiHour         = 0;
        tUINT32  l_uiMinutes      = 0;
        tUINT32  l_uiSeconds      = 0;
        tUINT32  l_uiMilliseconds = 0;
        tUINT32  l_uiMicroseconds = 0;
        tUINT32  l_uiNanoseconds  = 0;
    
        //m_cPath_Directory
        l_cDirectory.Realloc(4096);

        //Logs Directory Extraction
        if (m_cPath_Root.Length())
        {
            l_cDirectory.Set(m_cPath_Root.Get());
        }
        else
        {
            CFSYS::GetUserDirectory(&l_cDirectory);
            l_cDirectory.Append(1, TM("/Baical streams/"));
        }

   
        l_qwTime = ((tUINT64)(m_pHeader->sProcess_Time.dwHighDateTime) << 32) + (tUINT64)m_pHeader->sProcess_Time.dwLowDateTime;

        UnpackLocalTime(l_qwTime, l_uiYear, l_uiMonth, l_uiDay, l_uiHour, l_uiMinutes, 
                     l_uiSeconds, l_uiMilliseconds, l_uiMicroseconds, l_uiNanoseconds);

        PSPrint(l_pDate, LENGTH(l_pDate), TM("%04d_%02d_%02d-%02d_%02d_%02d"),
                l_uiYear, 
                l_uiMonth, 
                l_uiDay,
                l_uiHour, 
                l_uiMinutes, 
                l_uiSeconds
               );

        //Directory = Node Name \\ Time Process Name \\ Stream Name IDX  - will be received later
        l_cDirectory.Append(8,
                            TM("/"),
                            m_sInfo.pNode,
                            TM("/"), 
                            l_pDate,
                            TM(" "),
                            m_sInfo.pProcess_Name,
                            TM("/"),
                            m_sInfo.pStream_Name
                           );

        tXCHAR  l_pExt[8] = TM("");
        tUINT32 l_dwIDX   = 0;

        do 
        {
           l_cFile_Name.Set(l_cDirectory.Get());
           PSPrint(l_pExt, LENGTH(l_pExt), TM(" %02d/"), l_dwIDX);
           l_cFile_Name.Append(1, l_pExt);
           l_dwIDX ++;
        } while (CFSYS::Directory_Exists(l_cFile_Name.Get()));

        CFSYS::Directory_Create(l_cFile_Name.Get());

        m_cPath_Directory.Set(l_cFile_Name.Get());

        l_qwTime = ((tUINT64)(m_pHeader->sStream_Time.dwHighDateTime) << 32) + (tUINT64)m_pHeader->sStream_Time.dwLowDateTime;

        UnpackLocalTime(l_qwTime, l_uiYear, l_uiMonth, l_uiDay, l_uiHour, l_uiMinutes, 
                     l_uiSeconds, l_uiMilliseconds, l_uiMicroseconds, l_uiNanoseconds);

        PSPrint(l_pDate, LENGTH(l_pDate), TM("%04d_%02d_%02d-%02d_%02d_%02d_%03d"),
                l_uiYear, 
                l_uiMonth, 
                l_uiDay,
                l_uiHour, 
                l_uiMinutes, 
                l_uiSeconds,
                l_uiMilliseconds
               );

        l_cFile_Name.Append(2, l_pDate, BK_STORAGE_TRACE_FILE_EXT);
        m_cPath_File_Data.Set(l_cFile_Name.Get());
    }

    m_cPath_File_IDX.Set(m_cPath_File_Data.Get());
    m_cPath_File_IDX.Trim(m_cPath_File_Data.Length() - 4);
    m_cPath_File_IDX.Append(1, TM(".p7i"));

    if (!m_cFile_Data.Open(m_cPath_File_Data.Get(),  
                              IFile::ECREATE //No share possibility, to avoid file copy 
                            | IFile::EACCESS_READ 
                            | IFile::EACCESS_WRITE
                          )
       )
    {
        LOG_ERROR(TM("[0x%08p] File was not created %s System Error=%d"), 
                  this, 
                  m_cPath_File_Data.Get(),
                  GetLastError()
                 );
        l_bReturn = FALSE;
    }

    if (!m_cFile_IDX.Open(m_cPath_File_IDX.Get(),  
                             IFile::ECREATE //No share possibility, to avoid file copy 
                           | IFile::EACCESS_READ 
                           | IFile::EACCESS_WRITE
                         )
       )
    {
        LOG_ERROR(TM("[0x%08p] File was not created %s System Error=%d"), 
                  this, 
                  m_cPath_File_IDX.Get(),
                  GetLastError()
                 );
        l_bReturn = FALSE;
    }

    return l_bReturn;
}//Create_Storage


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Process_Packet
Bk::eResult CTrace_File::Process_Packet(sP7Ext_Header *i_pPacket)
{
    Bk::eResult l_eReturn = Bk::eOk;

    //data packet is most received packet, so we put it first
    if (EP7TRACE_TYPE_DATA == i_pPacket->dwSubType)
    {
        //if space in the buffer is not enough
        if ((m_dwData_Write_Used + i_pPacket->dwSize) >= TRACE_WRITE_DATA_BUFFER_LENGTH)
        {
            if (TRUE == Write_Buffer(m_cFile_Data, m_pData_Write, m_dwData_Write_Used))
            {
                m_qwData_Write_Offset += m_dwData_Write_Used;
                m_dwData_Write_Used = 0;
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Write buffer failed, free space is over ?"), this);
                m_bSuccess = FALSE;
                l_eReturn  = Bk::eErrorFileWrite;
            }
        }

        //if space in the buffer is not enough
        if (m_dwIDX_Write_Used >= TRACE_WRITE_INDEX_BUFFER_LENGTH)
        {
            if (TRUE == Write_Buffer(m_cFile_IDX, 
                                     (tUINT8*)m_pIDX_Write,
                                     m_dwIDX_Write_Used * sizeof(tUINT64)))
            {
                m_dwIDX_Write_Used  = 0;
                m_qwIDX_Write_Start = m_pHeader->qwTrace_Count;
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Write buffer failed, free space is over ?"), this);
                m_bSuccess = FALSE;
                l_eReturn  = Bk::eErrorFileWrite;
            }
        }


        if (m_bSuccess)
        {
            sP7Trace_Data *l_pTrace = (sP7Trace_Data*)i_pPacket; 

            if (!m_bIs_LittleEndian)
            {
                if (m_bIs_Extension)
                {
                    tUINT8 *l_pExt = ((tUINT8*)i_pPacket) + l_pTrace->sCommon.dwSize - 1;
                    tUINT8  l_bCount = l_pExt[0];
                    while (l_bCount--)
                    {
                        --l_pExt;
                        if (EP7TRACE_EXT_MODULE_ID == l_pExt[0])
                        {
                            l_pExt -= 2;
                            *(tUINT16*)l_pExt = ntohs(*(tUINT16*)l_pExt);
                            --l_pExt;
                        }
                    }
                }

                l_pTrace->wID        = ntohs(l_pTrace->wID);
                l_pTrace->dwThreadID = ntohl(l_pTrace->dwThreadID);
                l_pTrace->dwSequence = ntohl(l_pTrace->dwSequence);
                l_pTrace->qwTimer    = ntohqw(l_pTrace->qwTimer);

                sTrace_Desc *l_pDesc = (*m_pDesc)[l_pTrace->wID];
                tUINT8      *l_pIter = (tUINT8*)i_pPacket + sizeof(sP7Trace_Data);
                for (tUINT32 l_uI = 0; l_uI < l_pDesc->m_dwArgs_Len; l_uI++)
                {
                    tUINT8 l_bSize = l_pDesc->m_pArgs[l_uI].bSize;
                    if (4 == l_bSize)
                    {
                        *(tUINT32*)l_pIter = ntohl(*(tUINT32*)l_pIter);
                        l_pIter += 4;
                    }
                    else if (8 == l_bSize)
                    {
                        *(tUINT64*)l_pIter = ntohqw(*(tUINT64*)l_pIter);
                        l_pIter += 8;
                    }
                    else if (!l_bSize)
                    {
                        tUINT8 l_bType = l_pDesc->m_pArgs[l_uI].bType;
                        if (P7TRACE_ARG_TYPE_USTR16 == l_bType)
                        {
                            tXCHAR *l_pStr = (tXCHAR *)l_pIter;
                            while (*l_pStr)
                            {
                               *l_pStr = ntohs(*l_pStr);
                                l_pStr ++;
                            }

                            l_pIter = (tUINT8*)(++l_pStr);
                        }
                        else if (    (P7TRACE_ARG_TYPE_STRA == l_bType)
                                  || (P7TRACE_ARG_TYPE_USTR8 == l_bType)
                                )
                        {
                            while (0 != *l_pIter) l_pIter++;
                            l_pIter++;
                        }
                        else if (P7TRACE_ARG_TYPE_USTR32 == l_bType)
                        {
                            tUINT32 *l_pStr = (tUINT32 *)l_pIter;
                            while (*l_pStr)
                            {
                               *l_pStr = ntohl(*l_pStr);
                                l_pStr ++;
                            }

                            l_pIter = (tUINT8*)(++l_pStr);
                        }
                        else
                        {
                            LOG_ERROR(TM("[0x%08p] Unknown string type"), this);
                        }
                    }
                    else
                    {
                        LOG_ERROR(TM("[0x%08p] Unknown arg type"), this);
                    }
                }
            }//if (!m_bIs_LittleEndian)

            m_pHeader->pCounters[l_pTrace->bLevel]++;

            m_pIDX_Write[m_dwIDX_Write_Used] = m_qwData_Offset;
            m_dwIDX_Write_Used ++;

            memcpy(m_pData_Write + m_dwData_Write_Used, 
                   i_pPacket, 
                   i_pPacket->dwSize
                  );
            m_dwData_Write_Used += i_pPacket->dwSize;
            m_qwData_Offset     += i_pPacket->dwSize;
            m_pHeader->qwTrace_Count ++;
        }
    }
    else if (EP7TRACE_TYPE_DESC == i_pPacket->dwSubType)
    {
        sP7Trace_Format *l_pDesc = (sP7Trace_Format*)i_pPacket;

        if (!m_bIs_LittleEndian)
        {
            l_pDesc->wID       = ntohs(l_pDesc->wID);
            l_pDesc->wLine     = ntohs(l_pDesc->wLine);
            l_pDesc->wModuleID = ntohs(l_pDesc->wModuleID);
            l_pDesc->wArgs_Len = ntohs(l_pDesc->wArgs_Len);

            sP7Trace_Arg *l_pArgs = (sP7Trace_Arg*)((tUINT8*)i_pPacket + sizeof(sP7Trace_Format));
            ntohstr((tWCHAR*)(l_pArgs + l_pDesc->wArgs_Len));
        }

        //fill by empty elements if ID is larger than elements count
        while (m_pDesc->Count() <= l_pDesc->wID)
        {
            m_pDesc->Add_After(m_pDesc->Get_Last(), NULL);
        }

        if (NULL == (*m_pDesc)[l_pDesc->wID])
        {
            m_pDesc->Put_Data(m_pDesc->Get_ByIndex(l_pDesc->wID), 
                              new sTrace_Desc(l_pDesc), 
                              TRUE
                             );
        }

        if (m_pFiltered)
        {
            Bk::stDataChunk l_sChunk = { (tUINT8*)i_pPacket, i_pPacket->dwSize, NULL };
            m_pFiltered->Put_Packet(&l_sChunk);
        }
    }
    else if (EP7TRACE_TYPE_THREAD_START == i_pPacket->dwSubType)
    {
        sThreads              *l_pThreads = m_cThreads.Get_Data(m_cThreads.Get_Last());
        sP7Trace_Thread_Start *l_pThStart = (sP7Trace_Thread_Start*)i_pPacket;

        if (!m_bIs_LittleEndian)
        {
            l_pThStart->dwThreadID = ntohl(l_pThStart->dwThreadID);
            l_pThStart->qwTimer    = ntohqw(l_pThStart->qwTimer);
        }

        if (    (NULL == l_pThreads)
             || (l_pThreads->dwUsed >= l_pThreads->dwCount)
           )
        {
            l_pThreads = new sThreads(64);
            m_cThreads.Add_After(m_cThreads.Get_Last(), l_pThreads);
        }

        sThread *l_pThread = &l_pThreads->pThreads[l_pThreads->dwUsed];
        l_pThread->dwThreadID = l_pThStart->dwThreadID;
        l_pThread->qwStart    = l_pThStart->qwTimer;
        l_pThread->qwStop     = MAXUINT64;

        memcpy(l_pThread->pName, l_pThStart->pName, P7TRACE_THREAD_NAME_LENGTH);

        //if such item was not present in tree increase count of used threads
        if (Update_Threads_Tree(l_pThread)) 
        {
            l_pThreads->dwUsed++;
        }

        if (m_pFiltered)
        {
            Bk::stDataChunk l_sChunk = { (tUINT8*)i_pPacket, i_pPacket->dwSize, NULL };
            m_pFiltered->Put_Packet(&l_sChunk);
        }
    }
    else if (EP7TRACE_TYPE_THREAD_STOP == i_pPacket->dwSubType)
    {
        sP7Trace_Thread_Stop *l_pThStop = (sP7Trace_Thread_Stop*)i_pPacket;
        sRbThread            *l_pRbCurr = m_cThreads_Tree.Find(l_pThStop->dwThreadID);

        if (!m_bIs_LittleEndian)
        {
            l_pThStop->dwThreadID = ntohl(l_pThStop->dwThreadID);
            l_pThStop->qwTimer    = ntohqw(l_pThStop->qwTimer);
        }

        if (    (l_pRbCurr)
             && (l_pRbCurr->pThread)
           )
        {
            l_pRbCurr->pThread->qwStop = l_pThStop->qwTimer;
        }

        if (m_pFiltered)
        {
            Bk::stDataChunk l_sChunk = { (tUINT8*)i_pPacket, i_pPacket->dwSize, NULL };
            m_pFiltered->Put_Packet(&l_sChunk);
        }
    }
    else if (EP7TRACE_TYPE_MODULE == i_pPacket->dwSubType)
    {
        sP7Trace_Module *l_pModule = (sP7Trace_Module*)i_pPacket;

        if (!m_bIs_LittleEndian)
        {
            l_pModule->eVerbosity = (eP7Trace_Level)ntohl(l_pModule->eVerbosity);
            l_pModule->wModuleID  = ntohs(l_pModule->wModuleID);
        }

        //fill by empty elements if ID is larger than elements count
        while (m_cModules.Count() <= l_pModule->wModuleID)
        {
            m_cModules.Add_After(m_cModules.Get_Last(), NULL);
        }

        if (NULL == m_cModules[l_pModule->wModuleID])
        {
            m_cModules.Put_Data(m_cModules.Get_ByIndex(l_pModule->wModuleID), 
                                new sP7Trace_Module(*l_pModule), 
                                TRUE
                               );

            if (m_pExtra)
            {
                m_pExtra->Update(l_pModule);
            }
        }

        if (m_pFiltered)
        {
            Bk::stDataChunk l_sChunk = { (tUINT8*)i_pPacket, i_pPacket->dwSize, NULL };
            m_pFiltered->Put_Packet(&l_sChunk);
        }
    }
    else if (EP7TRACE_TYPE_VERB == i_pPacket->dwSubType)
    {
        sP7Trace_Verb *l_pVerbosity = (sP7Trace_Verb*)i_pPacket;

        if (!m_bIs_LittleEndian)
        {
            l_pVerbosity->eVerbosity = (eP7Trace_Level)ntohl(l_pVerbosity->eVerbosity);
            l_pVerbosity->wModuleID  = ntohs(l_pVerbosity->wModuleID); 
        }

        if (NULL != m_cModules[l_pVerbosity->wModuleID])
        {
            m_cModules[l_pVerbosity->wModuleID]->eVerbosity = l_pVerbosity->eVerbosity;

            if (m_pExtra)
            {
                m_pExtra->Update(m_cModules[l_pVerbosity->wModuleID]);
            }
        }
    }
    else if (EP7TRACE_TYPE_INFO == i_pPacket->dwSubType)
    {
        sP7Trace_Info *l_pInfo = (sP7Trace_Info *)i_pPacket;

        if (!m_bIs_LittleEndian)
        {
            l_pInfo->dwTime_Hi         = ntohl(l_pInfo->dwTime_Hi);
            l_pInfo->dwTime_Lo         = ntohl(l_pInfo->dwTime_Lo);
            l_pInfo->qwTimer_Value     = ntohqw(l_pInfo->qwTimer_Value);
            l_pInfo->qwTimer_Frequency = ntohqw(l_pInfo->qwTimer_Frequency);
            l_pInfo->qwFlags           = ntohqw(l_pInfo->qwFlags);
            ntohstr(l_pInfo->pName, l_pInfo->pName + P7TRACE_NAME_LENGTH);
        }

        if (m_bInitialized)
        {
            LOG_WARNING(TM("[0x%08p] Receive init data again !"), this);

            //if time is different it mean this (i_pPacket) belong to different 
            //file/stream
            if (    (l_pInfo->dwTime_Hi != m_pHeader->sStream_Time.dwHighDateTime)
                 || (l_pInfo->dwTime_Lo != m_pHeader->sStream_Time.dwLowDateTime)
               )
            {
                LOG_WARNING(TM("[0x%08p] Receive packet from different stream"), this);
                l_eReturn = Bk::eErrorMissmatch;
            }
        }
        else
        {
            m_pHeader->sStream_Time.dwHighDateTime = l_pInfo->dwTime_Hi;
            m_pHeader->sStream_Time.dwLowDateTime  = l_pInfo->dwTime_Lo;
            m_pHeader->qwTimer_Value               = l_pInfo->qwTimer_Value;
            m_pHeader->qwTimer_Frequency           = l_pInfo->qwTimer_Frequency;

            if (l_pInfo->qwFlags & P7TRACE_INFO_FLAG_UNSORTED)
            {
                m_pHeader->iFlags |= FILE_HEADER_FLAG_UNSORTED; 
            }

            if (l_pInfo->qwFlags & P7TRACE_INFO_FLAG_EXTENTION)
            {
                m_pHeader->iFlags |= FILE_HEADER_FLAG_TRACE_EXTENSION;
                m_bIs_Extension    = TRUE;
            }

            PWStrCpy(m_pHeader->pStream_Name, MAX_STREAM_NAME_LENGTH, l_pInfo->pName);

            m_sInfo.sStream_Time.dwHighDateTime = l_pInfo->dwTime_Hi;
            m_sInfo.sStream_Time.dwLowDateTime  = l_pInfo->dwTime_Lo;

        #if defined(UTF8_ENCODING)
            Convert_UTF16_To_UTF8(l_pInfo->pName, m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name));
        #else
            wcscpy_s(m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name), (wchar_t*)l_pInfo->pName); 
        #endif

            m_bIsInfo = TRUE;

            if (m_pExtra)
            {
                CWString l_cSession;

                l_cSession.Set(m_sInfo.pProcess_Name);
                l_cSession.Append(2, TM("-"), m_sInfo.pStream_Name);

                if (Bk::eOk != m_pExtra->Initialize(l_cSession.Get()))
                {
                    LOG_ERROR(TM("[0x%08p] Can't initialize extra object!"), this);
                }
            }

            if (!m_cFile_Data.IsOpened())
            {
                m_bSuccess = Create_Files();
                if (m_bSuccess)
                {
                    if (Write_Buffer(m_cFile_Data, (tUINT8*)m_pHeader, TRACE_HEADER_RESERVED_SIZE, FALSE))
                    {
                        m_bInitialized = TRUE;
                        //m_qwData_Offset += TRACE_HEADER_RESERVED_SIZE;
                    }
                    else
                    {
                        LOG_ERROR(TM("[0x%08p] Writing header failed"), this);
                        m_bSuccess = FALSE;
                        l_eReturn  = Bk::eErrorFileWrite;
                    }
                }
                else
                {
                    LOG_ERROR(TM("[0x%08p] Create_Files() failed"), this);
                }
            }//if (m_cFile_Data.IsOpened())
        }
    }
    else if (EP7TRACE_TYPE_CLOSE == i_pPacket->dwSubType)
    {
        l_eReturn = Bk::eErrorClosed;
    }

    return l_eReturn;
}//Process_Packet


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Update_Threads_Tree
tBOOL CTrace_File::Update_Threads_Tree(sThread *i_pThread)
{
    sRbThread *l_pRbCurr = m_cThreads_Tree.Find(i_pThread->dwThreadID);
    tBOOL      l_bReturn = TRUE;

    if (l_pRbCurr)
    {
        sRbThread *l_pRbPrev = NULL;
        while (l_pRbCurr)
        {
            if (i_pThread->qwStart > l_pRbCurr->pThread->qwStart)
            {
                sRbThread *l_pRbNew = NULL;
                if (l_pRbPrev) 
                {
                    l_pRbNew = new sRbThread(i_pThread, l_pRbCurr);
                    l_pRbPrev->pNext = l_pRbNew;
                }
                else //if it is first element in queue
                {
                    l_pRbNew  = new sRbThread(NULL, NULL);
                    //Move current (first) to new one
                    *l_pRbNew = *l_pRbCurr;
                    //Set current (first) with new data 
                    l_pRbCurr->pThread = i_pThread;
                    l_pRbCurr->pNext   = l_pRbNew;
                    //initialize pointer to new one
                    l_pRbNew = l_pRbCurr;
                }

                if (    (l_pRbNew->pNext)
                     && (MAXUINT64 == l_pRbNew->pNext->pThread->qwStop)
                   )
                {
                    l_pRbNew->pNext->pThread->qwStop = l_pRbNew->pThread->qwStart - 1;
                }

                break;
            }
            else if (i_pThread->qwStart == l_pRbCurr->pThread->qwStart)
            {
                //repeat !!!
                //l_pRbCurr->pThread->qwStop = i_pThread->qwStop;
                break;
            }
            else
            {
                l_pRbPrev = l_pRbCurr;
                l_pRbCurr = l_pRbCurr->pNext;
            }
        }
    }
    else //not found
    {
        m_cThreads_Tree.Push(new sRbThread(i_pThread, NULL), i_pThread->dwThreadID);
    }

    return l_bReturn;
}//Update_Threads_Tree


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Write_Buffer
tBOOL CTrace_File::Write_Buffer(CPFile  &i_rFile, 
                                tUINT8  *i_pBuffer,
                                size_t   i_dwSize, 
                                tBOOL    i_bAppend
                               )
{
    tBOOL l_bResult = TRUE;
   
    if (NULL == i_pBuffer)
    {
        return FALSE;
    }

    if (0 == i_dwSize)
    {
        return TRUE;
    }

    if (i_bAppend)
    {
        tUINT64 l_qwSize = i_rFile.Get_Size();
        if (l_qwSize)
        {
            l_bResult = i_rFile.Set_Position(l_qwSize);
        }
    }
    else
    {
        l_bResult = i_rFile.Set_Position(0ull);
    }

    if (!l_bResult)
    {
        LOG_ERROR(TM("[0x%08p] SetFilePointer fails"), this);
        return FALSE;
    }

    size_t l_szAA = i_rFile.Write(i_pBuffer, i_dwSize, FALSE);

    return (i_dwSize == l_szAA);
}//Write_Buffer


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Read_Buffer
tBOOL CTrace_File::Read_Buffer(CPFile  &i_rFile, 
                               tUINT64  i_qwOffset,
                               tUINT8  *i_pBuffer,
                               size_t   i_szBuffer, 
                               size_t  *o_pRead
                              )
{
    if (o_pRead)
    {
        *o_pRead = 0;
    }

    if (NULL == i_pBuffer)
    {
        return FALSE;
    }

    if (!i_rFile.Set_Position(i_qwOffset))
    {
        return FALSE;
    }

    *o_pRead = i_rFile.Read(i_pBuffer, (size_t)i_szBuffer);

    return (*o_pRead) ? TRUE : FALSE;
}//Read_Buffer



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//     sP7Trace_Data *l_pData  = (sP7Trace_Data*)Get_Raw_Trace(i_qwIndex);
//     //sequence number has only 32 bits, we should recalculate index to SeqN and
//     //take in account possible overflow
//     tUINT32          l_dwSeqN = (i_qwIndex + 1) % (MAXUINT32);
// 
//     if (NULL == l_pData)
//     {
//         l_eReturn = Bk::eErrorInternal;
//         goto l_lblExit;
//     }
// 
//     if  (l_dwSeqN != l_pData->dwSequence)
//     {
//         tUINT32        l_dwStep   = 8192;
//         tUINT64        l_qwOffset = 0;
//         BOOL           l_bExit    = FALSE;
//         sP7Trace_Data *l_pCompare = NULL;
//         tUINT64        l_qwStat   = 0;
//         tUINT64        l_qwEnd    = 0;
//         tUINT32        l_dwDiff   = MAXINT32;
// 
//         
//         ////////////////////////////////////////////////////////////////////////
//         //initialize cache: loading to the internal buffer from HDD 
//         if ( i_qwIndex >= (l_dwStep * 2) )
//         {
//             l_qwStat = i_qwIndex - (l_dwStep * 2);
//         }
//         else
//         {
//             l_qwStat = 0;
//         }
// 
//         Get_Raw_Trace(l_qwStat);
//         ////////////////////////////////////////////////////////////////////////
// 
// 
//         ////////////////////////////////////////////////////////////////////////
//         //start searching
//         while (FALSE == l_bExit)
//         {
//             //search forward
//             if ((i_qwIndex + l_qwOffset) < m_pHeader->qwTrace_Count)
//             {
//                 l_qwStat = i_qwIndex + l_qwOffset;
//                 l_qwEnd  = l_qwStat + l_dwStep;
//                 if (l_qwEnd > m_pHeader->qwTrace_Count)
//                 {
//                     l_qwEnd = m_pHeader->qwTrace_Count;
//                 }
// 
//                 for (; l_qwStat < l_qwEnd; l_qwStat ++)
//                 {
//                     l_pCompare = (sP7Trace_Data*)Get_Raw_Trace(l_qwStat);    
//                     if (l_pCompare)
//                     {
//                         if (l_pCompare->dwSequence == l_dwSeqN)
//                         {
//                             l_pData = l_pCompare;
//                             l_bExit = TRUE;
//                             break;
//                         }
//                         else if (    (l_pCompare->dwSequence > l_dwSeqN)
//                                   && ((l_pCompare->dwSequence - l_dwSeqN) <= l_dwDiff)
//                                 )
//                         {
//                             l_dwDiff = l_pCompare->dwSequence - l_dwSeqN;
//                             l_pData  = l_pCompare;
//                         }
//                     } //if (l_pCompare)
//                 }//for (; l_qwStat < l_qwEnd; l_qwStat ++)
//             }//if ((i_qwIndex + l_dwStep) > l_qwOffset)
// 
// 
//             //increment offset
//             l_qwOffset += l_dwStep;
// 
//             //Search backward
//             //we should be sure that even if we shift index by offset we still 
//             //have space to search
//             if (    (FALSE == l_bExit)
//                  && ((i_qwIndex + l_dwStep) > l_qwOffset)
//                )
//             {
//                 if (i_qwIndex >= l_qwOffset)
//                 {
//                     l_qwStat = i_qwIndex - l_qwOffset;
//                     l_qwEnd  = l_qwStat + l_dwStep;
//                 }
//                 else
//                 {
//                     l_qwStat = 0;
//                     l_qwEnd  = (i_qwIndex + l_dwStep) - l_qwOffset;
//                 }
// 
//                 for (; l_qwStat < l_qwEnd; l_qwStat ++)
//                 {
//                     l_pCompare = (sP7Trace_Data*)Get_Raw_Trace(l_qwStat);    
//                     if (l_pCompare)
//                     {
//                         if (l_pCompare->dwSequence == l_dwSeqN)
//                         {
//                             l_pData = l_pCompare;
//                             l_bExit = TRUE;
//                             break;
//                         }
//                         else if (    (l_pCompare->dwSequence > l_dwSeqN)
//                                   && ((l_pCompare->dwSequence - l_dwSeqN) <= l_dwDiff)
//                                 )
//                         {
//                             l_dwDiff = l_pCompare->dwSequence - l_dwSeqN;
//                             l_pData  = l_pCompare;
//                         }
//                     } //if (l_pCompare)
//                 }//for (; l_qwStat < l_qwEnd; l_qwStat ++)
//             }//if ((i_qwIndex + l_dwStep) > l_qwOffset)
// 
//             if (0x20000 <= l_qwOffset) //128k elements
//             {
//                 l_bExit = TRUE;
//             }
//         }
//     }
// 
//     l_eReturn = Format_Data((tUINT8*)l_pData, o_pItem); 
#pragma endregion STORAGE
