////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 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 "CliTrace.h"

static const GUID g_sPlugin_GUID = { 0xC6206BEB, 0xC4FA, 0xB267, 
                                     { 0x15, 0xAA, 0xB3, 0xB2,
                                       0xB4, 0xAC, 0x31, 0x6E
                                     }
                                   };


#define TIME_HRS_100NS                                            36000000000ull
#define TIME_MIN_100NS                                              600000000ull
#define TIME_SEC_100NS                                               10000000ull
#define TIME_MSC_100NS                                                  10000ull

#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 Trace (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_TRACE;

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

    return l_eReturn;
}//Is_Stream_Supported

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


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

    CViewTrace *l_pVTrace = new CViewTrace((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




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CViewTrace::CViewTrace(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_iTrace(NULL)
    , m_pNode(i_pNode)
    , m_iExtra(i_iExtra)
    , m_pProp(i_pProp)
    , m_pEnum(i_pEnum)
    , m_eTimeFormat(eTimeMedium)
    , m_qwStreamTime(0ull)
    , m_bFollowLast(TRUE)
{
    m_pTimeFormatters[eTimeFull]   = &FormatTimeFull;
    m_pTimeFormatters[eTimeMedium] = &FormatTimeMedium;
    m_pTimeFormatters[eTimeShort]  = &FormatTimeShort;
    m_pTimeFormatters[eTimeOffset] = &FormatTimeDiff;
    m_pTimeFormatters[eTimeDiff]   = &FormatTimeDiff;

    m_pLevel[EP7TRACE_LEVEL_TRACE]    = TM("TRACE");
    m_pLevel[EP7TRACE_LEVEL_DEBUG]    = TM("DEBUG");
    m_pLevel[EP7TRACE_LEVEL_INFO]     = TM("INFO ");
    m_pLevel[EP7TRACE_LEVEL_WARNING]  = TM("WARN.");
    m_pLevel[EP7TRACE_LEVEL_ERROR]    = TM("ERROR");
    m_pLevel[EP7TRACE_LEVEL_CRITICAL] = TM("CRIT.");

    m_pColors[EP7TRACE_LEVEL_TRACE].eBg    = IConsole::eWhite;
    m_pColors[EP7TRACE_LEVEL_TRACE].eFg    = IConsole::eBlack;   
    m_pColors[EP7TRACE_LEVEL_DEBUG].eBg    = IConsole::eLightGreen;   
    m_pColors[EP7TRACE_LEVEL_DEBUG].eFg    = IConsole::eBlack;   
    m_pColors[EP7TRACE_LEVEL_INFO].eBg     = IConsole::eLightCyan;    
    m_pColors[EP7TRACE_LEVEL_INFO].eFg     = IConsole::eBlack;    
    m_pColors[EP7TRACE_LEVEL_WARNING].eBg  = IConsole::eLightMagenta; 
    m_pColors[EP7TRACE_LEVEL_WARNING].eFg  = IConsole::eBlack; 
    m_pColors[EP7TRACE_LEVEL_ERROR].eBg    = IConsole::eLightRed;   
    m_pColors[EP7TRACE_LEVEL_ERROR].eFg    = IConsole::eBlack;   
    m_pColors[EP7TRACE_LEVEL_CRITICAL].eBg = IConsole::eRed;
    m_pColors[EP7TRACE_LEVEL_CRITICAL].eFg = IConsole::eBlack;

    m_pTimeFormat[eTimeFull]   = TM(" Time(full)                  |"); //11.02.2018 20:54:04.575'184"4 - 30 chars (full)
    m_pTimeFormat[eTimeMedium] = TM(" Time(medium)     |");            //20:54:04.575'184"4            - 19 chars (medium)
    m_pTimeFormat[eTimeShort]  = TM(" Time(small)   |");               //54:04.575'184"4               - 16 chars (small)
    m_pTimeFormat[eTimeOffset] = TM(" Time(offset)    |");             //+00:00:00.080'184"4           - 20 chars (offset+diff)
    m_pTimeFormat[eTimeDiff]   = TM(" Time(diff)      |");

    m_szText = 16384;
    m_pText = (tXCHAR*)malloc(m_szText * sizeof(tXCHAR));

    m_szMessage = 16384;
    m_pMessage  = (tXCHAR*)malloc(m_szMessage * sizeof(tXCHAR));

    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("Trace");
    l_cCfg.eWindowBgColor= IConsole::eBlack;
    l_cCfg.eItemBgColor  = IConsole::eBlack;
    l_cCfg.eItemFgColor  = IConsole::eGreen;
    l_cCfg.eItemSlFgColor= IConsole::eWhite;
    l_cCfg.eItemSlBgColor= IConsole::eBlue;

    l_cCfg.eItemClFgColor= IConsole::eBlack;
    l_cCfg.eItemClBgColor= IConsole::eLightGray;

    l_cCfg.uLinesPerItem = 1;

    CConsoleWnd::Init(l_cCfg);

    CConsoleWnd::PushColumn(eIndex, TRUE, TM(" #Idx |"));
    CConsoleWnd::PushColumn(eId, FALSE, TM(" Id  |"));
    CConsoleWnd::PushColumn(eLevel, FALSE, TM("Level|"));
    CConsoleWnd::PushColumn(eModule, FALSE, TM(" Module |"));
    CConsoleWnd::PushColumn(eProcessor, FALSE, TM("CPU|"));
    CConsoleWnd::PushColumn(eThread, FALSE, TM(" Thread |"));
    CConsoleWnd::PushColumn(eFile, TRUE, TM(" File           |"));
    CConsoleWnd::PushColumn(eLine, TRUE, TM("Line|"));
    CConsoleWnd::PushColumn(eFunction, FALSE, TM(" Function       |"));
    CConsoleWnd::PushColumn(eTime, TRUE, m_pTimeFormat[m_eTimeFormat]);
    CConsoleWnd::PushColumn(eText, TRUE, TM(" Text"));

    CConsoleWnd::EnableColumns(TRUE);

    CConsoleWnd::PushHelpString(TM(" * 0-9 - show/hide columns"), IConsole::eBlack, IConsole::eLightGreen);
    CConsoleWnd::PushHelpString(TM(" * t - time format (full-medium-short-offset-diff)"), IConsole::eBlack, IConsole::eLightGreen);
    CConsoleWnd::PushHelpString(TM(" * l/end - toggle on \"follow last\" mode"), IConsole::eBlack, IConsole::eLightGreen);


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

    m_qwStreamTime = (tUINT64)l_stStorInfo.sStream_Time.dwLowDateTime + (((tUINT64)l_stStorInfo.sStream_Time.dwHighDateTime) << 32);

    SetCaption(l_stStorInfo.pStream_Name);

    if (Bk::eOk == m_iReader->Query_Interface(Bk::eInterfaceTraceReader, (void*&)m_iTrace))
    {
        tUINT64 l_qwCount = 0ull;
        m_iTrace->Get_Count(&l_qwCount);
        this->SetItemsCount(l_qwCount);
    }
    else
    {
        m_bError = FALSE;
    }

    m_szOffsetY = 0;
    m_bFollowLast = TRUE;
}             

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CViewTrace::~CViewTrace()
{
    SAFE_RELEASE(m_iTrace);

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

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


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTrace::Print()
{
    size_t      l_szCount      = 0;
    tBOOL       l_bError       = FALSE;
    Bk::stTrace l_stTrace      = {};
    size_t      l_szOffset     = 0;
    stTime      l_szTime       = {};
    tUINT64     l_qwTimeOffset = 0ull;


    if (m_bHelp)
    {
        return m_uH;
    }

    tUINT64 l_qwCount = 0ull;
    m_iTrace->Get_Count(&l_qwCount);
    this->SetItemsCount(l_qwCount); 

    l_stTrace.pMessage = m_pMessage;
    l_stTrace.szMessage = m_szMessage;

    if (m_bFollowLast)
    {
        m_szOffsetY = 0;

        if (l_qwCount > m_szItemsPerWnd)
        {
            m_qwOffsetWnd = l_qwCount - m_szItemsPerWnd;
            m_szOffsetY   = m_szItemsPerWnd - 1;
        }
        else
        {
            m_qwOffsetWnd = 0ull;
            if (l_qwCount)
            {
                m_szOffsetY = (size_t)l_qwCount - 1;
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //print items
    while (    (l_szCount < m_szItemsPerWnd)
            && ((tUINT64)l_szCount < m_qwItemsCount)
            && (FALSE == l_bError)
            )
    {
        if (Bk::eOk != m_iTrace->Get_Trace(m_qwOffsetWnd + (tUINT64)l_szCount, &l_stTrace, TRUE))
        {
            l_bError = TRUE;
            break;
        }

        IConsole::eColor       l_eFg     = m_pColors[l_stTrace.eLevel].eFg;
        IConsole::eColor       l_eBg     = m_pColors[l_stTrace.eLevel].eBg;
        CConsoleWnd::stColumn *l_pColumn = NULL;

        l_szOffset = 0;

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


        l_pColumn = GetColumn(eIndex);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%d"), 
                                     l_stTrace.dwSequence
                                    );
        }

        l_pColumn = GetColumn(eId);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%d"), 
                                     l_stTrace.wID
                                    );
        }

        l_pColumn = GetColumn(eLevel);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%s"), 
                                     m_pLevel[l_stTrace.eLevel]
                                    );
        }

        l_pColumn = GetColumn(eModule);
        if (l_pColumn->bVisible)
        {
        #if defined(UTF8_ENCODING)
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%s/%d"), 
                                     l_stTrace.pModuleName ? l_stTrace.pModuleName : "-",
                                     l_stTrace.dwModuleID
                                     );
        #else
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%S/%d"), 
                                     l_stTrace.pModuleName ? l_stTrace.pModuleName : "-",
                                     l_stTrace.dwModuleID
                                     );
        #endif
        }

        l_pColumn = GetColumn(eProcessor);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%d"), 
                                     (tUINT32)l_stTrace.bProcessor
                                    );
        }

        l_pColumn = GetColumn(eThread);
        if (l_pColumn->bVisible)
        {
        #if defined(UTF8_ENCODING)
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%s/%d"), 
                                     l_stTrace.pThreadName ? l_stTrace.pThreadName : "-",
                                     l_stTrace.dwThreadID
                                     );
        #else
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%S/%d"), 
                                     l_stTrace.pThreadName ? l_stTrace.pThreadName : "-",
                                     l_stTrace.dwThreadID
                                     );
        #endif
        }

        l_pColumn = GetColumn(eFile);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%s"), 
                                     l_stTrace.pFile_Name
                                    );
        }

        l_pColumn = GetColumn(eLine);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%d"), 
                                     (tUINT32)l_stTrace.wLine
                                    );
        }

        l_pColumn = GetColumn(eFunction);
        if (l_pColumn->bVisible)
        {
            l_szOffset += WriteField(l_pColumn->szCaption, 
                                     m_pText + l_szOffset, 
                                     m_szText - l_szOffset, 
                                     TM("%s"), 
                                     l_stTrace.pFunction
                                    );
        }

        l_pColumn = GetColumn(eTime);
        if (l_pColumn->bVisible)
        {
            if (    (eTimeDiff == m_eTimeFormat)
                 && (0 == l_szCount)
               )
            {
                l_qwTimeOffset = l_stTrace.qwTime;
            }

            l_szTime.pBuffer         = m_pText + l_szOffset;
            l_szTime.szBuffer        = m_szText - l_szOffset;
            l_szTime.qwRawTime       = m_qwStreamTime + l_stTrace.qwTime;
            l_szTime.qwRawTimeOffset = l_stTrace.qwTime - l_qwTimeOffset;
            l_szTime.szLengh         = l_pColumn->szCaption;

            UnpackLocalTime(l_szTime.qwRawTime,
                            l_szTime.dwYear,
                            l_szTime.dwMonth,
                            l_szTime.dwDay,
                            l_szTime.dwHour,
                            l_szTime.dwMinutes,
                            l_szTime.dwSeconds,
                            l_szTime.dwMilliseconds,
                            l_szTime.dwMicroseconds,
                            l_szTime.dwNanoseconds
                           );

            if (eTimeDiff == m_eTimeFormat)
            {
                l_qwTimeOffset = l_stTrace.qwTime;
            }

            l_szOffset += m_pTimeFormatters[m_eTimeFormat](l_szTime);
        }

        PStrCpy(m_pText + l_szOffset, m_szText - l_szOffset, l_stTrace.pMessage);
        
        m_pConsole->WriteTextB(l_eFg, l_eBg, 
                               m_uW, m_uW, m_szOffsetX,
                               m_pText, 0, m_szText);
        
        l_szCount ++;
    }

    return l_szCount * m_szLinesPerItem;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
