#include "Jikan.h"
#include "JikanWrapper.h"

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

#ifdef INCLUDE_MPI
    #include "mpi.h"
#endif

using namespace std;

extern "C"
{
    extern class Jikan Timer;

    void TimerStart(const char* name)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif
        
        string str_name = name;

        Timer.Jikan_start(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

    void TimerEnd(const char* name)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif
        
        string str_name = name;

        Timer.Jikan_end(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

#ifdef INCLUDE_MPI
    void MPI_SyncTimerStart(const char* name)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif
        
        string str_name = name;

        int init_flag, fin_flag;

        MPI_Initialized(&init_flag);
        MPI_Finalized(&fin_flag);

        if((!fin_flag) && init_flag)
            MPI_Barrier(MPI_COMM_WORLD);

        Timer.Jikan_sync_start(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

    void MPI_SyncTimerEnd(const char* name)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif
        string str_name = name;

        int init_flag, fin_flag;

        MPI_Initialized(&init_flag);
        MPI_Finalized(&fin_flag);

        if((!fin_flag) && init_flag)
            MPI_Barrier(MPI_COMM_WORLD);

        Timer.Jikan_sync_end(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

    void MPI_SyncTimerStartCustom(const char* name, MPI_Comm comm)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif
        string str_name = name;

        int init_flag, fin_flag;

        MPI_Initialized(&init_flag);
        MPI_Finalized(&fin_flag);

        if((!fin_flag) && init_flag)
            MPI_Barrier(MPI_COMM_WORLD);

        Timer.Jikan_sync_start(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

    void MPI_SyncTimerEndCustom(const char* name, MPI_Comm comm)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif        
        string str_name = name;

        int init_flag, fin_flag;

        MPI_Initialized(&init_flag);
        MPI_Finalized(&fin_flag);

        if((!fin_flag) && init_flag)
            MPI_Barrier(comm);

        Timer.Jikan_sync_end(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

    void WriteOutputCustom(MPI_Comm comm, int id)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif

        bool ifWrite = Timer.ifWriteProc(comm, id);
        if(ifWrite == true)
            Timer.GenerateOutputData();

        #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

    void WriteOutput(int id)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif

        bool ifWrite = Timer.ifWriteProc(MPI_COMM_WORLD, id);
        // cout << ifWrite << endl;
        if(ifWrite == true)
            Timer.GenerateOutputData();

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }

#else
    void WriteOutput()
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif
    
        bool ifWrite = Timer.ifWriteProc();
        if(ifWrite == true)
            Timer.GenerateOutputData();

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }
#endif

#ifdef INCLUDE_OPEN_MP
    void OpenMP_SyncTimerStart(const char* name)
    {
        #pragma omp barrier
        #pragma omp master
        {
            string str_name = name;

            Timer.Jikan_sync_start(str_name);
        }
    }

    void OpenMP_SyncTimerEnd(const char* name)
    {
        #pragma omp barrier
        #pragma omp master
        {
            string str_name = name;

            Timer.Jikan_sync_end(str_name);
        }
    }
#endif
//--------

#ifdef INCLUDE_GPU_TIMER
    void CudaTimerStart(const char* name)
    {
    #ifdef INCLUDE_OPEN_MP
        #pragma omp master
        {
    #endif

        string str_name = name;

        Timer.cuda_Jikan_start(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }
    
    void CudaTimerEnd(const char* name)
    {
    #ifdef INCLUDE_OPEN_MP

        #pragma omp master
        {
    #endif
        string str_name = name;

        Timer.cuda_Jikan_end(str_name);

    #ifdef INCLUDE_OPEN_MP
        }
    #endif
    }
#endif
}