#include "lthread_diag.h"
#include "lthread_queue.h"
#include "lthread_pool.h"
#include "lthread_objcache.h"
#include "lthread_sched.h"
#include "lthread_diag_api.h"
static uint64_t dummy_ref;
#define DIAG_SCHED_STATS_FORMAT \
"core %d\n%33s %12s %12s %12s %12s\n"
#define DIAG_CACHE_STATS_FORMAT \
"%20s %12lu %12lu %12lu %12lu %12lu\n"
#define DIAG_QUEUE_STATS_FORMAT \
"%20s %12lu %12lu %12lu\n"
const char *diag_event_text[] = {
    "LTHREAD_CREATE     ",  
    "LTHREAD_EXIT       ",  
    "LTHREAD_JOIN       ",  
    "LTHREAD_CANCEL     ",  
    "LTHREAD_DETACH     ",  
    "LTHREAD_FREE       ",  
    "LTHREAD_SUSPENDED  ",  
    "LTHREAD_YIELD      ",  
    "LTHREAD_RESCHEDULED",  
    "LTHREAD_SLEEP      ",  
    "LTHREAD_RESUMED    ",  
    "LTHREAD_AFFINITY   ",  
    "LTHREAD_TMR_START  ",  
    "LTHREAD_TMR_DELETE ",  
    "LTHREAD_TMR_EXPIRED",  
    "COND_CREATE        ",  
    "COND_DESTROY       ",  
    "COND_WAIT          ",  
    "COND_SIGNAL        ",  
    "COND_BROADCAST     ",  
    "MUTEX_CREATE       ",  
    "MUTEX_DESTROY      ",  
    "MUTEX_LOCK         ",  
    "MUTEX_TRYLOCK      ",  
    "MUTEX_BLOCKED      ",  
    "MUTEX_UNLOCKED     ",  
    "SCHED_CREATE       ",  
    "SCHED_SHUTDOWN     "   
};
void lthread_diagnostic_set_mask(DIAG_USED uint64_t mask)
{
#if LTHREAD_DIAG
    diag_mask = mask;
#else
        "LTHREAD_DIAG is not set, see lthread_diag_api.h\n");
#endif
}
void
_sched_stats_consistency_check(void);
void
_sched_stats_consistency_check(void)
{
#if LTHREAD_DIAG
    int i;
    struct lthread_sched *sched;
    uint64_t count = 0;
    uint64_t capacity = 0;
    for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
        sched = schedcore[i];
        if (sched == NULL)
            continue;
        
        count += 8;
        count += DIAG_COUNT(sched->ready, size);
        count += DIAG_COUNT(sched->pready, size);
        count += DIAG_COUNT(sched->lthread_cache, available);
        count += DIAG_COUNT(sched->stack_cache, available);
        count += DIAG_COUNT(sched->tls_cache, available);
        count += DIAG_COUNT(sched->per_lthread_cache, available);
        count += DIAG_COUNT(sched->cond_cache, available);
        count += DIAG_COUNT(sched->mutex_cache, available);
        
        if (sched->qnode_pool->fast_alloc != NULL)
            count++;
        count += DIAG_COUNT(sched->qnode_pool, available);
        capacity += DIAG_COUNT(sched->qnode_pool, capacity);
    }
    if (count != capacity) {
            "Scheduler caches are inconsistent\n");
    } else {
            "Scheduler caches are ok\n");
    }
#endif
}
#if LTHREAD_DIAG
static inline void
_qnode_pool_display(DIAG_USED struct qnode_pool *p)
{
    printf(DIAG_CACHE_STATS_FORMAT,
            p->name,
            DIAG_COUNT(p, rd),
            DIAG_COUNT(p, wr),
            DIAG_COUNT(p, available),
            DIAG_COUNT(p, prealloc),
            DIAG_COUNT(p, capacity));
    fflush(stdout);
}
#endif
#if LTHREAD_DIAG
static inline void
_lthread_queue_display(DIAG_USED struct lthread_queue *q)
{
#if DISPLAY_OBJCACHE_QUEUES
    printf(DIAG_QUEUE_STATS_FORMAT,
            q->name,
            DIAG_COUNT(q, rd),
            DIAG_COUNT(q, wr),
            DIAG_COUNT(q, size));
    fflush(stdout);
#else
    printf("%s: queue stats disabled\n",
            q->name);
#endif
}
#endif
#if LTHREAD_DIAG
static inline void
_objcache_display(DIAG_USED struct lthread_objcache *c)
{
    printf(DIAG_CACHE_STATS_FORMAT,
            c->name,
            DIAG_COUNT(c, rd),
            DIAG_COUNT(c, wr),
            DIAG_COUNT(c, available),
            DIAG_COUNT(c, prealloc),
            DIAG_COUNT(c, capacity));
    _lthread_queue_display(c->q);
    fflush(stdout);
}
#endif
void
lthread_sched_stats_display(void)
{
#if LTHREAD_DIAG
    int i;
    struct lthread_sched *sched;
    for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
        sched = schedcore[i];
        if (sched != NULL) {
            printf(DIAG_SCHED_STATS_FORMAT,
                    sched->lcore_id,
                    "rd",
                    "wr",
                    "present",
                    "nb preallocs",
                    "capacity");
            _lthread_queue_display(sched->ready);
            _lthread_queue_display(sched->pready);
            _qnode_pool_display(sched->qnode_pool);
            _objcache_display(sched->lthread_cache);
            _objcache_display(sched->stack_cache);
            _objcache_display(sched->tls_cache);
            _objcache_display(sched->per_lthread_cache);
            _objcache_display(sched->cond_cache);
            _objcache_display(sched->mutex_cache);
        fflush(stdout);
        }
    }
    _sched_stats_consistency_check();
#else
        "lthread diagnostics disabled\n"
        "hint - set LTHREAD_DIAG in lthread_diag_api.h\n");
#endif
}
static uint64_t
_lthread_diag_default_cb(uint64_t time, struct lthread *lt, int diag_event,
        uint64_t diag_ref, const char *text, uint64_t p1, uint64_t p2)
{
    uint64_t _p2;
    switch (diag_event) {
    case LT_DIAG_LTHREAD_CREATE:
    case LT_DIAG_MUTEX_CREATE:
    case LT_DIAG_COND_CREATE:
        _p2 = dummy_ref;
        break;
    default:
        _p2 = p2;
        break;
    }
    printf("%"PRIu64" %d %8.8lx %8.8lx %s %8.8lx %8.8lx\n",
        time,
        lcore,
        (uint64_t) lt,
        diag_ref,
        text,
        p1,
        _p2);
    return dummy_ref++;
}
{
    diag_cb = _lthread_diag_default_cb;
    diag_mask = 0;
}
void lthread_diagnostic_enable(DIAG_USED diag_callback cb,
                DIAG_USED uint64_t mask)
{
#if LTHREAD_DIAG
    if (cb == NULL)
        diag_cb = _lthread_diag_default_cb;
    else
        diag_cb = cb;
    diag_mask = mask;
#else
        "LTHREAD_DIAG is not set, see lthread_diag_api.h\n");
#endif
}