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

static const GUID g_sPlugin_GUID = { 0xb539de44, 0x7e6a, 0x4ea7, 
                                     { 0xa7, 0x41, 0x46, 0xf8,
                                       0xe5, 0x7c, 0x6c, 0x65
                                     }
                                   };


#define SAFE_RELEASE(x) if (x) { x->Release(); x = NULL;}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#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::ePluginViewerCLI;
    o_pInfo->dwAPI_Version = BK_PLUGIN_API_VERSION;
    o_pInfo->qwVersion     = 0; //will be filled outside;
    o_pInfo->pFormats      = NULL;

    PStrCpy(o_pInfo->pName, LENGTH(o_pInfo->pName), TM("Viewer: P7 Telemetry (cli)"));

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

    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_Viewer
P7_EXPORT Bk::eResult __cdecl Create_Viewer(Bk::IViewer           **o_pViewer,  //[Out] result viewer interface
                                            WND_HANDLE              i_hParent,  //[Inp] parent window
                                            Bk::IBNode             *i_pNode,    //[Inp] stored configuration node
                                            Bk::IStorageReader     *i_iReader,  //[Inp] reader interface
                                            Bk::IStreamEx          *i_iExtra,   //[Inp] stream extra interface, NULL for offline streams
                                            CProperty              *i_pProp,    //[Inp] properties
                                            Bk::IViewersEnumerator *i_pEnum     //[Inp] viewers enumerator
                                           )
{
    Bk::eResult l_eReturn = Bk::eOk;

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

    CViewTelemetry *l_pVTrace = new CViewTelemetry((CConsole*)i_hParent, i_pNode, i_iReader, i_iExtra, i_pProp, i_pEnum);
   
    if (    (l_pVTrace)
         && (TRUE == l_pVTrace->Is_Failed())
       )
    {
        delete l_pVTrace;
        l_pVTrace = NULL;
    }

    *o_pViewer = l_pVTrace; 

    return l_eReturn;
}//Create_Viewer

