#include "assert.h"
#include <inttypes.h>          // for PRIx64
#include <stddef.h>            // for size_t
#include <stdint.h>            // for uint64_t
#include <stdlib.h>            // for abort
#include <stdarg.h>            // for va_end
#include "backtrace.h"  // for spew_backtrace
#include "spew.h"       // for log0, ::SPEW_FATAL, the_spew_log
#include "../version.h"

void assert_fail(const char *assertion,
                 const char *file, size_t line, const char *function)
{
    log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
         /*is_stack_trace=*/0,
         SPEW_FATAL, file, line, function,
         "ASSERTION FAILURE: %s", assertion);
    log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
         /*is_stack_trace=*/0,
         SPEW_FATAL, file, line, function, "GITREV %s", gitrev);
    spew_backtrace(SPEW_FATAL);
    abort();
}


void assert_fail_int(const char *assertion, const char *str_var, int var,
                     const char *file, size_t line, const char *function)
{
    log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
         /*is_stack_trace=*/0,
         SPEW_FATAL, file, line, function, "ASSERTION FAILURE: %s, %s=%d",
         assertion, str_var, var);
    spew_backtrace(SPEW_FATAL);
    abort();
}


void assert_fail_v(const char *assertion,
                   const char *file,
                   size_t line,
                   const char *function,
                   const char *fmt_string,
                   ...) {
    va_list args;
#ifndef    __CHECKER__
    // va_start() contains a cast to void * (from char const **, in
    // this case), which runs afoul of sparse's check for qualifier
    // drops.
    //
    va_start(args, fmt_string);
#endif // !__CHECKER__
    log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
         /*is_stack_trace=*/0,
         SPEW_FATAL, file, line, function,
         "ASSERTION FAILURE: %s", assertion);
    log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
         /*is_stack_trace=*/0,
         SPEW_FATAL, file, line, function, "GITREV %s", gitrev);
    logv(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
         /*is_stack_trace=*/0,
         SPEW_FATAL, file, line, function, fmt_string, args);
    spew_backtrace(SPEW_FATAL);
    abort();
    va_end(args);
}

void check_eq_u(uintmax_t a, uintmax_t b,
                const char *file, size_t line, const char *function,
                const char *assertion)
{
    if (a != b) {
        log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
             /*is_stack_trace=*/0,
             SPEW_FATAL, file, line, function,
             "ASSERTION FAILURE: [%s]: 0x%"PRIxMAX" != 0x%"PRIxMAX,
             assertion, a, b);
        log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
             /*is_stack_trace=*/0,
             SPEW_FATAL, file, line, function, "GITREV %s", gitrev);
        spew_backtrace(SPEW_FATAL);
        abort();
    }
}

void check_eq_i(intmax_t a, intmax_t b,
                const char *file, size_t line, const char *function,
                const char *assertion)
{
    if (a != b) {
        log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
             /*is_stack_trace=*/0,
             SPEW_FATAL, file, line, function,
             "ASSERTION FAILURE [%s]: %"PRIiMAX" != %"PRIiMAX,
             assertion, a, b);
        log0(&the_spew_log, /*honor_spewage_suppression=*/0, /*maybe_roll=*/1,
             /*is_stack_trace=*/0,
             SPEW_FATAL, file, line, function, "GITREV %s", gitrev);
        spew_backtrace(SPEW_FATAL);
        abort();
    }
}
