////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2019 (c) Baical                                                        /
//                                                                             /
// This library is free software; you can redistribute it and/or               /
// modify it under the terms of the GNU Lesser General Public                  /
// License as published by the Free Software Foundation; either                /
// version 3.0 of the License, or (at your option) any later version.          /
//                                                                             /
// This library is distributed in the hope that it will be useful,             /
// but WITHOUT ANY WARRANTY; without even the implied warranty of              /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU           /
// Lesser General Public License for more details.                             /
//                                                                             /
// You should have received a copy of the GNU Lesser General Public            /
// License along with this library.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#ifndef BK_P7_TELEMETRY_H
#define BK_P7_TELEMETRY_H

#define INDEXES_PER_SECOND                                                 (100) //1 per 10 ms

// Data file will have this structure
// [sFile_Header] [sSample] 

class CTel_File; //pre-declaration

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CIdList
  : public CListPool<tUINT16>
{
protected:
    virtual tBOOL Data_Release(tUINT16 i_pData)
    {
        UNUSED_ARG(i_pData);
        return TRUE;
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CReader
    : public Bk::ITelemetryReader
{
    //put volatile variables at the top, to obtain 32 bit alignment. 
    //Project has 8 bytes alignment by default
    tINT32 volatile        m_lReference;
    CTel_File             *m_pParent;

    CIdList                m_cIdList;
    tLOCK                  m_hCsIdList;
public:
    PRAGMA_PACK_ENTER(2)
    struct stTelemetrySample
    {
        /// <summary> time offset in 100ns intervals, value between 0 and Bk::stTelemetryInfo::qwDuration </summary>
        tUINT64 qwTime;     
        /// <summary> sample value </summary>
        tINT64  llValue; 
        /// <summary> counter ID </summary>
        tUINT8  bID; 
    }ATTR_PACK(2);
    PRAGMA_PACK_EXIT()



    CReader::stTelemetrySample *m_pData_Read;
    size_t                      m_dwData_Read_Count;
    tUINT64                     m_qwData_Read_First_Index;
                              
    sIndex_Item                *m_pIDX_Read;
    size_t                      m_dwIDX_Read_Count;
    tUINT64                     m_qwIDX_Read_First_Index;

public:
    CReader(CTel_File *i_pParent, tBOOL &o_rError);
    virtual ~CReader();

    Bk::eResult __stdcall Get_Info(Bk::stTelemetryInfo *o_pInfo);
    Bk::eResult __stdcall Get_Counter(tUINT16 i_wId, Bk::stTelemetryCounter *o_pCounter, Bk::eTelemetryCounterValue i_eDetails);
    Bk::eResult __stdcall Get_Nearest_Index(tUINT64  i_qwTime, tUINT64 *o_pIndex);
    Bk::eResult __stdcall Get_Samples(tUINT64 i_qwIndex, Bk::stTelemetrySample *o_pSamples, tUINT32 i_dwCount, tUINT32 *o_dwCount);
    Bk::eResult __stdcall Pull_New_Counter(tUINT16 &o_rId);
    tUINT32     __stdcall Get_New_Counters_Count();
    void                  Push_New_Counter(tUINT16 i_uId);

    tINT32 __stdcall Add_Ref()
    {
        return ATOMIC_INC(&m_lReference);
    }

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

        return l_lResult;
    }
};


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CTel_File
    : public Bk::IStorage
    , public Bk::IStorageReader
{
protected:
    //put volatile variables at the top, to obtain 32 bit alignment. 
    //Project has 8 bytes alignment by default
    tINT32 volatile         m_lReference;
                           
    sFile_Header            m_sData_Header; 
    sIndex_Header           m_sIDX_Header;
    Bk::stStorageInfo       m_sInfo;
    tBOOL                   m_bIsInfo;
    tBOOL                   m_bReadOnly;
    tBOOL                   m_bDelete;
      
    IP7_Trace              *m_iP7_Trace;
    IP7_Trace::hModule      m_hP7_TraceModule;
    IP7_Telemetry          *m_iP7_Tel;

    ////////////////////////////////////////////////////////////////////////////
    CWString                m_cPath_Root;
    CWString                m_cPath_Directory;
    CWString                m_cPath_File_Data;
    CWString                m_cPath_File_IDX; 

    ////////////////////////////////////////////////////////////////////////////
    // Data file variables
    CPFile                  m_hFile_Data;
                           
    CReader::stTelemetrySample *m_pData_Write;
    tUINT32                 m_dwData_Write_Count;
    tUINT64                 m_qwData_Write_First_Index;
                           
    ////////////////////////////////////////////////////////////////////////////
    // Index file variables
    CPFile                  m_hFile_IDX;
                          
    sIndex_Item            *m_pIDX_Write;
    tUINT32                 m_dwIDX_Write_Count;
    tUINT64                 m_qwIDX_Write_First_Index;
                          
    ////////////////////////////////////////////////////////////////////////////
    // service variables
    tLOCK                   m_hCS;
    Bk::IBNode             *m_pNode;
    CProperty              *m_pProp;
    tBOOL                   m_bSuccess;
    tBOOL                   m_bInitialized;
    tBOOL                   m_bIs_LittleEndian;
    tUINT64                 m_qwQuantum_Duration;
    tXCHAR                 *m_pNames[P7TELEMETRY_COUNTERS_MAX_COUNT_V1];

    Bk::IStreamExTelemetry *m_pExtra;

    CBList<CReader*>        m_cReaders;

public:
    CTel_File(Bk::IBNode *i_pNode, CProperty *i_pProp);

    virtual ~CTel_File();

    Bk::eResult             Init_Reader(const tXCHAR *i_pFile_Name);

    Bk::eResult   __stdcall Get_Info(Bk::stStorageInfo *o_pInfo);

    Bk::eResult   __stdcall Get_Path(tXCHAR **o_pPath);

    Bk::eResult   __stdcall Get_Size(tUINT64 *o_pSize);

    Bk::eResult   __stdcall Get_Description(Bk::stStorageDesc &io_rDesc);

    Bk::eResult             Init_Writer(Bk::stStreamInfo *i_pInfo);
    Bk::eResult   __stdcall Uninit_Writer();
    Bk::eResult   __stdcall Put_Packet(Bk::stDataChunk *i_pChunk);

    Bk::eResult   __stdcall Put_Stream_Ex(Bk::IStreamEx *i_pEx);

    Bk::eResult   __stdcall Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown);

    Bk::eResult __stdcall   Delete();

    
    Bk::eResult __stdcall   Get_Info(Bk::stTelemetryInfo *o_pInfo);

    Bk::eResult __stdcall   Get_Counter(tUINT16                    i_wId,
                                        Bk::stTelemetryCounter    *o_pCounter,
                                        Bk::eTelemetryCounterValue i_eDetails
                                       );        
                                                            
    Bk::eResult __stdcall   Get_Nearest_Index(CReader *i_pReader, tUINT64  i_qwTime, tUINT64 *o_pIndex);

    Bk::eResult __stdcall   Get_Samples(CReader               *i_pReader, 
                                        tUINT64                i_qwIndex,
                                        Bk::stTelemetrySample *o_pSamples, 
                                        tUINT32                i_dwCount, 
                                        tUINT32               *o_dwCount 
                                       );

    tBOOL                   Set_Directory(const tXCHAR *i_pDirectory);

    tBOOL                   Add_Reader(CReader *i_pReader);
    tBOOL                   Del_Reader(CReader *i_pReader);



    tINT32 __stdcall Add_Ref()
    {
        return ATOMIC_INC(&m_lReference);
    }

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

        return l_lResult;
    }

    tBOOL Is_Failed()
    {
        return (! m_bSuccess);
    }

protected:
    tBOOL              Create_Files();
    inline Bk::eResult Process_Packet(const sP7Ext_Header *i_pPacket);
    tBOOL              Add_Indexes(tUINT64 i_qwCount);
    tBOOL              Write_Buffer(CPFile  &i_rFile, tUINT8 *i_pBuffer, size_t i_dwSize, tBOOL i_bAppend = TRUE);
    tBOOL              Read_Buffer(CPFile &i_rFile, tUINT64 i_qwOffset, tUINT8 *i_pBuffer, size_t i_szBuffer, size_t *o_pRead);
};

#endif //BK_P7_TELEMETRY_H