////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#pragma once

#include "Prop.h"
#include "UTF.h"

#pragma warning( disable : 4250 )

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CP7Stream_Ex
class  CP7Stream_Ex
    : public virtual Bk::IStreamEx
{
protected:
    //put volatile variables at the top, to obtain 32 bit alignment. 
    //Project has 8 bytes alignment by default
    tINT32 volatile    m_lReference;

    tBOOL              m_bActive;
    tUINT32            m_uChannel;
    CConnectionP7     *m_pConnection;
    CProperty         *m_pRoot;
    tBOOL              m_bBigEndian;
    CLock              m_cLock;
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex::CP7Stream_Ex()
    CP7Stream_Ex(tUINT32        i_uChannel,
                 CConnectionP7 *i_pConnection,
                 CProperty     *i_pRoot
                )
        : m_lReference(1)
        , m_bActive(TRUE)
        , m_uChannel(i_uChannel)
        , m_pConnection(i_pConnection)
        , m_pRoot(i_pRoot)
        , m_bBigEndian(FALSE)
    {
        if (m_pRoot)
        {
            m_pRoot->Add_Ref();
        }
        else
        {
            CProperty::Create(NULL, CProperty::eGroup, TM("root"), m_pRoot);
        }

        if (m_pConnection)
        {
            m_bBigEndian = m_pConnection->Is_BigEndian();
        }
    }//CP7Stream_Ex::CP7Stream_Ex()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex::~CP7Stream_Ex()
    virtual ~CP7Stream_Ex()
    {
        m_pRoot->Release();
    }//CP7Stream_Ex::~CP7Stream_Ex()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex::Is_Active()
    Bk::eResult __stdcall Is_Active(tBOOL *o_pActive)
    {
        CLock l_cLock(&m_cLock);
        if (o_pActive)
        {
            *o_pActive = m_bActive;
        }
        return Bk::eOk;
    }//CP7Stream_Ex::Is_Active()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex::Get_Status()
    Bk::eResult __stdcall Get_Status(Bk::stStreamStatus *o_pStatus)
    {
        Bk::eResult l_eReturn = Bk::eErrorWrongInput;
        CLock l_cLock(&m_cLock);
        if (    (m_bActive)
             && (m_pConnection)
           )
        {
            l_eReturn = m_pConnection->Get_Status(o_pStatus);
        }

        return l_eReturn;
    }//CP7Stream_Ex::Get_Status()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex::Get_Root()
    virtual Bk::eResult __stdcall Get_Root(CProperty *&o_rRoot) = 0;


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex::Deactivate()
    void Deactivate()
    {
        CLock l_cLock(&m_cLock);
        m_bActive     = FALSE;
        m_pConnection = NULL;
    }//CP7Stream_Ex::Deactivate()


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


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

        return l_lResult;
    }//CP7Stream_Ex::Release()
};//CP7Stream_Ex


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CP7Stream_Ex_Trace
class CP7Stream_Ex_Trace
    : public virtual CP7Stream_Ex
    , public virtual Bk::IStreamExTrace
    , public CProperty::IApplyer
{
    CProperty *m_pSession;
    CProperty *m_pPropTrc;
    CProperty *m_pPropUsr;

public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Trace::CP7Stream_Ex_Trace()
    CP7Stream_Ex_Trace(tUINT32        i_uChannel,
                       CConnectionP7 *i_pConnection,
                       CProperty     *i_pRoot
                      )
        : CP7Stream_Ex(i_uChannel, i_pConnection, i_pRoot)
        , m_pSession(NULL)
        , m_pPropTrc(NULL)
        , m_pPropUsr(NULL)
    {
        if (m_pRoot)
        {
            m_pRoot->Get_SubItem(TM("Trace"), m_pPropTrc);
            if (!m_pPropTrc)
            {
                CProperty::Create(m_pRoot, CProperty::eGroup, TM("Trace"), m_pPropTrc);
            }
            else
            {
                m_pPropTrc->Get_SubItem(STREAM_EX_LOCAL_SETTINGS, m_pPropUsr);
            }
        }

        if (    (m_pPropTrc)
             && (!m_pPropUsr)
           )
        {
            CProperty::Create(m_pPropTrc, CProperty::eInt, STREAM_EX_LOCAL_SETTINGS, m_pPropUsr);
            if (m_pPropUsr)
            {
                m_pPropUsr->Set_Range(0ll, 1ll);
                m_pPropUsr->Set_Value(0ll);
                m_pPropUsr->Set_Desc(TM("User settings for trace is used instead remote client settings\n")
                                     TM("Applicable for all verbosity levels for modules modified by user\n")
                                     TM("Baical restart is required!\n")
                                     TM("press F2 (or mouse double click) to edit"));
            }
        }
    }//CP7Stream_Ex_Trace::CP7Stream_Ex_Trace()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Trace::~CP7Stream_Ex_Trace()
    virtual ~CP7Stream_Ex_Trace()
    {
        if (m_pSession)
        {
            m_pSession->Release();
            m_pSession = NULL;
        }

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

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

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //Get_Root()
    virtual Bk::eResult __stdcall Get_Root(CProperty *&o_rRoot)
    {
        CLock l_cLock(&m_cLock);
        o_rRoot = m_pSession;
        o_rRoot->Add_Ref();

        return Bk::eOk;
    }//CP7Stream_Ex::Get_Root()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //Initialize()
    Bk::eResult __stdcall Initialize(const tXCHAR *i_pSession)
    {
        CLock l_cLock(&m_cLock);
        if (!m_pSession)
        {
            m_pPropTrc->Get_SubItem(i_pSession, m_pSession);
            if (!m_pSession)
            {
                CProperty::Create(m_pPropTrc, CProperty::eGroup, i_pSession, m_pSession);
            }
        }

        return m_pSession ? Bk::eOk : Bk::eErrorMissmatch;
    }//CP7Stream_Ex::Get_Root()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Trace::Close()
    Bk::eResult __stdcall Close()
    {
        Bk::eResult l_eReturn = Bk::eErrorBlocked;
        CLock l_cLock(&m_cLock);

        if (    (m_bActive)
             && (m_pConnection)
           )
        {
            sP7Ext_Header l_sPacket = { EP7USER_TYPE_TRACE,
                                        EP7TRACE_TYPE_DELETE,
                                        sizeof(sP7Ext_Header)
                                      };

            if (m_bBigEndian)
            {
                sP7Ext_Raw *l_pRaw = (sP7Ext_Raw*)&l_sPacket;
                l_pRaw->dwBits = htonl(l_pRaw->dwBits);
            }

            l_eReturn = m_pConnection->Put_Packet(m_uChannel, (const tUINT8*)&l_sPacket, sizeof(l_sPacket));
        }

        return l_eReturn;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Trace::Update()
    Bk::eResult __stdcall Update(void *i_pModule)
    {
        tXCHAR          l_pName[P7TRACE_MODULE_NAME_LENGTH];
        Bk::eResult     l_bReturn   = Bk::eErrorWrongInput;
        CProperty      *l_pItem     = NULL; 
        tINT32          l_iOldIndex = -1;
        const void     *l_pData     = NULL;
        size_t          l_szData    = 0;
        tBOOL           l_bUseLocal = FALSE;
        sP7Trace_Module*l_pModule   = (sP7Trace_Module*)i_pModule;
                                 
        CLock l_cLock(&m_cLock);

        if (    (!l_pModule)
             || (!m_pSession)
           )
        {
            goto l_lblExit;
        }

        l_iOldIndex = (tINT32)l_pModule->eVerbosity;

    #ifdef UTF8_ENCODING 
        strcpy(l_pName, l_pModule->pName);
    #else
        Convert_UTF8_To_UTF16(l_pModule->pName, 
                              (tWCHAR*)l_pName, 
                              P7TRACE_MODULE_NAME_LENGTH
                             );
    #endif                             


        if (m_pPropUsr)
        {
            tINT64 l_llVal = 0;
            m_pPropUsr->Get_Value(l_llVal);
            l_bUseLocal = l_llVal ? TRUE : FALSE;
        }

        m_pSession->Get_Lock()->Lock(); ////////////////////////////////////////////////////////////////////////////////

        //trying to find item by name
        m_pSession->Get_SubItem(l_pName, l_pItem);
        if (!l_pItem)
        {
            CProperty::Create(m_pSession, CProperty::eEnum, l_pName, l_pItem);

            if (l_pItem)
            {
                l_pItem->Add_Item(TM("Trace"),    EP7TRACE_LEVEL_TRACE);
                l_pItem->Add_Item(TM("Debug"),    EP7TRACE_LEVEL_DEBUG);
                l_pItem->Add_Item(TM("Info"),     EP7TRACE_LEVEL_INFO);
                l_pItem->Add_Item(TM("Warning"),  EP7TRACE_LEVEL_WARNING);
                l_pItem->Add_Item(TM("Error"),    EP7TRACE_LEVEL_ERROR);
                l_pItem->Add_Item(TM("Critical"), EP7TRACE_LEVEL_CRITICAL);
                l_pItem->Set_Desc(TM("Module verbosity level"));
        
                l_pItem->Set_Desc(TM("Module verbosity level\npress F2 (or mouse double click) to edit"));
                l_pItem->Set_Data(&l_pModule->wModuleID, sizeof(l_pModule->wModuleID));

                l_pItem->Set_Current_Index((tINT32)l_pModule->eVerbosity);
            }
        }
        else 
        {
            l_pItem->Get_Current_Index(l_iOldIndex);
        }

        m_pSession->Get_Lock()->Unlock(); //////////////////////////////////////////////////////////////////////////////

        if (!l_pItem)
        {
            l_bReturn = Bk::eErrorMissmatch;
            goto l_lblExit;
        }

        l_pItem->Set_Applyer(this, FALSE);

        l_pItem->Get_Data(l_pData, l_szData);
        if (    (!l_pData)
             || (l_pModule->wModuleID != (*(const tUINT32*)l_pData))
           )
        {
            l_pItem->Set_Data(&l_pModule->wModuleID, sizeof(l_pModule->wModuleID));
        }

        l_bReturn = Bk::eOk;

        if (l_iOldIndex != (tINT32)l_pModule->eVerbosity)
        {
            if (    (CProperty::eUserModified & l_pItem->Flags())
                 && (l_bUseLocal)
               )
            {
                if (!l_pItem->Set_Current_Index(l_iOldIndex)) //to apply value
                {
                    l_bReturn = Bk::eErrorInternal;
                }
            }
            else
            {
                CLock l_cLock(l_pItem->Get_Lock());
                l_pItem->Set_Applyer(NULL, FALSE);
                l_pItem->Set_Current_Index((tINT32)l_pModule->eVerbosity);
                l_pItem->Set_Applyer(this, FALSE);
            }
        }

    l_lblExit:
        if (l_pItem)
        {
            l_pItem->Release();
        }

        return l_bReturn;
    }//CP7Stream_Ex_Trace::Update()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Trace::Query_Interface()
    Bk::eResult __stdcall Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown)
    {
        o_rUnknown = NULL;

        if (Bk::eInterfaceStreamExTrace == i_eId)
        {
            o_rUnknown = dynamic_cast<Bk::IStreamExTrace*>(this);

            if (o_rUnknown)
            {
                CP7Stream_Ex::Add_Ref();
            }

            return Bk::eOk;
        }

        return Bk::eErrorNotImplemented;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Trace::Apply()
    tBOOL Apply(CProperty *i_pItem, const void *i_pOld_Value)
    {
        UNUSED_ARG(i_pOld_Value);

        tBOOL l_bReturn = FALSE;
        CLock l_cLock(&m_cLock);

        if (    (m_bActive)
             && (m_pConnection)
           )
        {
            tINT32      l_lValue = -1;
            const void *l_pData  = NULL;
            size_t      l_szData = 0;
                
            i_pItem->Get_Current_Index(l_lValue);
            i_pItem->Get_Data(l_pData, l_szData);

            if (0 <= l_lValue)
            {
                sP7Trace_Verb l_sPacket = { { EP7USER_TYPE_TRACE, 
                                              EP7TRACE_TYPE_VERB,
                                              sizeof(sP7Trace_Verb)
                                            },
                                            (eP7Trace_Level)l_lValue,
                                            *(const tUINT16*)l_pData
                                          };

                if (m_bBigEndian)
                {
                    l_sPacket.sCommonRaw.dwBits = htonl(l_sPacket.sCommonRaw.dwBits);
                    l_sPacket.wModuleID         = htons(l_sPacket.wModuleID);
                    l_sPacket.eVerbosity        = (eP7Trace_Level)htonl(l_sPacket.eVerbosity);
                }

                if (Bk::eOk == m_pConnection->Put_Packet(m_uChannel, (const tUINT8*)&l_sPacket, sizeof(sP7Trace_Verb)))
                {
                    l_bReturn = TRUE;
                }//if (Bk::eOk == l_eReturn)
            }
        }

        return l_bReturn;
    }//CP7Stream_Ex_Trace::Apply()
};//CP7Stream_Ex_Trace


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CP7Stream_Ex_Telemetry
class CP7Stream_Ex_Telemetry
    : public virtual CP7Stream_Ex
    , public virtual Bk::IStreamExTelemetry
    , public CProperty::IApplyer
{
public:
    enum eProtocol
    {
        eProtocol_v1 = 0,
        eProtocol_v2
    };

protected:
    CProperty *m_pSession;
    CProperty *m_pPropTel;
    CProperty *m_pPropUsr;
    eProtocol  m_eProtocol;

public:

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry
    CP7Stream_Ex_Telemetry(tUINT32        i_uChannel,
                           CConnectionP7 *i_pConnection,
                           CProperty     *i_pRoot,
                           eProtocol      i_eProtocol
                          )
        : CP7Stream_Ex(i_uChannel, i_pConnection, i_pRoot)
        , m_pSession(NULL)
        , m_pPropTel(NULL)
        , m_pPropUsr(NULL)
        , m_eProtocol(i_eProtocol)
    {
        if (m_pRoot)
        {
            m_pRoot->Get_SubItem(TM("Telemetry"), m_pPropTel);
            if (!m_pPropTel)
            {
                CProperty::Create(m_pRoot, CProperty::eGroup, TM("Telemetry"), m_pPropTel);
            }
            else
            {
                m_pPropTel->Get_SubItem(STREAM_EX_LOCAL_SETTINGS, m_pPropUsr);
            }
        }

        if (    (m_pPropTel)
             && (!m_pPropUsr)
           )
        {
            CProperty::Create(m_pPropTel, CProperty::eInt, STREAM_EX_LOCAL_SETTINGS, m_pPropUsr);
            if (m_pPropUsr)
            {
                m_pPropUsr->Set_Range(0ll, 1ll);
                m_pPropUsr->Set_Value(0ll);
                m_pPropUsr->Set_Desc(TM("User settings for telemetry is used instead remote client settings\n")
                                     TM("Applicable for all modified by user counters enable flags\n")
                                     TM("Baical restart is required!\n")
                                     TM("press F2 (or mouse double click) to edit")
                                    );
            }
        }
    }//CP7Stream_Ex_Trace::CP7Stream_Ex_Trace()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //~CP7Stream_Ex_Telemetry()
    virtual ~CP7Stream_Ex_Telemetry()
    {
        if (m_pSession)
        {
            m_pSession->Release();
            m_pSession = NULL;
        }

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

        if (m_pPropUsr)
        {
            m_pPropUsr->Release();
            m_pPropUsr = NULL;
        }
    }//CP7Stream_Ex::~CP7Stream_Ex()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry::Get_Root()
    virtual Bk::eResult __stdcall Get_Root(CProperty *&o_rRoot)
    {
        CLock l_cLock(&m_cLock);
        o_rRoot = m_pSession;
        o_rRoot->Add_Ref();

        return Bk::eOk;
    }//CP7Stream_Ex_Telemetry::Get_Root()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry::Initialize()
    Bk::eResult __stdcall Initialize(const tXCHAR *i_pSession)
    {
        CLock l_cLock(&m_cLock);
        if (!m_pSession)
        {
            m_pPropTel->Get_SubItem(i_pSession, m_pSession);
            if (!m_pSession)
            {
                CProperty::Create(m_pPropTel, CProperty::eGroup, i_pSession, m_pSession);
            }
        }

        return m_pSession ? Bk::eOk : Bk::eErrorMissmatch;
    }//CP7Stream_Ex_Telemetry::Get_Root()


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry::Query_Interface()
    Bk::eResult __stdcall Query_Interface(Bk::eInterface i_eId, void *&o_rUnknown)
    {
        o_rUnknown = NULL;

        if (Bk::eInterfaceStreamExTelemetry == i_eId)
        {
            o_rUnknown = dynamic_cast<Bk::IStreamExTelemetry*>(this);

            if (o_rUnknown)
            {
                CP7Stream_Ex::Add_Ref();
            }

            return Bk::eOk;
        }

        return Bk::eErrorNotImplemented;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry::Update_Counter()
    Bk::eResult __stdcall Update_Counter(tUINT32 i_dwID, const tXCHAR *i_pName, tBOOL i_bOn)       
    {
        Bk::eResult l_bReturn     = Bk::eErrorWrongInput;
        CProperty  *l_pItem       = NULL; 
        const void *l_pData       = NULL;
        size_t      l_szData      = 0;
        tINT64      l_qwValReal   = i_bOn ? 1ll : 0ll;
        tINT64      l_qwValStored = l_qwValReal;
        tBOOL       l_bUseLocal   = FALSE;

        CLock l_cLock(&m_cLock);

        if (    (!i_pName)
             || (!m_pSession)
           )
        {
            goto l_lblExit;
        }

        if (m_pPropUsr)
        {
            tINT64 l_llVal = 0;
            m_pPropUsr->Get_Value(l_llVal);
            l_bUseLocal = l_llVal ? TRUE : FALSE;
        }

        m_pSession->Get_Lock()->Lock(); ////////////////////////////////////////////////////////////////////////////////

        //trying to find item by name
        m_pSession->Get_SubItem(i_pName, l_pItem);
        if (!l_pItem)
        {
            CProperty::Create(m_pSession, CProperty::eInt, i_pName, l_pItem);
            if (l_pItem) //creation error
            {
                l_pItem->Set_Range(0, 1);
                l_pItem->Set_Desc(TM("Counter enable/disable flag\npress F2 (or mouse double click) to edit"));
                l_pItem->Set_Value(l_qwValReal);
            }
        }
        else
        {
            l_pItem->Get_Value(l_qwValStored);
        }

        m_pSession->Get_Lock()->Unlock(); //////////////////////////////////////////////////////////////////////////////

        if (!l_pItem)
        {
            l_bReturn = Bk::eErrorMissmatch;
            goto l_lblExit;
        }


        l_pItem->Set_Applyer(this, FALSE);

        l_pItem->Get_Data(l_pData, l_szData);
        if (    (!l_pData)
             || (i_dwID != (*(const tUINT32*)l_pData))
           )
        {
            l_pItem->Set_Data(&i_dwID, sizeof(i_dwID));
        }

        l_bReturn = Bk::eOk;

        if (l_qwValReal != l_qwValStored)
        {
            if (l_bUseLocal)
            {
                if (!l_pItem->Set_Value(l_qwValStored)) //to apply value
                {
                    l_bReturn = Bk::eErrorInternal;
                }
            }
            else
            {
                CLock l_cLock(l_pItem->Get_Lock());
                l_pItem->Set_Applyer(NULL, FALSE);
                if (!l_pItem->Set_Value(l_qwValReal)) 
                {
                    l_bReturn = Bk::eErrorInternal;
                }
                l_pItem->Set_Applyer(this, FALSE);
            }
        }

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

        return l_bReturn;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry::Close()
    Bk::eResult __stdcall Close()
    {
        Bk::eResult l_eReturn = Bk::eErrorBlocked;
        CLock l_cLock(&m_cLock);

        if (    (m_bActive)
             && (m_pConnection)
           )
        {
            sP7Ext_Header l_sPacket = { EP7USER_TYPE_TELEMETRY_V2, EP7TEL_TYPE_DELETE, sizeof(sP7Ext_Header)};

            if (m_bBigEndian)
            {
                sP7Ext_Raw *l_pRaw = (sP7Ext_Raw*)&l_sPacket;
                l_pRaw->dwBits = htonl(l_pRaw->dwBits);
            }

            l_eReturn = m_pConnection->Put_Packet(m_uChannel, (const tUINT8*)&l_sPacket, sizeof(l_sPacket));
        }

        return l_eReturn;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //CP7Stream_Ex_Telemetry::Apply()
    tBOOL Apply(CProperty *i_pItem, const void *i_pOld_Value)
    {
        UNUSED_ARG(i_pOld_Value);
        tBOOL l_bReturn = FALSE;

        CLock l_cLock(&m_cLock);

        if (    (m_bActive)
             && (m_pConnection)
           )
        {
            tINT64      l_llValue = 0ll;
            const void *l_pData   = NULL;
            size_t      l_szData  = 0;
                
            i_pItem->Get_Value(l_llValue);
            i_pItem->Get_Data(l_pData, l_szData);

            if (0 <= l_llValue)
            {
                if (eProtocol_v1 == m_eProtocol)
                {
                    sP7Tel_Enable_v1 l_sPacket = { { EP7USER_TYPE_TELEMETRY_V1,
                                                     EP7TEL_TYPE_ENABLE,
                                                     sizeof(sP7Tel_Enable_v1)
                                                   },
                                                   (tUINT8)(*(tUINT32*)l_pData),
                                                   (tUINT8)((l_llValue) ? 1 : 0)
                    };

                    if (m_bBigEndian)
                    {
                        l_sPacket.sCommonRaw.dwBits = htonl(l_sPacket.sCommonRaw.dwBits);
                    }

                    if (Bk::eOk == m_pConnection->Put_Packet(m_uChannel, (const tUINT8*)&l_sPacket, sizeof(l_sPacket)))
                    {
                        l_bReturn = TRUE;
                    }//if (Bk::eOk == l_eReturn)
                }
                else
                {
                    sP7Tel_Enable_v2 l_sPacket = { { EP7USER_TYPE_TELEMETRY_V2,
                                                     EP7TEL_TYPE_ENABLE,
                                                     sizeof(sP7Tel_Enable_v2)
                                                   },
                                                   (tUINT16)(*(tUINT32*)l_pData),
                                                   (tUINT16)((l_llValue) ? 1 : 0)
                    };

                    if (m_bBigEndian)
                    {
                        l_sPacket.sCommonRaw.dwBits = htonl(l_sPacket.sCommonRaw.dwBits);
                    }

                    if (Bk::eOk == m_pConnection->Put_Packet(m_uChannel, (tUINT8*)&l_sPacket, sizeof(l_sPacket)))
                    {
                        l_bReturn = TRUE;
                    }//if (Bk::eOk == l_eReturn)
                }
            }
        }

        return l_bReturn;
    }//CP7Stream_Ex_Telemetry::Apply()
};//CP7Stream_Ex_Telemetry
