/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "cm_perf_statistics.h"
#include "cm_mem.h"
#include "cm_sdk_provider.h"

#if MDF_PROFILER_ENABLED

CmPerfStatistics::CmPerfStatistics()
{

    m_apiCallRecordCount  = 0;
    m_apiCallFile         = nullptr;

    m_perfStatisticFile   = nullptr;
    m_perfStatisticCount = 0;

    m_profilerOn      = false;
    m_profilerLevel    = CM_RT_PERF_LOG_LEVEL_DEFAULT;

    GetProfilerLevel(); // get profiler level from env variable "CM_RT_PERF_LOG"

    if(m_profilerLevel >= CM_RT_PERF_LOG_LEVEL_ETW)
    {   // Register 'MDF Provider' in System
        EventRegisterMDF_PROVIDER();
    }

}

CmPerfStatistics::~CmPerfStatistics()
{
    DumpApiCallRecords();

    DumpPerfStatisticRecords();
}

void CmPerfStatistics::GetProfilerLevel()
{   // Enabled Profiler in Debug Mode
    m_profilerLevel = CM_RT_PERF_LOG_LEVEL_RECORDS;
    m_profilerOn   = true;
    return;
}

//! Insert API Call Records Into m_apiCallRecords Array and Update Perf Statistic Array accordingly
void CmPerfStatistics::InsertApiCallRecord(char *functionName, float time, LARGE_INTEGER start, LARGE_INTEGER end)
{
    CLock locker(m_criticalSectionOnApiCallRecords);

    ApiCallRecord *records = new ApiCallRecord ;

    records->startTime = start;
    records->endTime   = end;
    records->duration   = time;

    CM_STRCPY(records->functionName, MSG_STRING_SIZE, functionName);

    m_apiCallRecords.push_back(records);
    m_apiCallRecordCount++;

    InsertPerfStatistic(records);

}

//Update Perf Statistic Array
void CmPerfStatistics::InsertPerfStatistic(ApiCallRecord *record)
{
    uint32_t index = 0;

    for( index = 0 ; index < m_perfStatisticCount ; index ++)
    {
        ApiPerfStatistic *perfStatisticRecords = m_perfStatisticRecords[index];
        if(perfStatisticRecords != nullptr)
        {
            if(!strcmp(record->functionName, perfStatisticRecords->functionName))
            { // existing
               perfStatisticRecords->callTimes ++ ;
               perfStatisticRecords->time += record->duration;

               //Update statistic records
               m_perfStatisticRecords[index] = perfStatisticRecords;

               break;
            }
        }
    }

    if(index == m_perfStatisticCount)
    { // record does not exist, create new entry
        ApiPerfStatistic *perfStatisticRecords = new ApiPerfStatistic;
        CM_STRCPY(perfStatisticRecords->functionName, MSG_STRING_SIZE, record->functionName);

        perfStatisticRecords->callTimes = 1;
        perfStatisticRecords->time       = record->duration;

        m_perfStatisticRecords.push_back(perfStatisticRecords);
        m_perfStatisticCount ++;
    }

    return ;
}

//Dump APICall Records and Release m_apiCallRecords Array
void CmPerfStatistics::DumpApiCallRecords()
{
    if(!m_profilerOn)
    {
        return ;
    }

    CM_FOPEN(m_apiCallFile, "CmPerfLog.csv", "wb");
    if(! m_apiCallFile )
    {
        fprintf(stdout, "Fail to create file CmPerfLog.csv \n ");
        return ;
    }
    fprintf(m_apiCallFile,  "%-40s %s \t %s \t %s \n", "FunctionName", "StartTime", "EndTime", "Duration");

    for(uint32_t i=0 ; i< m_apiCallRecordCount ; i++)
    {
        ApiCallRecord *records = m_apiCallRecords[i];

        fprintf(m_apiCallFile,  "%-40s  %lld \t %lld \t %fms \n", records->functionName,
           records->startTime.QuadPart, records->endTime.QuadPart, records->duration);

        CmSafeRelease(records);
    }

    m_apiCallRecords.clear();

    fclose(m_apiCallFile);

}

//Dump Perf Statistic Records and Release m_perfStatisticRecords Array
void CmPerfStatistics::DumpPerfStatisticRecords()
{
    if(!m_profilerOn)
    {
        return ;
    }

    CM_FOPEN(m_perfStatisticFile, "CmPerfStatistics.txt","wb");
    if(!m_perfStatisticFile )
    {
        fprintf(stdout, "Fail to create file CmPerfStatistics.txt \n ");
        return ;
    }
    fprintf(m_perfStatisticFile,  "%-40s %s \t %s \n", "FunctionName", "Total Time(ms)", "Called Times");

    for(uint32_t i=0 ; i< m_perfStatisticCount; i++)
    {
        ApiPerfStatistic *perfStatisticRecords = m_perfStatisticRecords[i];

        fprintf(m_perfStatisticFile,  "%-40s %fms \t %d \n", perfStatisticRecords->functionName,
           perfStatisticRecords->time, perfStatisticRecords->callTimes);

        CmSafeRelease(perfStatisticRecords);
    }

    m_perfStatisticRecords.clear();

    fclose(m_perfStatisticFile);

}

#endif
