#include <sys/time.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include "Jikan.h"
#include "Jikan-config.h"

#ifdef INCLUDE_OPEN_MP
    #include "omp.h"
#endif

using namespace std;

void Jikan::cuda_Jikan_start(const string& name)
{
#ifdef INCLUDE_OPEN_MP
    #pragma omp master
    {
#endif
    bool ExistFlag;
    ExistFlag = this->ifContains(name);

    if(ExistFlag == false)
    {
        (this->Events)[name] = EventData(name);
        this->EventType["CUDA events"].insert(name);
        (this->Events)[name].ifCUDA = 1;

        cudaEventCreate(&(((this->Events)[name]).gpu_start));
        cudaEventCreate(&(((this->Events)[name]).gpu_end));
    }
    
    (this->Events)[name].ifStart = true;
	cudaEventRecord ((this->Events)[name].gpu_start);
#ifdef INCLUDE_OPEN_MP
    }
#endif
}

void Jikan::cuda_Jikan_end(const string& name)
{
#ifdef INCLUDE_OPEN_MP
    #pragma omp master
    {
#endif
    bool ExistFlag, ifStart;
    ExistFlag = this->ifContains(name);
    ifStart = (this->Events)[name].ifStart;

    if((!ExistFlag) || (!ifStart))
        return;

    float GPUtime = 0.0;

    cudaEventRecord((this->Events)[name].gpu_end);
	cudaEventSynchronize((this->Events)[name].gpu_end);
    cudaEventElapsedTime(&GPUtime, (this->Events)[name].gpu_start, (this->Events)[name].gpu_end); //milliseconds 

    GPUtime *= 1e-3;
    (this->Events)[name].elapsed_time += GPUtime;
    (this->Events)[name].count ++;

#ifdef SAVE_TIME_SERIES
    (this->Events)[name].time_series.push_back(GPUtime);
#endif

    (this->Events)[name].ifStart = false;

#ifdef INCLUDE_OPEN_MP
    }
#endif
}

void Jikan::FreeCudaEvents()
{
#ifdef INCLUDE_OPEN_MP
    #pragma omp master
    {
#endif

    map<string, EventData>::iterator it;
    for (it = this->Events.begin(); it!=this->Events.end(); ++it)
    {
        if((it->second).ifCUDA == 1)
        { 
            cudaEventDestroy((it->second).gpu_start);
            cudaEventDestroy((it->second).gpu_end);
        }
    }

#ifdef INCLUDE_OPEN_MP
    }
#endif
}