#ifndef _RDAVG_H_
#define _RDAVG_H_


typedef struct rd_avg_s {
        struct {
                int64_t maxv;
                int64_t minv;
                int64_t avg;
                int64_t sum;
                int     cnt;
                rd_ts_t start;
        } ra_v;
        mtx_t ra_lock;
        enum {
                RD_AVG_GAUGE,
                RD_AVG_COUNTER,
        } ra_type;
} rd_avg_t;


/**
 * Add timestamp 'ts' to averager 'ra'.
 */
static RD_UNUSED void rd_avg_add (rd_avg_t *ra, int64_t v) {
        mtx_lock(&ra->ra_lock);
	if (v > ra->ra_v.maxv)
		ra->ra_v.maxv = v;
	if (ra->ra_v.minv == 0 || v < ra->ra_v.minv)
		ra->ra_v.minv = v;
	ra->ra_v.sum += v;
	ra->ra_v.cnt++;
        mtx_unlock(&ra->ra_lock);
}


/**
 * @brief Calculate the average
 */
static RD_UNUSED void rd_avg_calc (rd_avg_t *ra, rd_ts_t now) {
        if (ra->ra_type == RD_AVG_GAUGE) {
                if (ra->ra_v.cnt)
                        ra->ra_v.avg = ra->ra_v.sum / ra->ra_v.cnt;
                else
                        ra->ra_v.avg = 0;
        } else {
                rd_ts_t elapsed = now - ra->ra_v.start;

                if (elapsed)
                        ra->ra_v.avg = (ra->ra_v.sum * 1000000llu) / elapsed;
                else
                        ra->ra_v.avg = 0;

                ra->ra_v.start = elapsed;
        }
}


/**
 * Rolls over statistics in 'src' and stores the average in 'dst'.
 * 'src' is cleared and ready to be reused.
 */
static RD_UNUSED void rd_avg_rollover (rd_avg_t *dst,
					     rd_avg_t *src) {
        rd_ts_t now = rd_clock();

        mtx_lock(&src->ra_lock);
        dst->ra_type = src->ra_type;
	dst->ra_v    = src->ra_v;
	memset(&src->ra_v, 0, sizeof(src->ra_v));
        src->ra_v.start = now;
        mtx_unlock(&src->ra_lock);

        rd_avg_calc(dst, now);
}


/**
 * Initialize an averager
 */
static RD_UNUSED void rd_avg_init (rd_avg_t *ra, int type) {
        rd_avg_t dummy;
        memset(ra, 0, sizeof(*ra));
        mtx_init(&ra->ra_lock, 0);
        ra->ra_type = type;

        rd_avg_rollover(&dummy, ra);
}

/**
 * Destroy averager
 */
static RD_UNUSED void rd_avg_destroy (rd_avg_t *ra) {
        mtx_destroy(&ra->ra_lock);
}

#endif /* _RDAVG_H_ */
