#pragma once

#include "Jikan.h"
#include "Event.h"

#include <iostream>
#include <string>
#include <set>

#ifdef INCLUDE_MPI
    #include <mpi.h>
#endif

class Jikan
{
public:
    std::map<std::string, EventData> Events;   // Measuring code blocks info: string = code block name ID, EventData = measuring code block data
    std::map<std::string, std::set<std::string> > EventType; // Code blocks measurment types: synchronious or ansynchronious 
    std::string JSONname; // Measurments dump filename
    std::string JSONdata; // Dump string
    std::string Error;    // Dump error ?

    Jikan();
    ~Jikan();

    void JikanStart(const std::string& name, const int& mode); // Start code block measurments
    void JikanEnd(  const std::string& name); // End code block measurments

#ifdef INCLUDE_GPU_TIMER
    void StartCUDASync(const std::string& name); // Start CUDA synchronization
    void EndCUDASync(  const std::string& name); // End CUDA synchronization
#endif

    void GenerateTypedOutputData(const std::string& EventType, std::string& EventTypeString);
    void GenerateOutputData();

// private:
    bool ifContains(const std::string& name); // Check if current code block measurments are already exist
    
#ifdef INCLUDE_MPI
    bool ifWriteProc(MPI_Comm comm, int id); // Check if current MPI proc ID == id
#endif

    bool ifWriteProc();
    void SetDumpFilename(const std::string& name); // Change dump filename to <name>

private:
    void BarrierMPI(); // Start MPI barrier
    void StartEvent(const std::string& name); 
    void EndEvent(const std::string& name);

#ifdef INCLUDE_GPU_TIMER
    void CUDAGetTime(const std::string& name);
#endif
};