//compiler check than function match the prototype
static const fnBkCreateViewer g_pCreateViewer = Create_Viewer;
}//extern "C"
#pragma endregion  ENTRY




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CViewTelemetry::CViewTelemetry(CConsole               *i_pConsole,
                               Bk::IBNode             *i_pNode,
                               Bk::IStorageReader     *i_iReader,
                               Bk::IStreamEx          *i_iExtra, 
                               CProperty              *i_pProp,
                               Bk::IViewersEnumerator *i_pEnum            
                              )
    : CConsoleWnd()  
    , m_lReference(1)
    , m_pConsole(i_pConsole)
    , m_iReader(i_iReader)
    , m_iTelemetry(NULL)
    , m_pNode(i_pNode)
    , m_iExtra(i_iExtra)
    , m_pProp(i_pProp)
    , m_pEnum(i_pEnum)
    , m_bRealTime(TRUE)
    , m_wCount(0)
{
    CConsoleWnd::sCfg l_cCfg = {};

    l_cCfg.pConsole      = dynamic_cast<IConsole*>(m_pConsole);
    l_cCfg.szItemsCount  = 0;
    l_cCfg.uX            = 0;
    l_cCfg.uY            = 0;
    l_cCfg.uH            = m_pConsole->GetHeight() - 1;
    l_cCfg.uW            = m_pConsole->GetWidth();
    l_cCfg.pCaption      = TM("Telemetry");
    l_cCfg.eWindowBgColor= IConsole::eBlack;
    l_cCfg.eItemBgColor  = IConsole::eBlack;
    l_cCfg.eItemFgColor  = IConsole::eGreen;
    l_cCfg.eItemSlFgColor= IConsole::eBlue;
    l_cCfg.eItemSlBgColor= IConsole::eWhite;
    l_cCfg.uLinesPerItem = 2;

    CConsoleWnd::Init(l_cCfg);

    CConsoleWnd::PushHelpString(TM(" * Enter - enable/disable counter remotely"), IConsole::eBlack, IConsole::eLightGreen);

    Bk::stStorageInfo l_stStorInfo = {};
    m_iReader->Get_Info(&l_stStorInfo);

    SetCaption(l_stStorInfo.pStream_Name);

    if (Bk::eOk == m_iReader->Query_Interface(Bk::eInterfaceTelemetryReader, (void*&)m_iTelemetry))
    {
        Bk::stTelemetryInfo l_stInfo = {};
        m_iTelemetry->Get_Info(&l_stInfo);
        m_wCount = l_stInfo.wCounters;
        this->SetItemsCount(m_wCount);
    }
    else
    {
        m_bError = FALSE;
    }

    m_szOffsetY = 0;
}             

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CViewTelemetry::~CViewTelemetry()
{
    SAFE_RELEASE(m_iTelemetry);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTelemetry::Print()
{
    size_t l_szCount = 0;
    tBOOL  l_bError  = FALSE;

    if (m_bHelp)
    {
        return m_uH;
    }

    Bk::stTelemetryInfo l_stInfo = {};
    m_iTelemetry->Get_Info(&l_stInfo);
    m_wCount = l_stInfo.wCounters;
    this->SetItemsCount(m_wCount);

    while (    (l_szCount < m_szItemsPerWnd)
            && ((tUINT64)l_szCount < m_qwItemsCount)
            && (FALSE == l_bError)
            )
    {
        Bk::stTelemetryCounter l_stCounter = {};
        IConsole::eColor       l_eFg       = m_eItemFgColor;
        IConsole::eColor       l_eBg       = m_eItemBgColor;

        if (l_szCount == m_szOffsetY)
        {
            l_eFg = m_eItemSlFgColor;
            l_eBg = m_eItemSlBgColor;
        }

        m_iTelemetry->Get_Counter((tUINT8)(m_qwOffsetWnd + l_szCount), &l_stCounter, Bk::eTelemetryAllValues);

        m_pConsole->WriteTextO(l_eFg, l_eBg, m_uW, m_uW, m_szOffsetX,
                               TM("Name: {%-48s} On=%d, Samples=%lld"),
                               l_stCounter.pName,
                               (tUINT32)l_stCounter.bOn,
                               l_stCounter.qwSamples
                               );

        m_pConsole->WriteTextO(l_eFg, l_eBg, m_uW, m_uW, m_szOffsetX,
                               TM("Min:%-20f Max:%-20f Avg:%-20f"),
                               l_stCounter.dbMin_Real,
                               l_stCounter.dbMax_Real,
                               l_stCounter.dbAvg_Real
                               );

        l_szCount ++;
    }

    return l_szCount * m_szLinesPerItem;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tBOOL CViewTelemetry::ProcessKey(tUINT16 i_xChar, tBOOL &o_rExit)
{
    if (m_bError)
    {
        return FALSE;
    }

    if (m_bHelp)
    {
        return CConsoleWnd::ProcessKey(i_xChar);
    }

    tBOOL l_bReturn = FALSE;

    if ((tUINT16)IConsole::eKeyEnter == i_xChar)
    {
        tUINT64 l_qwIndex = m_qwOffsetWnd + m_szOffsetY;

        if (m_iExtra)
        {
            Bk::stTelemetryCounter l_stCounter = {};
            CProperty             *l_pRoot     = NULL;

            m_iTelemetry->Get_Counter((tUINT8)(l_qwIndex), &l_stCounter, Bk::eTelemetryStaticValue);

            m_iExtra->Get_Root(l_pRoot);
            if (l_pRoot)
            {
                CProperty *l_pProp = NULL;
                l_pRoot->Get_SubItem(l_stCounter.pName, l_pProp);
                if (l_pProp)
                {
                    tINT64 l_iVal = 0;
                    l_pProp->Get_Value(l_iVal);
                    l_pProp->Set_Value((l_iVal) ? 0ll : 1ll);
                    l_pProp->Release();
                    l_pProp = NULL;
                }

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

        l_bReturn = TRUE;
    }
    else if ((tUINT16)IConsole::eKeyEsc == i_xChar)
    {
        o_rExit   = TRUE;
        l_bReturn = TRUE;
    }
    else
    {
        l_bReturn = CConsoleWnd::ProcessKey(i_xChar);
    }

    return l_bReturn;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Bk::eResult CViewTelemetry::Show()
{
    tUINT32          l_dwWait  = 100;
    IConsole::sInput l_stInput = {};
    tBOOL            l_bExit   = FALSE;
    tUINT32          l_uiTick  = GetTickCount();

    while (!l_bExit)
    {
        if (    (m_bRealTime)
             && (250 <= CTicks::Difference(GetTickCount(), l_uiTick))
           )
        {
            Redraw();
            l_uiTick = GetTickCount();
        }

        if (m_pConsole->GetInput(l_stInput))
        {
            if (    (IConsole::eKey == l_stInput.eEvent)
                 && (l_stInput.stKey.bDown)
               )
            {
                while (l_stInput.stKey.wCount--)
                {
                    ProcessKey(l_stInput.stKey.wKey, l_bExit);
                }
            }
            else if (IConsole::eSize == l_stInput.eEvent)
            {
                //to resize ...
            }

            l_dwWait = 0;
        }
        else
        {
            l_dwWait = 10;
        }

        if (l_dwWait)
        {
            CThShell::Sleep(l_dwWait);
        }
    }

    return Bk::eOk;
}
