#ifndef    __SPEW_H__
#define    __SPEW_H__


#include <stdarg.h>         // for va_list
#include <stddef.h>         // for size_t
#include <stdint.h>         // for uint64_t
#include "simplest-defs.h"  // for boolint
struct spewlog;  // lines 13-13


enum {
    SPEW_DEFAULT_ROLL_PERIOD = 1440, // go: logging.DefaultLogfileRollPeriod
                                     // go: logging.MaxLogFileSize
    SPEW_MAX_LOGFILE_SIZE    = 1024ll * 1024ll * 512ll,
    THREADNAME_SIZE          = 16,   // <== man pthread_setname_np()
};

/* Two INFO levels.

   The theory is that in production we want INFO about
   open/close/dropped connections, how many extents we found on disk,
   etc.  Such INFO is kind-of useless and distracting for tests.
   Thus, the default verbosity is SPEW_TEST_INFO for tests.
   Production code should run at SPEW_INFO and print more spewage.
*/
enum {
    FIRST_SPEW_LEVEL = 0u,
    SPEW_TRACE       = 0u,
    SPEW_DEBUG       = 1u,
    SPEW_INFO        = 2u,
    SPEW_TEST_INFO   = 3u,
    SPEW_WARN        = 4u,
    SPEW_ERROR       = 5u,
    SPEW_BUG         = 6u,
    SPEW_FATAL       = 7u,
    SPEW_CRIT        = 8u,
    N_SPEW_LEVELS    = 9u,
};


extern struct spewlog the_spew_log;
extern struct spewlog the_metrics_log;


const char *
log_get_level_name(size_t level);
size_t
log_level_of_level_name(const char *level_name);
size_t
log_set_level(struct spewlog *log, size_t new_level);
size_t
log_add_level(struct spewlog *log, size_t add, size_t sub);

void
HACK_log_inc_level_count(size_t level);
void
spew_reset_level_counts(uint64_t *level_counters, size_t level_counters_len);

void
log_config(struct spewlog *log, const char *logrootdir, const char *argv0,
           size_t roll_period_m, boolint spew_to_stderr, boolint debug);
void log_teardown(struct spewlog *log);

boolint
log_enabled(struct spewlog *log, size_t level);

void
logv(struct spewlog *log,
     boolint honor_spewage_suppression,
     boolint maybe_roll,
     boolint is_stack_trace,
     size_t level,
     const char *file,
     size_t line,
     const char *fn,
     const char *fmt,
     va_list ap)
    __attribute__((format(printf, 9, 0)));

void log0(struct spewlog *log,
          boolint honor_spewage_suppression,
          boolint maybe_roll,
          boolint is_stack_trace,
          size_t level,
          const char *file,
          size_t line,
          const char *fn,
          const char *fmt,
          ...)
    __attribute__ ((format (printf, 9, 10)));
void
log1(struct spewlog *log,
     const char *fmt,
     ...)
    __attribute__ ((format (printf, 2, 3)));
boolint
spew_logfile_ok(struct spewlog *log);

/* Legacy APIs.
 */
static inline const char *
spew_get_level_name(size_t level)
{
    return log_get_level_name(level);
}
static inline size_t
spew_set_level(size_t new_level)
{
    return log_set_level(&the_spew_log, new_level);
}
static inline size_t spew_more(void)
{
    return log_add_level(&the_spew_log, /*add=*/0, /*sub=*/1);
}
static inline size_t spew_less(void)
{
    return log_add_level(&the_spew_log, /*add=*/1, /*sub=*/0);
}

/* Simple backtrace facility.
 */
void spew_backtrace_simple(void);

#define would_spew(lvl)  log_enabled(&the_spew_log, (lvl))

#define spew(lvl, fmt, ...)                                             \
    ({                                                                  \
        if (log_enabled(&the_spew_log, lvl)) {                          \
            log0(&the_spew_log,                                         \
                 /*honor_spewage_suppression=*/1,  /*maybe_roll=*/1,    \
                 /*is_stack_trace=*/0,                                  \
                 lvl, __FILE__, __LINE__, __FUNCTION__,                 \
                 fmt, ##__VA_ARGS__);                                   \
        }                                                               \
    })

#define spew_no_roll(lvl, fmt, ...)                                     \
    ({                                                                  \
        if (log_enabled(&the_spew_log, lvl)) {                          \
            log0(&the_spew_log,                                         \
                 /*honor_spewage_suppression=*/1,  /*maybe_roll=*/0,    \
                 /*is_stack_trace=*/0,                                  \
                 lvl, __FILE__, __LINE__, __FUNCTION__,                 \
                 fmt, ##__VA_ARGS__);                                   \
        }                                                               \
    })

#define spew_for_backtrace(lvl, file, line, function, symbol)           \
    ({                                                                  \
        if (log_enabled(&the_spew_log, lvl)) {                          \
            log0(&the_spew_log,                                         \
                 /*honor_spewage_suppression=*/1,  /*maybe_roll=*/0,    \
                 /*is_stack_trace=*/1,                                  \
                 lvl, file, line, function, "%s", symbol);              \
        }                                                               \
    })

void
spew_config(int argc, char *const argv[], const char *logrootdir,
            size_t roll_period_m, boolint spew_to_stderr, boolint debug);
void
spew_teardown(void);

size_t
spew_get_level(void);
// Different from DEBUG_spew_get_level because it acquires a lock.

boolint
DEBUG_shutup(boolint suppress);
size_t
DEBUG_spew_get_level(void);


#endif // !__SPEW_H__
