////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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)
    #pragma warning(disable : 4091)
    #include "Shlobj.h"
#endif

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

    #define MAXINT64    ((tINT64)(MAXUINT64 >> 1))
    #define MININT64    ((tINT64)~MAXINT64)
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma region TOOLS

#define WRITE_DATA_BUFFER_LENGTH                                          (3855)//sizeof(Bk::stTelemetrySample) * X = 64 kb
#define READ_DATA_BUFFER_LENGTH                                         (0XFFFF)//sizeof(Bk::stTelemetrySample) * X ~ 1 mb

#define WRITE_INDEX_BUFFER_LENGTH                                        (16384)//sizeof(sIndex_Item) * X = 128 kb
#define READ_INDEX_BUFFER_LENGTH                                         (32768)//sizeof(sIndex_Item) * X = 256 kb
#define SEC_IN_100NS                                                 10000000.0


static const GUID g_sPlugin_GUID = { 0x9f770104, 0x81f, 0x48cd, { 0x98, 0xf7, 0x5a, 0x10, 0x6, 0xf1, 0xad, 0x60 } };

#define SIZEOF64(x)                                          ((tUINT64)sizeof(x))



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

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tDOUBLE ntohdb(tDOUBLE i_dbX)
{
    tDOUBLE l_dbReturn; //not initialized on purpose
    tUINT8 *l_pSrc = (tUINT8*)(&i_dbX);
    tUINT8 *l_pDst = (tUINT8*)(&l_dbReturn);

    l_pDst[0] = l_pSrc[7];
    l_pDst[1] = l_pSrc[6];
    l_pDst[2] = l_pSrc[5];
    l_pDst[3] = l_pSrc[4];
    l_pDst[4] = l_pSrc[3];
    l_pDst[5] = l_pSrc[2];
    l_pDst[6] = l_pSrc[1];
    l_pDst[7] = l_pSrc[0];

    return l_dbReturn;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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 endregion TOOLS
           

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#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("Telemetry storage v2 (*") BK_STORAGE_TELEMETRY_FILE_EXT_V2 TM(")");

    PStrCpy(o_pInfo->pName, LENGTH(o_pInfo->pName), TM("Storage: P7 Telemetry v2"));

    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_TELEMETRY_V2;

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

    CTel_File *l_pStorage = new CTel_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;
    const tXCHAR *l_pExtension = NULL;
    CTel_File  *l_pStorage   = 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_TELEMETRY_FILE_EXT_V2))
       )
    {
        return Bk::eErrorWrongInput;
    }

    l_pStorage = new CTel_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 COUNTER


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CCounter::CCounter(const sP7Tel_Counter_v2 *i_pCounter, tBOOL i_bLittleEndian)
    : pHeader(NULL)
    , pName(NULL)
{
    size_t l_szNameLen = i_pCounter->sCommon.dwSize - (sizeof(sP7Tel_Counter_v2) - sizeof(sP7Tel_Counter_v2::pName));
    size_t l_szHeader  = l_szNameLen + sizeof(sCounter);
    pHeader = (sCounter*)malloc(l_szHeader);

    memset(pHeader, 0, l_szHeader);
    
    pHeader->bInitialized = TRUE;
    pHeader->dbCumulative = 0.0;
    pHeader->dbMax_Real   = -DBL_MAX;
    pHeader->dbMin_Real   = DBL_MAX;
    pHeader->qwSamples    = 0;
    pHeader->uSize        = (tUINT32)l_szHeader;

    l_szNameLen = l_szNameLen / sizeof(tWCHAR);
    
    if (i_bLittleEndian)
    {
        pHeader->uId          = i_pCounter->wID;
        pHeader->bOn          = i_pCounter->bOn;
        pHeader->dbMax        = i_pCounter->dbMax;
        pHeader->dbMax_Alarm  = i_pCounter->dbAlarmMax;
        pHeader->dbMin        = i_pCounter->dbMin;
        pHeader->dbMin_Alarm  = i_pCounter->dbAlarmMin;
        PWStrCpy(pHeader->pName, l_szNameLen, i_pCounter->pName);
    }
    else
    {
        pHeader->uId          = ntohs(i_pCounter->wID);
        pHeader->bOn          = ntohs(i_pCounter->bOn);
        pHeader->dbMax        = ntohdb(i_pCounter->dbMax);
        pHeader->dbMax_Alarm  = ntohdb(i_pCounter->dbAlarmMax);
        pHeader->dbMin        = ntohdb(i_pCounter->dbMin);
        pHeader->dbMin_Alarm  = ntohdb(i_pCounter->dbAlarmMin);
    
        memcpy(pHeader->pName, i_pCounter->pName, l_szNameLen * sizeof(tWCHAR));
        ntohstr(pHeader->pName, pHeader->pName + l_szNameLen);
    }
    
    l_szNameLen *= 4; //worst case
    
    pName = (tXCHAR*)malloc(l_szNameLen*sizeof(tXCHAR));
    
    #if defined(UTF8_ENCODING)
        Convert_UTF16_To_UTF8(pHeader->pName, pName, l_szNameLen);
    #else
        PStrCpy(pName, l_szNameLen, pHeader->pName);
    #endif              
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CCounter::CCounter(sCounter *i_pHeader)
    : pHeader(NULL)
    , pName(NULL)
{
    size_t l_szNameLen = i_pHeader->uSize - (sizeof(sCounter) - sizeof(sCounter::pName));
    size_t l_szHeader  = i_pHeader->uSize;
    pHeader = (sCounter*)malloc(l_szHeader);
    memcpy(pHeader, i_pHeader, l_szHeader);

    l_szNameLen *= 4; //worst case

    pName = (tXCHAR*)malloc(l_szNameLen*sizeof(tXCHAR));

    #if defined(UTF8_ENCODING)
        Convert_UTF16_To_UTF8(pHeader->pName, pName, l_szNameLen);
    #else
        PStrCpy(pName, l_szNameLen, pHeader->pName);
    #endif              
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CCounter::~CCounter()
{
    if (pHeader)
    {
        free(pHeader);
        pHeader = NULL;
    }

    if (pName)
    {
        free(pName);
        pName = NULL;
    }
}
#pragma endregion COUNTER


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma region READER

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CReader
CReader::CReader(CTel_File *i_pParent, tBOOL &o_rError)
    : m_lReference(1)
    , m_pParent(i_pParent)
    , m_pData_Read(0)
    , m_dwData_Read_Count(0)
    , m_qwData_Read_First_Index(MAXUINT64)
    , m_pIDX_Read(0)
    , m_dwIDX_Read_Count(0)
    , m_qwIDX_Read_First_Index(MAXUINT64)
{
    o_rError = FALSE;

    LOCK_CREATE(m_hCsIdList);

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

    m_pData_Read              = new Bk::stTelemetrySample[READ_DATA_BUFFER_LENGTH];
    m_dwData_Read_Count       = 0;
    m_qwData_Read_First_Index = MAXUINT64;

    m_pIDX_Read               = new sIndex_Item[READ_INDEX_BUFFER_LENGTH];
    m_dwIDX_Read_Count        = 0;
    m_qwIDX_Read_First_Index  = MAXUINT64;

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

    //at the end, when object is constructed
    if (    (m_pParent)
         && (!o_rError)
       )
    {
        m_pParent->Add_Reader(this);
    }
}//CReader


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~CReader
CReader::~CReader()
{
    m_dwData_Read_Count       = 0;
    m_qwData_Read_First_Index = MAXUINT64;

    m_dwIDX_Read_Count        = 0;
    m_qwIDX_Read_First_Index  = MAXUINT64;


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

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

    if (m_pParent)
    {
        m_pParent->Del_Reader(this);
        m_pParent->Release();
    }

    m_cIdList.Clear(TRUE);

    LOCK_DESTROY(m_hCsIdList);
}//~CReader


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Info
Bk::eResult CReader::Get_Info(Bk::stTelemetryInfo *o_pInfo)
{
    return m_pParent->Get_Info(o_pInfo);
}//Get_Info


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counter
Bk::eResult CReader::Get_Counter(tUINT16 i_wIndex, Bk::stTelemetryCounter *o_pCounter, Bk::eTelemetryCounterValue i_eDetails)
{
    return m_pParent->Get_Counter(i_wIndex, o_pCounter, i_eDetails);
}//Get_Counter


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Nearest_Index
Bk::eResult CReader::Get_Nearest_Index(tUINT64  i_qwTime, tUINT64 *o_pIndex)
{
    return m_pParent->Get_Nearest_Index(this, i_qwTime, o_pIndex);
}//Get_Nearest_Index


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Samples
Bk::eResult CReader::Get_Samples(tUINT64 i_qwIndex, Bk::stTelemetrySample *o_pSamples, tUINT32 i_dwCount, tUINT32 *o_dwCount)
{
    return m_pParent->Get_Samples(this, i_qwIndex, o_pSamples, i_dwCount, o_dwCount);
}//Get_Samples

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Pull_New_Counter
Bk::eResult CReader::Pull_New_Counter(tUINT16 &o_rId)
{
    Bk::eResult l_eResut = Bk::eErrorTimeout;

    LOCK_ENTER(m_hCsIdList);
    if (m_cIdList.Count())
    {
        pAList_Cell l_pEl = m_cIdList.Get_First();
        o_rId = m_cIdList.Get_Data(l_pEl);
        m_cIdList.Del(l_pEl, FALSE);
        l_eResut = Bk::eOk;
    }
    LOCK_EXIT(m_hCsIdList);

    return l_eResut;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_New_Counters_Count
tUINT32 CReader::Get_New_Counters_Count()
{
    tUINT32 l_uResut = 0;

    LOCK_ENTER(m_hCsIdList);
    l_uResut = m_cIdList.Count();
    LOCK_EXIT(m_hCsIdList);

    return l_uResut;
}//Get_New_Counters_Count


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Push_New_Counter
void CReader::Push_New_Counter(tUINT16 i_uId)
{
    LOCK_ENTER(m_hCsIdList);
    m_cIdList.Add_After(m_cIdList.Get_Last(), i_uId);
    LOCK_EXIT(m_hCsIdList);
}

#pragma endregion READER




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma region STORAGE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CTel_File
CTel_File::CTel_File(Bk::IBNode *i_pNode, CProperty *i_pProp)
    : m_lReference(1)
    , m_pCounters(NULL)
    , m_szCounters(4096)
    , m_szActiveCounters(0)

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

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

    , m_pData_Write(NULL)
    , m_dwData_Write_Count(0)
    , m_qwData_Write_First_Index(0ULL)

    , m_pIDX_Write(NULL)
    , m_dwIDX_Write_Count(0)
    , m_qwIDX_Write_First_Index(0ULL)

    , m_pNode(i_pNode)
    , m_pProp(i_pProp)
    , m_bSuccess(TRUE)
    , m_bInitialized(FALSE)
    , m_bIs_LittleEndian(TRUE)
    , m_qwQuantum_Duration(MAXUINT64)
    , m_pExtra(NULL)
{
    CProperty *l_pPropPath = NULL;
    CWString   l_cPath;

    memset(&m_sInfo, 0, sizeof(m_sInfo));
    memset(&m_sData_Header, 0, sizeof(m_sData_Header));
    memset(&m_sIDX_Header, 0, sizeof(m_sIDX_Header));
    
    m_iP7_Trace = P7_Get_Shared_Trace(BK_SHARED_TRACE);
    m_iP7_Tel   = P7_Get_Shared_Telemetry(BK_SHARED_TELEMETRY);

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

    m_pCounters = (CCounter**)malloc(m_szCounters * sizeof(CCounter*));
    memset(m_pCounters, 0, m_szCounters * sizeof(CCounter*));

    LOCK_CREATE(m_hCS);

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

    //loading settings
    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;
    }
}//CTel_File


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~CTel_File
CTel_File::~CTel_File()
{
    Uninit_Writer();

    m_cReaders.Clear(FALSE);
    m_hFile_Data.Close(TRUE);
    m_hFile_IDX.Close(TRUE);

    if (    (m_bDelete)
         && (m_cPath_Directory.Get())
       )
    {
        tXCHAR  *l_pDir  = NULL;
        tXCHAR  *l_pDivA = NULL;
        tXCHAR  *l_pDivB = NULL;
        CWString  l_cDirCopy;
        
        CFSYS::Delete_File(m_cPath_File_Data.Get());
        CFSYS::Delete_File(m_cPath_File_IDX.Get());
        
        l_cDirCopy.Set(m_cPath_Directory.Get());
        l_pDir  = l_cDirCopy.Get();
        l_pDivA = l_pDir + PStrLen(l_pDir) - 1;
        l_pDivB = l_pDivA;

        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_pCounters)
    {
        for (size_t l_szI = 0; l_szI < m_szCounters; l_szI++)
        {
            if (m_pCounters[l_szI])
            {
                delete m_pCounters[l_szI];
                m_pCounters[l_szI] = NULL;
            }
        }

        free(m_pCounters);
        m_pCounters = NULL;
    }

    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);
}//~CTel_File


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Init_Reader
Bk::eResult CTel_File::Init_Reader(const tXCHAR *i_pFile_Name)
{
    Bk::eResult l_eReturn = Bk::eOk;

    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)
    {
        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_cPath_File_Data.Set(i_pFile_Name);
        if (!m_hFile_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_hFile_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_hFile_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 data header
    if (Bk::eOk == l_eReturn)
    {
        size_t l_dwRead = 0;

        if (Read_Buffer(m_hFile_Data, 0, (tUINT8*)&m_sData_Header, sizeof(m_sData_Header), &l_dwRead))
        {
            if (sizeof(m_sData_Header) > l_dwRead)
            {
                LOG_ERROR(TM("[0x%08p] Error header reading - size missmatch"), this);
                l_eReturn = Bk::eErrorInternal;
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Error header reading"), this);
            l_eReturn = Bk::eErrorInternal;
        }
    }

    //read index header
    if (Bk::eOk == l_eReturn)
    {
        size_t l_dwRead = 0;

        if (Read_Buffer(m_hFile_IDX, 0, (tUINT8*)&m_sIDX_Header, sizeof(m_sIDX_Header), &l_dwRead))
        {
            if (sizeof(m_sIDX_Header) > l_dwRead)
            {
                LOG_ERROR(TM("[0x%08p] Error header reading - size missmatch"), this);
                l_eReturn = Bk::eErrorInternal;
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Error header reading"), this);
            l_eReturn = Bk::eErrorInternal;
        }
    }

    //check header version and flags
    if (Bk::eOk == l_eReturn)
    {
        if (    (FILE_HEADER_MARKER != m_sData_Header.qwMarker)
             || (FILE_HEADER_VERSION != m_sData_Header.wHeader_Version)
           )
        {
            LOG_ERROR(TM("[0x%08p] File is not supported (marker (%I64d)/version (%d) are incorrect)"), 
                      this,
                      m_sData_Header.qwMarker,
                      (tUINT32)m_sData_Header.wHeader_Version
                     );
            l_eReturn = Bk::eErrorInternal;
        }
    }

    //reading counters
    if (Bk::eOk == l_eReturn)
    {
        tUINT64 l_qwOffset = SIZEOF64(m_sData_Header) + m_sData_Header.qwSamples_Count * SIZEOF64(Bk::stTelemetrySample);
        tUINT64 l_qwSize   = m_hFile_Data.Get_Size() - l_qwOffset;

        if (    (l_qwSize)
             && (m_hFile_Data.Get_Size() > l_qwOffset)
           )
        {
            tUINT8 *l_pData = (tUINT8*)malloc((size_t)l_qwSize);
            if (l_pData)
            {
                size_t l_szRead = 0;
                if (    (Read_Buffer(m_hFile_Data, l_qwOffset, l_pData, (size_t)l_qwSize, &l_szRead))
                     && (l_szRead == (size_t)l_qwSize)
                   )
                {
                    tUINT8 *l_pDataIt  = l_pData;
                    tUINT8 *l_pDataEnd = l_pData + (size_t)l_qwSize;
                    while (l_pDataIt < l_pDataEnd)
                    {
                        sCounter *l_pCounter = (sCounter *)l_pDataIt;

                        if (Realloc_Counters(l_pCounter->uId))
                        {
                            m_pCounters[l_pCounter->uId] = new CCounter(l_pCounter);
                            if (l_pCounter->uId > m_szActiveCounters)
                            {
                                m_szActiveCounters = l_pCounter->uId + 1;
                            }
                        }
                        else
                        {
                            LOG_ERROR(TM("[0x%08p] Can't read counter [%d]"), this, l_pCounter->uId);
                        }

                        l_pDataIt += l_pCounter->uSize;
                    }
                }
                else
                {
                    LOG_ERROR(TM("[0x%08p] Can't read counters data [%lld]"), this, l_qwSize);
                    l_eReturn = Bk::eErrorInternal;
                }

                free(l_pData);
                l_pData = NULL;
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Memory allocation failure [%lld]"), this, l_qwSize);
                l_eReturn = Bk::eErrorInternal;
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] File has no counters description"), this);
            l_eReturn = Bk::eErrorInternal;
        }
    }

    if (Bk::eOk == l_eReturn)
    {
        ////////////////////////////////////////////////////////////////////////
        //Fill info
        GUID l_sGUID = BK_READER_GUID_TELEMETRY;
        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_sData_Header.pNode_Name, m_sInfo.pNode, LENGTH(m_sInfo.pNode));
        Convert_UTF16_To_UTF8(m_sData_Header.pProcess_Name, m_sInfo.pProcess_Name, LENGTH(m_sInfo.pProcess_Name));
        Convert_UTF16_To_UTF8(m_sData_Header.pStream_Name, m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name));
    #else
        wcscpy_s(m_sInfo.pNode, LENGTH(m_sInfo.pNode), m_sData_Header.pNode_Name);
        wcscpy_s(m_sInfo.pProcess_Name, LENGTH(m_sInfo.pProcess_Name), m_sData_Header.pProcess_Name);
        wcscpy_s(m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name), m_sData_Header.pStream_Name); 
    #endif

        m_sInfo.dwProcess_ID  = m_sData_Header.dwProcess_ID;
        m_sInfo.sProcess_Time = m_sData_Header.sProcess_Time;
        m_sInfo.sStream_Time  = m_sData_Header.sStream_Time; 

        m_bIsInfo = TRUE;
    }

    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Init_Reader


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Info
Bk::eResult CTel_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 CTel_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 CTel_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_Count + m_dwIDX_Write_Count;
    *o_pSize += m_hFile_Data.Get_Size();
    *o_pSize += m_hFile_IDX.Get_Size();
    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Get_Size


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

    LOCK_ENTER(m_hCS); 
    PSPrint(io_rDesc.pText, 
            io_rDesc.szTextMax,
            TM("Counters: %d (S:%lld)"),
            (tUINT32)m_szActiveCounters,
            (tUINT64)m_sData_Header.qwSamples_Count
           );
    LOCK_EXIT(m_hCS); 

    io_rDesc.eLevel = Bk::eStorageDescLevelNormal;

    return Bk::eOk;
}//Get_Description


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Init_Writer
Bk::eResult CTel_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 Bk::stTelemetrySample[WRITE_DATA_BUFFER_LENGTH];
        m_pIDX_Write  = new sIndex_Item[WRITE_INDEX_BUFFER_LENGTH];

        m_qwData_Write_First_Index = 0ULL;
        m_qwIDX_Write_First_Index  = 0ULL;

        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_sData_Header, 0, sizeof(m_sData_Header));
        m_sData_Header.qwMarker          = FILE_HEADER_MARKER;
        m_sData_Header.wHeader_Version   = FILE_HEADER_VERSION;
        m_sData_Header.wProtocol_Version = i_pInfo->wProtocol_Version;
        m_sData_Header.dwProcess_ID      = i_pInfo->dwProcess_ID;
        m_sData_Header.sProcess_Time     = i_pInfo->sProcess_Time;

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

        ////////////////////////////////////////////////////////////////////////
        //Fill info
        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;

        m_bIs_LittleEndian = !i_pInfo->bIs_BigEndian;

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

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


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Uninit_Writer
Bk::eResult CTel_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;
    }

    if (m_hFile_Data.IsOpened())
    {
        if (m_bSuccess)
        {
            m_bSuccess = Write_Buffer(m_hFile_Data, 
                                      (tUINT8*)m_pData_Write, 
                                      m_dwData_Write_Count * sizeof(Bk::stTelemetrySample)
                                     );
        }

        m_dwData_Write_Count       = 0;
        m_qwData_Write_First_Index = 0;


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

        //write counters
        for (size_t l_szI = 0; l_szI < m_szCounters && m_bSuccess; l_szI++)
        {
            if (m_pCounters[l_szI])
            {
                m_bSuccess = Write_Buffer(m_hFile_Data, 
                                          (tUINT8*)m_pCounters[l_szI]->pHeader, 
                                          (size_t)m_pCounters[l_szI]->pHeader->uSize, 
                                          TRUE
                                          );
            }
        }

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

        if (!m_hFile_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 (NULL != m_hFile_Data)


    if (    (m_hFile_IDX.IsOpened())
         && (m_bSuccess)
       )
    {
        tUINT64 l_qwIDX_Count = m_sData_Header.qwDuration / m_qwQuantum_Duration;

        if (    (m_sData_Header.qwSamples_Count)
             && (!l_qwIDX_Count)
             && (!m_sIDX_Header.qwCount)
           )
        {
            l_qwIDX_Count ++;
        }

        if (l_qwIDX_Count > m_sIDX_Header.qwCount)
        {
            Add_Indexes(l_qwIDX_Count - m_sIDX_Header.qwCount);
        }

        if (m_bSuccess)
        {
            m_bSuccess = Write_Buffer(m_hFile_IDX, 
                                      (tUINT8*)m_pIDX_Write, 
                                      m_dwIDX_Write_Count * sizeof(sIndex_Item)
                                     );
        }

        m_dwIDX_Write_Count       = 0;
        m_qwIDX_Write_First_Index = 0ULL;


        //rewrite header with updated information
        if (m_bSuccess)
        {
            m_bSuccess = Write_Buffer(m_hFile_IDX, 
                                      (tUINT8*)&m_sIDX_Header, 
                                      sizeof(m_sIDX_Header), 
                                      FALSE
                                     );
        }

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

        if (!m_hFile_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;
    }

    //LOG_INFO(TM("[0x%08p] First IDX = %I64d, Count = %I64d, Duration (count 10ms chunks) = %I64d, Difference = %I64d"), 
    //             this, 
    //             m_sIDX_Header.qwFirst_IDX,
    //             m_sIDX_Header.qwCount,
    //             m_sData_Header.qwDuration * 100ULL /  m_sData_Header.qwTimer_Frequency,
    //             (m_sData_Header.qwDuration * 100ULL /  m_sData_Header.qwTimer_Frequency) - m_sIDX_Header.qwCount
    //            );

    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 CTel_File::Put_Packet(Bk::stDataChunk *i_pChunk)
{
    Bk::eResult          l_eReturn = Bk::eOk;
    const 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_TELEMETRY_V2 == 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 CTel_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::eInterfaceStreamExTelemetry, (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 CTel_File::Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown)
{
    Bk::eResult l_eReturn = Bk::eErrorNotSupported;

    o_rUnknown = NULL;

    if (Bk::eInterfaceTelemetryReader == i_eId)
    {
        tBOOL    l_bError  = FALSE;
        CReader *l_pReader = new CReader(this, l_bError);
        if (!l_bError)
        {
            o_rUnknown = dynamic_cast<Bk::ITelemetryReader*>(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 CTel_File::Delete()
{
    LOCK_ENTER(m_hCS); 
    m_bDelete = TRUE;
    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Delete


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

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

    LOCK_ENTER(m_hCS); 

    o_pInfo->wCounters  = (tUINT16)m_szActiveCounters;
    if (m_sData_Header.qwTimer_Frequency)
    {
        o_pInfo->qwDuration = (tUINT64)((tDOUBLE)m_sData_Header.qwDuration * SEC_IN_100NS /  (tDOUBLE)m_sData_Header.qwTimer_Frequency);
    }
    else
    {
       o_pInfo->qwDuration = 0;
    }
    o_pInfo->qwSamples  = m_sData_Header.qwSamples_Count;

    LOCK_EXIT(m_hCS); 

    return Bk::eOk;
}//Get_Info


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Counter
Bk::eResult CTel_File::Get_Counter(tUINT16                    i_wIndex,
                                   Bk::stTelemetryCounter    *o_pCounter,
                                   Bk::eTelemetryCounterValue i_eDetails
                                )        
{
    if (FALSE == m_bSuccess)
    {
        return Bk::eErrorInternal;
    }

    if (    (NULL == o_pCounter)
         || (P7TELEMETRY_COUNTERS_MAX_COUNT_V2 <= i_wIndex)
       )
    {
        return Bk::eErrorWrongInput;
    }


    Bk::eResult l_eResult = Bk::eErrorWrongInput;
    LOCK_ENTER(m_hCS); 
    
    if (    (i_wIndex < m_szCounters)
         && (m_pCounters[i_wIndex])
       )
    {
        if (Bk::eTelemetryStaticValue & i_eDetails)
        {
            o_pCounter->bInitialized = m_pCounters[i_wIndex]->pHeader->bInitialized;
            o_pCounter->dbAlarmMax   = m_pCounters[i_wIndex]->pHeader->dbMax_Alarm;
            o_pCounter->dbAlarmMin   = m_pCounters[i_wIndex]->pHeader->dbMin_Alarm;
            o_pCounter->dbMax        = m_pCounters[i_wIndex]->pHeader->dbMax;
            o_pCounter->dbMin        = m_pCounters[i_wIndex]->pHeader->dbMin;

            o_pCounter->pName = m_pCounters[i_wIndex]->pName;
        }
    
        if (Bk::eTelemetryDynamicValue & i_eDetails)
        {
            o_pCounter->bOn = m_pCounters[i_wIndex]->pHeader->bOn;

            if (m_pExtra)
            {
                CProperty *l_pRoot = NULL;
                m_pExtra->Get_Root(l_pRoot);
                if (l_pRoot)
                {
                    CProperty *l_pProp = NULL;
                    l_pRoot->Get_SubItem(m_pCounters[i_wIndex]->pName, l_pProp);
                    if (l_pProp)
                    {
                        tINT64 l_iVal = 0;
                        l_pProp->Get_Value(l_iVal);
                        o_pCounter->bOn = (tUINT8)l_iVal;
                        l_pProp->Release();
                        l_pProp = NULL;
                    }

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

            o_pCounter->qwSamples  = m_pCounters[i_wIndex]->pHeader->qwSamples;
            o_pCounter->dbMin_Real = m_pCounters[i_wIndex]->pHeader->dbMin_Real;
            o_pCounter->dbMax_Real = m_pCounters[i_wIndex]->pHeader->dbMax_Real;
            if (m_pCounters[i_wIndex]->pHeader->qwSamples)
            {
                o_pCounter->dbAvg_Real = (m_pCounters[i_wIndex]->pHeader->dbCumulative
                                          / (double)m_pCounters[i_wIndex]->pHeader->qwSamples
                                         );
            }
            else
            {
                o_pCounter->dbAvg_Real = DBL_MAX;
            }
        }

        l_eResult = Bk::eOk;
    }

    LOCK_EXIT(m_hCS); 

    return l_eResult;

} //Get_Counter


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Nearest_Index
Bk::eResult CTel_File::Get_Nearest_Index(CReader *i_pReader, tUINT64 i_qwTime, tUINT64 *o_pIndex)
{
    Bk::eResult l_eReturn = Bk::eOk;
    tUINT64     l_qwIndex;

    LOCK_ENTER(m_hCS); 

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

    l_qwIndex = i_qwTime / 100000; //calculate time index

    if (    (l_qwIndex >= m_sIDX_Header.qwCount)
         || (NULL == o_pIndex)
       )
    {
        l_eReturn = Bk::eErrorWrongInput;
        goto l_lblExit;
    }

    if (l_qwIndex < m_sIDX_Header.qwFirst_IDX)
    {
        *o_pIndex = 0ULL;
        goto l_lblExit;
    }

    //shift index for value of missed indexes
    l_qwIndex -= m_sIDX_Header.qwFirst_IDX;

    //if sample exist in write buffer
    if (    (l_qwIndex >= m_qwIDX_Write_First_Index)
         && (l_qwIndex < (m_qwIDX_Write_First_Index + (tUINT64)m_dwIDX_Write_Count))
       )
    {
        *o_pIndex = m_pIDX_Write[l_qwIndex - m_qwIDX_Write_First_Index].qwSample_IDX;
    }
    else 
    {               
        //if index is out of read buffer - we need to read from file
        if (    (l_qwIndex < i_pReader->m_qwIDX_Read_First_Index)
             || (l_qwIndex >= (i_pReader->m_qwIDX_Read_First_Index + (tUINT64)i_pReader->m_dwIDX_Read_Count))
           )
        {
            Read_Buffer(m_hFile_IDX, 
                        SIZEOF64(sIndex_Header) + (l_qwIndex * SIZEOF64(sIndex_Item)),
                        (tUINT8 *)i_pReader->m_pIDX_Read,
                        READ_INDEX_BUFFER_LENGTH * sizeof(sIndex_Item), 
                        &i_pReader->m_dwIDX_Read_Count
                       );

            i_pReader->m_dwIDX_Read_Count /= sizeof(sIndex_Item);

            if (i_pReader->m_dwIDX_Read_Count) //more or equal than one element
            {
                i_pReader->m_qwIDX_Read_First_Index = l_qwIndex;
            }
        }

        if (    (l_qwIndex >= i_pReader->m_qwIDX_Read_First_Index)
             && (l_qwIndex < (i_pReader->m_qwIDX_Read_First_Index + (tUINT64)i_pReader->m_dwIDX_Read_Count))
           )
        {
            *o_pIndex = i_pReader->m_pIDX_Read[l_qwIndex - i_pReader->m_qwIDX_Read_First_Index].qwSample_IDX;
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] funcion fail to read index %I64d"), 
                      this,
                      l_qwIndex
                     );
        }
    }

l_lblExit:
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Get_Nearest_Index

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Get_Samples
Bk::eResult CTel_File::Get_Samples(CReader *i_pReader, tUINT64 i_qwIndex, Bk::stTelemetrySample *o_pSamples, tUINT32 i_dwCount, tUINT32 *o_dwCount)
{
    Bk::eResult l_eReturn      = Bk::eOk;
    tUINT64      l_qwBuffer_IDX = 0;

    LOCK_ENTER(m_hCS); 

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

    if (    (NULL == o_pSamples)
         || (NULL == o_dwCount)
         || (0    >= i_dwCount)
         || (i_qwIndex >= m_sData_Header.qwSamples_Count)
       )
    {
        l_eReturn = Bk::eErrorWrongInput;
        goto l_lblExit;
    }

    *o_dwCount = 0;

    while ((*o_dwCount) < i_dwCount)
    {
        //if sample exist in write buffer
        if (    (i_qwIndex >= m_qwData_Write_First_Index)
             && (i_qwIndex < (m_qwData_Write_First_Index + (tUINT64)m_dwData_Write_Count))
           )
        {
            l_qwBuffer_IDX = i_qwIndex - m_qwData_Write_First_Index;

            o_pSamples[*o_dwCount].wID     = m_pData_Write[l_qwBuffer_IDX].wID;
            o_pSamples[*o_dwCount].dbValue = m_pData_Write[l_qwBuffer_IDX].dbValue;
            o_pSamples[*o_dwCount].qwTime  = (tUINT64)((tDOUBLE)(   m_pData_Write[l_qwBuffer_IDX].qwTime
                                                                  - m_sData_Header.qwTimer_Value
                                                                ) * SEC_IN_100NS 
                                                               / (tDOUBLE)m_sData_Header.qwTimer_Frequency
                                                      );
        }
        else 
        {
            //if index is out of read buffer - we need to read from file
            if (    (i_qwIndex < i_pReader->m_qwData_Read_First_Index)
                 || (i_qwIndex >= (i_pReader->m_qwData_Read_First_Index + (tUINT64)i_pReader->m_dwData_Read_Count))
               )
            {
                Read_Buffer(m_hFile_Data, 
                            SIZEOF64(sFile_Header) + (i_qwIndex * SIZEOF64(Bk::stTelemetrySample)),
                            (tUINT8 *)i_pReader->m_pData_Read,
                            READ_DATA_BUFFER_LENGTH * sizeof(Bk::stTelemetrySample), 
                            &i_pReader->m_dwData_Read_Count
                           );

                i_pReader->m_dwData_Read_Count /= sizeof(Bk::stTelemetrySample);

                if (i_pReader->m_dwData_Read_Count) //more or equal than one element
                {
                    i_pReader->m_qwData_Read_First_Index = i_qwIndex;
                }
            }

            if (    (i_qwIndex >= i_pReader->m_qwData_Read_First_Index)
                 && (i_qwIndex < (i_pReader->m_qwData_Read_First_Index + (tUINT64)i_pReader->m_dwData_Read_Count))
               )
            {
                l_qwBuffer_IDX = i_qwIndex - i_pReader->m_qwData_Read_First_Index;

                o_pSamples[*o_dwCount].wID     = i_pReader->m_pData_Read[l_qwBuffer_IDX].wID;
                o_pSamples[*o_dwCount].dbValue = i_pReader->m_pData_Read[l_qwBuffer_IDX].dbValue;
                o_pSamples[*o_dwCount].qwTime  = (tUINT64)((tDOUBLE)(   i_pReader->m_pData_Read[l_qwBuffer_IDX].qwTime 
                                                                      - m_sData_Header.qwTimer_Value
                                                                    ) * SEC_IN_100NS 
                                                                    / (tDOUBLE)m_sData_Header.qwTimer_Frequency
                                                          ); 
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Get_Samples() fail to read index %I64d"), 
                          this,
                          i_qwIndex
                         );
                break;
            }
        }

        
        (*o_dwCount)++;

        i_qwIndex ++;
        if (i_qwIndex >= m_sData_Header.qwSamples_Count)
        {
            break;
        }
    } //while ((*o_dwCount) < i_dwCount)

l_lblExit:
    LOCK_EXIT(m_hCS); 

    return l_eReturn;
}//Get_Samples


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Set_Directory
tBOOL CTel_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


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tBOOL CTel_File::Add_Reader(CReader *i_pReader)
{
    LOCK_ENTER(m_hCS); 
    m_cReaders.Add_After(m_cReaders.Get_Last(), i_pReader);
    //adding all ID to reader
    for (size_t l_szI = 0; l_szI < m_szActiveCounters; l_szI++)
    {
        if (m_pCounters[l_szI])
        {
            i_pReader->Push_New_Counter((tUINT16)m_pCounters[l_szI]->pHeader->uId);
        }
    }

    LOCK_EXIT(m_hCS); 
    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tBOOL CTel_File::Del_Reader(CReader *i_pReader)
{
    LOCK_ENTER(m_hCS); 
    pAList_Cell l_pEl = NULL;
    while ((l_pEl = m_cReaders.Get_Next(l_pEl)))
    {
        if (m_cReaders.Get_Data(l_pEl) == i_pReader)
        {
            m_cReaders.Del(l_pEl, FALSE);
            break;
        }
    }
    LOCK_EXIT(m_hCS); 
    return TRUE;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Create_Storage
tBOOL CTel_File::Create_Files()
{
    CWString l_cDirectory;
    CWString l_cFile_Name;
    tBOOL    l_bReturn        = TRUE;
    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/"));
    }
    
    tXCHAR l_pDate[32] = TM("");

    l_qwTime = ((tUINT64)(m_sData_Header.sProcess_Time.dwHighDateTime) << 32) + (tUINT64)m_sData_Header.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_sData_Header.sStream_Time.dwHighDateTime) << 32) + (tUINT64)m_sData_Header.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_TELEMETRY_FILE_EXT_V2);

    m_cPath_File_Data.Set(l_cFile_Name.Get());

    if (!m_hFile_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;
    }

    l_cFile_Name.Trim(l_cFile_Name.Length() - 4);
    l_cFile_Name.Append(1, TM(".p7i"));

    m_cPath_File_IDX.Set(l_cFile_Name.Get());

    if (!m_hFile_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 CTel_File::Process_Packet(const sP7Ext_Header *i_pPacket)
{
    Bk::eResult l_eReturn = Bk::eOk;

    //data packet is most received packet, so we put it first
    if (EP7TEL_TYPE_VALUE == i_pPacket->dwSubType)
    {
        //if space in the buffer is not enough
        if ((m_dwData_Write_Count + 1) >= WRITE_DATA_BUFFER_LENGTH)
        {
            if (TRUE == Write_Buffer(m_hFile_Data,
                                     (tUINT8*)m_pData_Write, 
                                     m_dwData_Write_Count * sizeof(Bk::stTelemetrySample)
                                    )
               )
            {
                m_qwData_Write_First_Index += m_dwData_Write_Count;
                m_dwData_Write_Count        = 0;
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Write buffer failed, free space is over ?"), this);
                m_bSuccess = FALSE;
            }
        }
        
        if (m_bSuccess)
        {
            const sP7Tel_Value_v2 *l_pVal     = (sP7Tel_Value_v2*)i_pPacket;
            Bk::stTelemetrySample *l_pSample  = &m_pData_Write[m_dwData_Write_Count];
            CCounter              *l_pCounter = (l_pVal->wID < m_szCounters) ? m_pCounters[l_pVal->wID] : NULL;
        
            l_pSample->wID = l_pVal->wID;
        
            if (!m_bIs_LittleEndian)
            {
                l_pSample->dbValue = ntohdb(l_pVal->dbValue);
                l_pSample->qwTime  = ntohqw(l_pVal->qwTimer);
            }
            else
            {
                l_pSample->dbValue = l_pVal->dbValue;
                l_pSample->qwTime  = l_pVal->qwTimer;
            }
        
        
            m_sData_Header.qwDuration = l_pSample->qwTime - m_sData_Header.qwTimer_Value;
        
            tUINT64  l_qwIDX_Count = m_sData_Header.qwDuration / m_qwQuantum_Duration;
            if (l_qwIDX_Count > m_sIDX_Header.qwCount)
            {
                Add_Indexes(l_qwIDX_Count - m_sIDX_Header.qwCount);
            }
        
            m_dwData_Write_Count           ++;
            m_sData_Header.qwSamples_Count ++;
        
            if (l_pCounter)
            {
                l_pCounter->pHeader->qwSamples    ++;
                l_pCounter->pHeader->dbCumulative += l_pSample->dbValue;
                
                if (l_pSample->dbValue > l_pCounter->pHeader->dbMax_Real)
                {
                    l_pCounter->pHeader->dbMax_Real = l_pSample->dbValue;
                }
                
                if (l_pSample->dbValue < l_pCounter->pHeader->dbMin_Real)
                {
                    l_pCounter->pHeader->dbMin_Real = l_pSample->dbValue;
                }
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Counter {%d} isn't initialized"), this, l_pVal->wID);
            }
        }
    }
    else if (EP7TEL_TYPE_COUNTER == i_pPacket->dwSubType)
    {
        const sP7Tel_Counter_v2 *l_pCounterIn  = (sP7Tel_Counter_v2*)i_pPacket;
        
        if (Realloc_Counters(l_pCounterIn->wID))
        {
            if (l_pCounterIn->wID >= m_szActiveCounters)
            {
                m_szActiveCounters = l_pCounterIn->wID + 1;
            }

            if (!m_pCounters[l_pCounterIn->wID])
            {
                m_pCounters[l_pCounterIn->wID] = new CCounter(l_pCounterIn, m_bIs_LittleEndian);

                pAList_Cell l_pEl = NULL;
                while ((l_pEl = m_cReaders.Get_Next(l_pEl)))
                {
                    CReader *l_pReader = m_cReaders.Get_Data(l_pEl);
                    if (l_pReader)
                    {
                        l_pReader->Push_New_Counter(l_pCounterIn->wID);
                    }
                }
            }
            else
            {
                LOG_WARNING(TM("[0x%08p] Counter {%d} is already allocated"), this, l_pCounterIn->wID);
            }
            
            if (m_pExtra)
            {
                if (Bk::eOk != m_pExtra->Update_Counter(l_pCounterIn->wID, m_pCounters[l_pCounterIn->wID]->pName, l_pCounterIn->bOn))
                {
                    LOG_ERROR(TM("[0x%08p] Failed to update counter {%s}"), this, m_pCounters[l_pCounterIn->wID]->pName);
                }
            }
        }
        else
        {
            LOG_ERROR(TM("[0x%08p] Counter {%d} isn't allocated"), this, l_pCounterIn->wID);
        }
    }
    else if (EP7TEL_TYPE_INFO == i_pPacket->dwSubType)
    {
        sP7Tel_Info l_sInfo;

        memcpy(&l_sInfo, i_pPacket, sizeof(sP7Tel_Info));

        if (!m_bIs_LittleEndian)
        {
            l_sInfo.dwTime_Hi         = ntohl(l_sInfo.dwTime_Hi);
            l_sInfo.dwTime_Lo         = ntohl(l_sInfo.dwTime_Lo);
            l_sInfo.qwTimer_Value     = ntohqw(l_sInfo.qwTimer_Value);
            l_sInfo.qwTimer_Frequency = ntohqw(l_sInfo.qwTimer_Frequency);
            l_sInfo.qwFlags           = ntohqw(l_sInfo.qwFlags);
            ntohstr(l_sInfo.pName, l_sInfo.pName + P7TELEMETRY_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_sInfo.dwTime_Hi != m_sData_Header.sStream_Time.dwHighDateTime)
                 || (l_sInfo.dwTime_Lo != m_sData_Header.sStream_Time.dwLowDateTime)
               )
            {
                LOG_WARNING(TM("[0x%08p] Receive packet from different stream"),
                            this
                           );
                l_eReturn = Bk::eErrorMissmatch;
            }
        }
        else
        {
            m_sData_Header.sStream_Time.dwHighDateTime = l_sInfo.dwTime_Hi;
            m_sData_Header.sStream_Time.dwLowDateTime  = l_sInfo.dwTime_Lo;
            m_sData_Header.qwTimer_Value               = l_sInfo.qwTimer_Value;
            m_sData_Header.qwTimer_Frequency           = l_sInfo.qwTimer_Frequency;
            //m_qwSample_Time                            = l_sInfo->qwTimer_Value;
            m_qwQuantum_Duration                       = l_sInfo.qwTimer_Frequency / INDEXES_PER_SECOND;

            PWStrCpy(m_sData_Header.pStream_Name, MAX_STREAM_NAME_LENGTH, l_sInfo.pName);

            m_sInfo.sStream_Time.dwHighDateTime = l_sInfo.dwTime_Hi;
            m_sInfo.sStream_Time.dwLowDateTime  = l_sInfo.dwTime_Lo;

        #if defined(UTF8_ENCODING)
            Convert_UTF16_To_UTF8(l_sInfo.pName, m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name));
        #else
            wcscpy_s(m_sInfo.pStream_Name, LENGTH(m_sInfo.pStream_Name), l_sInfo.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_hFile_Data.IsOpened())
            {
                m_bSuccess = Create_Files();
                if (m_bSuccess)
                {
                    if (    (Write_Buffer(m_hFile_Data, (tUINT8*)&m_sData_Header, sizeof(m_sData_Header), FALSE))
                         && (Write_Buffer(m_hFile_IDX, (tUINT8*)&m_sIDX_Header, sizeof(m_sIDX_Header), FALSE))
                       )
                    {
                        m_bInitialized = TRUE;
                    }
                    else
                    {
                        LOG_ERROR(TM("[0x%08p] Writing header failed"), this);
                        m_bSuccess = FALSE;
                    }
                }
                else
                {
                    LOG_ERROR(TM("[0x%08p] Create_Files() failed"), this);
                }
            }//if (NULL == m_hFile_Data)
        }
    }
    else if (EP7TEL_TYPE_CLOSE == i_pPacket->dwSubType)
    {
        tUINT64 l_qwIDX_Count = m_sData_Header.qwDuration / m_qwQuantum_Duration;

        if (    (m_sData_Header.qwSamples_Count)
             && (!l_qwIDX_Count)
             && (!m_sIDX_Header.qwCount)
           )
        {
            l_qwIDX_Count ++;
        }

        if (l_qwIDX_Count > m_sIDX_Header.qwCount)
        {
            Add_Indexes(l_qwIDX_Count - m_sIDX_Header.qwCount);
        }

        l_eReturn = Bk::eErrorClosed;
    }

    return l_eReturn;
}//Process_Packet


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Add_Indexes
tBOOL CTel_File::Add_Indexes(tUINT64 i_qwCount)
{
    tBOOL l_bResult = TRUE;

    if (0 == m_sIDX_Header.qwCount)
    {
        if (m_sData_Header.qwSamples_Count) //if module receive few samples before
        {
            m_sIDX_Header.qwFirst_IDX    = 0;
            m_sIDX_Header.qwCount        = 1;
            m_pIDX_Write[0].qwSample_IDX = 0;
            i_qwCount --;
            m_dwIDX_Write_Count ++;
        }
        else //if we receive first sample and there time is passed
        {
            m_sIDX_Header.qwFirst_IDX = i_qwCount - 1;
            m_sIDX_Header.qwCount     = i_qwCount - 1;
            i_qwCount                 = 1;
        }
    }

    while (i_qwCount --)
    {
        if ((m_dwIDX_Write_Count + 1) >= WRITE_INDEX_BUFFER_LENGTH)
        {
            if (TRUE == Write_Buffer(m_hFile_IDX,
                                     (tUINT8*)m_pIDX_Write, 
                                     m_dwIDX_Write_Count * sizeof(sIndex_Item)
                                    )
               )
            {
                m_qwIDX_Write_First_Index += m_dwIDX_Write_Count;
                m_dwIDX_Write_Count        = 0;
            }
            else
            {
                LOG_ERROR(TM("[0x%08p] Write index buffer failed, free space is over ?"), this);
                m_bSuccess = FALSE;
            }
        }

        m_pIDX_Write[m_dwIDX_Write_Count].qwSample_IDX = m_sData_Header.qwSamples_Count;
        m_dwIDX_Write_Count ++;

        m_sIDX_Header.qwCount ++;
    }

    return l_bResult;
}//Add_Indexes


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Write_Buffer
tBOOL CTel_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 CTel_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
#pragma endregion STORAGE


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Realloc_Counters
tBOOL CTel_File::Realloc_Counters(tUINT32 i_uID)
{
    if ((size_t)i_uID < m_szCounters)
    {
        return TRUE;
    }

    size_t l_szNewLen  = (size_t)((i_uID + 4096u) & (~4095u));
    void  *l_pNewBlock = realloc(m_pCounters, l_szNewLen * sizeof(CCounter*));

    if (l_pNewBlock)
    {
        m_pCounters = (CCounter**)l_pNewBlock;

        while (m_szCounters < l_szNewLen)
        {
            m_pCounters[m_szCounters++] = 0;
        }

        return TRUE;
    }

    return FALSE;


}
