#include "stats.hpp"
#ifdef STATS


INIT_EARLY std::set<Timer*> Timer::registry;


Timer::Timer(const char* desc, bool reg) {
    strncpy(name, desc, sizeof(name));
    name[sizeof(name)-1] = '\0';
    memset(&sum, 0, sizeof(sum));
    if (reg) registry.insert(this);
}


Timer::~Timer() {
    registry.erase(this);
}


void Timer::reset() {
    memset(&sum, 0, sizeof(sum));
}


uint64_t Timer::get() const {
    return (sum.tv_sec * 1000000000ll) + sum.tv_nsec;
}


void Timer::start() {
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &curr);
}


void Timer::end() {
    struct timespec end;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
    if (end.tv_nsec >= curr.tv_nsec) {
        sum.tv_sec += end.tv_sec - curr.tv_sec;
        sum.tv_nsec += end.tv_nsec - curr.tv_nsec;
        if (sum.tv_nsec >= 1000000000l) {
            sum.tv_sec++;
            sum.tv_nsec -= 1000000000l;
            assert(sum.tv_nsec < 1000000000l);
        }
    } else {
        sum.tv_nsec += end.tv_nsec + (1000000000l - curr.tv_nsec);
        if (end.tv_sec > curr.tv_sec + 1) {
            sum.tv_sec += end.tv_sec - curr.tv_sec - 1;
        }
        if (sum.tv_nsec >= 2000000000l) {
            sum.tv_sec += 2;
            sum.tv_nsec -= 2000000000l;
            assert(sum.tv_nsec < 1000000000l);
        } else if (sum.tv_nsec >= 1000000000l) {
            sum.tv_sec++;
            sum.tv_nsec -= 1000000000l;
            assert(sum.tv_nsec < 1000000000l);
        }
    }
}


void Timer::print() const {
    LOG("%s: %ld.%09ld", name, sum.tv_sec, sum.tv_nsec);
}


void Timer::print_all() {
    for (std::set<Timer*>::const_iterator it = registry.begin(); it != registry.end(); ++it) {
        (*it)->print();
    }
}


void Timer::reset_all() {
    for (std::set<Timer*>::const_iterator it = registry.begin(); it != registry.end(); ++it) {
        (*it)->reset();
    }
}


INIT_EARLY std::set<Counter*> Counter::registry;


Counter::Counter(const char* desc, bool reg) {
    strncpy(name, desc, sizeof(name));
    name[sizeof(name)-1] = '\0';
    num = 0;
    sum = 0;
    if (reg) registry.insert(this);
}


Counter::~Counter() {
    registry.erase(this);
}


void Counter::reset() {
    num = 0;
    sum = 0;
}


void Counter::inc() {
    ++num;
}


void Counter::inc(unsigned i) {
    ++num;
    sum += i;
}

void Counter::print() const {
    LOG("%s: %llu/%llu (%f)", name, sum, num, num? (float)sum/(float)num: 0);
}


void Counter::print_all() {
    for (std::set<Counter*>::const_iterator it = registry.begin(); it != registry.end(); ++it) {
        (*it)->print();
    }
}


void Counter::reset_all() {
    for (std::set<Counter*>::const_iterator it = registry.begin(); it != registry.end(); ++it) {
        (*it)->reset();
    }
}


#endif