////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                     /
// 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.                   /
//                                                                                                                     /
// 2012-2021 (c) Baical                                                                                                /
//                                                                                                                     /
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "common.h"


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////uP7 callbacks/////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint64_t t3getTimerFrequency(void *i_pCtx)      { UNUSED_ARG(i_pCtx); return GetPerformanceFrequency(); }
uint64_t t3getTimerValue(void *i_pCtx)          { UNUSED_ARG(i_pCtx); return GetPerformanceCounter();   }
uint32_t t3getCurrentThreadId(void *i_pCtx)     { UNUSED_ARG(i_pCtx); return CProc::Get_Thread_Id();    }
bool     t3sendPacket(void *i_pCtx, enum euP7packetRank i_eRank, const struct stP7packetChunk *i_pChunks,
                      size_t  i_szChunks, size_t i_szData)
{
    UNUSED_ARG(i_eRank);
    IuP7Fifo  *l_pFifo = static_cast<IuP7Fifo  *>(i_pCtx);
    return l_pFifo->Write((IuP7Fifo::stChunk*)i_pChunks, i_szChunks, i_szData);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool t3Performance(const tXCHAR *i_pSessionFolder)
{
    const uint8_t l_bCpuId = 1;
    IuP7proxy    *l_iProxy = uP7createProxy(TM("/P7.Verb=4 /P7.Sink=FileBin /P7.Files=2 /P7.Dir=. /P7.Pool=4096"), i_pSessionFolder);
    IuP7Fifo     *l_pFifo  = NULL;

    if (!l_iProxy)
    {
        return false;
    }

    if (!l_iProxy->RegisterCpu(l_bCpuId, false, GetPerformanceFrequency(), TM("t2Format"), 4*1024*1024, true, l_pFifo))
    {
        return false;
    }


    struct stuP7config l_stConfig; 

    l_stConfig.bSessionIdCrc7        = g_bCrc7;               /**< Session ID crc */ 
    l_stConfig.uSessionId            = g_uSessionId;          /**< Session ID*/  
    l_stConfig.pCtxTimer             = NULL;                  /**< Context for timer functions */
    l_stConfig.cbTimerFrequency      = t3getTimerFrequency;   /**< callback to get system high-resolution timer frequency */
    l_stConfig.cbTimerValue          = t3getTimerValue;       /**< callback to get system high-resolution timer value */
    l_stConfig.pCtxLock              = NULL;                  /**< Context for lock functions, set it to NULL if there is no RTOS*/
    l_stConfig.cbLock                = NULL;                  /**< callback to call OS lock function, set it to NULL if there is no RTOS */
    l_stConfig.cbUnLock              = NULL;                  /**< callback to call OS unlock function, set it to NULL if there is no RTOS */
    l_stConfig.pCtxPacket            = l_pFifo;               /**< Context for data sending */
    l_stConfig.cbSendPacket          = t3sendPacket;          /**< callback to send data packet to host */  
    l_stConfig.pCtxThread            = 0;                     /**< Context for cbGetCurrentThreadId function */
    l_stConfig.cbGetCurrentThreadId  = t3getCurrentThreadId;  /**< callback get current thread ID, set it to NULL if there is no RTOS*/  
    l_stConfig.eDefaultVerbosity     = euP7Level_Trace;       /**< Default verbosity*/  

    //Generated descriptors, located in uP7preprocessed.h
    l_stConfig.pModules              = g_pModules;            /**< Trace modules descriptors, generated by uP7 preprocessor*/
    l_stConfig.szModules             = g_szModules;           /**< Trace modules descriptors count*/  
    l_stConfig.pTraces               = g_pTraces;             /**< Trace descriptors, generated by uP7 preprocessor*/  
    l_stConfig.szTraces              = g_szTraces;            /**< Trace descriptors count*/  
    l_stConfig.pTelemetry            = g_pTelemetry;          /**< Telemetry descriptors, generated by uP7 preprocessor*/  
    l_stConfig.szTelemetry           = g_szTelemetry;         /**< Telemetry descriptors count*/  
    l_stConfig.eAlignment            = euP7alignment4;

    if (!uP7initialize(&l_stConfig))
    {
        return false; //we don't care about memory leak! test failed application will be closed
    }

    huP7Module l_hModule1 = uP7_MODULE_INVALID_ID;
    huP7TelId  l_hCounter1 = uP7_TELEMETRY_INVALID_ID;
    if (    (!uP7TelCreateCounter("t3 Counter", 0, 0, 1, 1, true, &l_hCounter1))
         || (!uP7TrcRegisterModule("t3", euP7Level_Trace, &l_hModule1))
       )
    {
        return false;
    }
    
    volatile bool l_bExit = false;
    std::thread l_hThread = std::thread([&] 
    {
        stReadContext l_stReadCtx = {0};
        l_stReadCtx.pFifo = l_pFifo;

        while (!l_bExit)
        {
            uP7ProcessIncomingData(&l_stReadCtx, 10);
        }
    });

    auto start = std::chrono::system_clock::now();
    uint32_t uSent = 0;
    uint32_t uLost = 0;

    for (uint32_t l_uI = 0; l_uI < 20'000'000; l_uI ++)
    {
        if (uP7CRT(47, l_hModule1, "Trace test 0x%X" , l_uI))
        {
            uSent++;
        }
        else
        {
            uLost++;
        }
    }

    auto delta   = std::chrono::system_clock::now() - start;
    auto delta_d = std::chrono::duration_cast<std::chrono::duration<double>> (delta).count();
    printf("Trace test result: time:%.02f sec, sent = %u, lost: %u, per second:%.01f\n", delta_d, uSent, uLost, (double)uSent/delta_d);

    start = std::chrono::system_clock::now();
    uSent = 0;
    uLost = 0;

    for (uint32_t l_uI = 0; l_uI < 20'000'000; l_uI ++)
    {
        if (uP7TelSentSample(l_hCounter1, 0.0))
        {
            uSent++;
        }
        else
        {
            uLost++;
        }
    }

    delta   = std::chrono::system_clock::now() - start;
    delta_d = std::chrono::duration_cast<std::chrono::duration<double>> (delta).count();
    printf("Telemetry test result: time:%.02f sec, sent = %u, lost: %u, per second:%.01f\n", delta_d, uSent, uLost, (double)uSent/delta_d);

    std::this_thread::sleep_for(std::chrono::seconds(2));

    l_bExit = true;
    l_hThread.join();

    uP7unInitialize();

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

    if (l_iProxy)
    {
        l_iProxy->UnRegisterCpu(l_bCpuId);
        l_iProxy->Release();
        l_iProxy = NULL;
    }

    return true;
}
