#include "profiling.h"

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#ifdef SHOW_PERF
uint64_t tprof[LIM_THREAD_PROF_TYPE][LIM_THREAD] = {0};
uint64_t proc_freq = 1000;
uint64_t gprof[LIM_GLOBAL_PROF_TYPE] = {0};
#endif

uint64_t RealtimeMsec(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (uint64_t)1000 * (tv.tv_sec + ((1e-6) * tv.tv_usec));
}

static int CalcThreadTime(uint64_t *a, int len, double *max, double *min, double *avg) {
#ifdef SHOW_PERF
    int i = 0;
    uint64_t umax = 0, umin = UINT64_MAX, uavg = 0;
    for (i = 0; i < len; i++) {
        if (a[i] > umax)
            umax = a[i];
        if (a[i] < umin)
            umin = a[i];
        uavg += a[i];
    }
    *avg = uavg * 1.0 / len / proc_freq;
    *max = umax * 1.0 / proc_freq;
    *min = umin * 1.0 / proc_freq;
#endif
    return 1;
}

#define PRINT_GP(gpname) \
    fprintf(stderr, "time %-15s:     %0.2lfs\n", #gpname, gprof[gpname] * 1.0 / proc_freq);

#define PRINT_TP(tpname)                                                                                  \
    {                                                                                                              \
        double maxTime, minTime, avgTime;                                                                          \
        CalcThreadTime(tprof[tpname], nthread, &maxTime, &minTime, &avgTime);                                      \
        fprintf(stderr, "time %-15s:  avg %0.2lfs min %0.2lfs max %0.2lfs\n", #tpname, avgTime, minTime, maxTime); \
    }

int DisplayProfilingBQSR(int nthread) {
#ifdef SHOW_PERF
    fprintf(stderr, "\n");
    PRINT_GP(GP_read);
    if (nthread == 1) {
        PRINT_GP(GP_clip_read);
        PRINT_GP(GP_read_ref);
        PRINT_GP(GP_calc_snp);
        PRINT_GP(GP_read_vcf);
        PRINT_TP(TP_read_vcf);
        PRINT_TP(TP_calc_skips);
        PRINT_GP(GP_covariate);
        PRINT_TP(TP_readgroup);
        PRINT_TP(TP_qualityscore);
        PRINT_TP(TP_context);
        PRINT_TP(TP_cycle);
        PRINT_GP(GP_frac_err);
        PRINT_GP(GP_update_info);
    } else {
        PRINT_TP(TP_clip_read);
        PRINT_TP(TP_read_ref);
        PRINT_TP(TP_calc_snp);
        PRINT_TP(TP_read_vcf);
        PRINT_TP(TP_calc_skips);
        PRINT_TP(TP_covariate);
        PRINT_TP(TP_readgroup);
        PRINT_TP(TP_qualityscore);
        PRINT_TP(TP_context);
        PRINT_TP(TP_cycle);
        PRINT_TP(TP_frac_err);
        PRINT_TP(TP_update_info);
    }
    PRINT_GP(GP_whole_process);

    fprintf(stderr, "\n");
#endif

    return 0;
}

int DisplayProfilingApplyBQSR(int nthread) {
#ifdef SHOW_PERF
    fprintf(stderr, "\n");
    PRINT_GP(GP_read);
    if (nthread == 1) {
        PRINT_GP(GP_covariate);
    } else {
        PRINT_TP(TP_covariate);
        PRINT_TP(TP_readgroup);
        PRINT_TP(TP_qualityscore);
        PRINT_TP(TP_context);
        PRINT_TP(TP_cycle);
    }
    PRINT_GP(GP_whole_process);

    fprintf(stderr, "\n");
#endif

    return 0;
}