tBOOL CViewTrace::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;
        l_bReturn = TRUE;
    }
    else if (    (TM('0') <= i_xChar)
                && (TM('9') >= i_xChar)
            )
    {
        CConsoleWnd::stColumn *l_pColumn = GetColumn(i_xChar - TM('0'));
        if (l_pColumn)
        {
            CConsoleWnd::EnableColumn(i_xChar - TM('0'), !l_pColumn->bVisible);
        }

        l_bReturn = TRUE;

        Redraw();
    }
    else if (    (TM('l') == i_xChar)
                || (TM('L') == i_xChar)
                || ((tUINT16)IConsole::eKeyEnd == i_xChar)
            )
    {
        m_bFollowLast = TRUE;
        l_bReturn     = TRUE;
        Redraw();
    }
    else if (    ((tUINT16)IConsole::eKeyArrowLeft  == i_xChar)
                || ((tUINT16)IConsole::eKeyArrowRight == i_xChar)
                || ((tUINT16)IConsole::eKeyArrowUp    == i_xChar)
                || ((tUINT16)IConsole::eKeyArrowDown  == i_xChar)
                || ((tUINT16)IConsole::eKeyPageUp     == i_xChar)
                || ((tUINT16)IConsole::eKeyPageDown   == i_xChar)
                || ((tUINT16)IConsole::eKeyHome       == i_xChar)
            )
    {
        m_bFollowLast = FALSE;
        l_bReturn = CConsoleWnd::ProcessKey(i_xChar);
    }
    else if (    (TM('t') == i_xChar)
                || (TM('T') == i_xChar)
            )
    {
        m_eTimeFormat = (eTimeFormat)((int)m_eTimeFormat + 1);
        if (m_eTimeFormat >= eTimeFormatMax)
        {
            m_eTimeFormat = eTimeFull;
        }

        CConsoleWnd::SetColumnCaption(eTime, m_pTimeFormat[m_eTimeFormat]);

        Redraw();

        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 CViewTrace::Show()
{
    tUINT32          l_dwWait  = 100;
    IConsole::sInput l_stInput = {};
    tBOOL            l_bExit   = FALSE;
    tUINT32          l_uiTick  = GetTickCount();

    while (!l_bExit)
    {
        if (    (m_bFollowLast)
             && (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;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTrace::WriteField(size_t i_szLength, tXCHAR *o_pBuffer, size_t  i_szBuffer, const tXCHAR *i_pFormat, ...)
{
    va_list l_pVA;
    size_t  l_szReturn = 0;

    if (3 > i_szLength)
    {
        return 0;
    }

    va_start(l_pVA, i_pFormat);
    int l_iReturn = PVsnprintf(o_pBuffer, i_szBuffer, i_pFormat, l_pVA);
    va_end(l_pVA);

    if (0 >= l_iReturn)
    {
        return 0;
    }

    l_szReturn = (size_t)l_iReturn;

    i_szLength--; //separator

    if (l_szReturn > i_szLength)
    {
        o_pBuffer[i_szLength - 1] = TM('>');
        l_szReturn = i_szLength;
    }
    else if (l_szReturn < i_szLength)
    {
        tXCHAR *l_pIter = o_pBuffer + l_szReturn;

        while (    ((size_t)l_szReturn < i_szLength)
                && ((size_t)l_szReturn <= i_szBuffer)
                )
        {
            *l_pIter = TM(' ');
            l_pIter ++;
            l_szReturn++;
        }
    }

    o_pBuffer[l_szReturn++] = TM('|');

    return l_szReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTrace::FormatTimeFull(const stTime &i_rTime)
{
    return WriteField(i_rTime.szLengh,
                        i_rTime.pBuffer,
                        i_rTime.szBuffer,
                        TM("%02d.%02d.%04d %02d:%02d:%02d.%03d'%03d\"%d"),
                        i_rTime.dwDay,
                        i_rTime.dwMonth,
                        i_rTime.dwYear,
                        i_rTime.dwHour,
                        i_rTime.dwMinutes,
                        i_rTime.dwSeconds,
                        i_rTime.dwMilliseconds,
                        i_rTime.dwMicroseconds,
                        i_rTime.dwNanoseconds
                        );
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTrace::FormatTimeMedium(const stTime &i_rTime)
{
    return WriteField(i_rTime.szLengh,
                        i_rTime.pBuffer,
                        i_rTime.szBuffer,
                        TM("%02d:%02d:%02d.%03d'%03d\"%d"),
                        i_rTime.dwHour,
                        i_rTime.dwMinutes,
                        i_rTime.dwSeconds,
                        i_rTime.dwMilliseconds,
                        i_rTime.dwMicroseconds,
                        i_rTime.dwNanoseconds
                        );
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTrace::FormatTimeShort(const stTime &i_rTime)
{
    return WriteField(i_rTime.szLengh,
                        i_rTime.pBuffer,
                        i_rTime.szBuffer,
                        TM("%02d:%02d.%03d'%03d\"%d"),
                        i_rTime.dwMinutes,
                        i_rTime.dwSeconds,
                        i_rTime.dwMilliseconds,
                        i_rTime.dwMicroseconds,
                        i_rTime.dwNanoseconds
                        );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
size_t CViewTrace::FormatTimeDiff(const stTime &i_rTime)
{
    tUINT64 l_qwTime     = i_rTime.qwRawTimeOffset;
    tUINT64 l_dwReminder = l_qwTime % TIME_MSC_100NS; //micro & 100xNanoseconds
    tUINT64 l_dwNano     = l_qwTime % 10;
    tUINT64 l_dwMicro    = l_dwReminder / 10;

    l_qwTime -= l_dwReminder;

    tUINT64 l_qwSec = l_qwTime / TIME_SEC_100NS;
    tUINT64 l_qwMSc = (l_qwTime - (l_qwSec * TIME_SEC_100NS)) / TIME_MSC_100NS;

    return WriteField(i_rTime.szLengh,
                        i_rTime.pBuffer,
                        i_rTime.szBuffer,
                        TM("+%06d.%03d'%03d\"%d"),
                        (tUINT32)l_qwSec,
                        (tUINT32)l_qwMSc,
                        (tUINT32)l_dwMicro,
                        (tUINT32)l_dwNano
                        );
}
