#if defined(LOGGING) && !defined(QUIET)

#include "colors.h"
#include "inline.h"

#include <stdarg.h>
#include <string.h>

static void begin_logging(kissat *solver, const char *fmt, va_list *ap)
{
    TERMINAL(stdout, 1);
    assert(GET_OPTION(log));
    fputs("c ", stdout);
    COLOR(MAGENTA);
    printf("LOG %u ", solver->level);
    vprintf(fmt, *ap);
}

static void end_logging(void)
{
    TERMINAL(stdout, 1);
    fputc('\n', stdout);
    COLOR(NORMAL);
    fflush(stdout);
}

void kissat_log_msg(kissat *solver, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    end_logging();
}

static void append_sprintf(char *str, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    const size_t len = strlen(str);
    vsprintf(str + len, fmt, ap);
    va_end(ap);
}

const char *kissat_log_lit(kissat *solver, unsigned lit)
{
    assert(solver);
    char *res = kissat_next_format_string(&solver->format);
    sprintf(res, "%u", lit);
    if (!solver->compacting && GET_OPTION(log) > 1) {
        append_sprintf(res, "(%d)", kissat_export_literal(solver, lit));
        if (solver->values) {
            const value value = VALUE(lit);
            if (value) {
                append_sprintf(res, "=%d", value);
                if (solver->assigned)
                    append_sprintf(res, "@%u", LEVEL(lit));
            }
        }
    }
    assert(strlen(res) < FORMAT_STRING_SIZE);
    return res;
}

static void log_lits(kissat *solver, size_t size, const unsigned *lits)
{
    for (size_t i = 0; i < size; i++) {
        fputc(' ', stdout);
        fputs(LOGLIT(lits[i]), stdout);
    }
}

void kissat_log_lits(kissat *solver, size_t size, const unsigned *lits, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    printf(" size %zu clause", size);
    log_lits(solver, size, lits);
    end_logging();
}

void kissat_log_resolvent(kissat *solver, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    const size_t size = SIZE_STACK(solver->resolvent_lits);
    printf(" size %zu resolvent", size);
    const unsigned *lits = BEGIN_STACK(solver->resolvent_lits);
    log_lits(solver, size, lits);
    end_logging();
}

void kissat_log_ints(kissat *solver, size_t size, const int *lits, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    printf(" size %zu external literals clause", size);
    for (size_t i = 0; i < size; i++)
        printf(" %d", lits[i]);
    end_logging();
}

void kissat_log_extensions(kissat *solver, size_t size, const extension *exts, const char *fmt, ...)
{
    assert(size > 0);
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    const extension *begin = BEGIN_STACK(solver->extend);
    const size_t pos = exts - begin;
    printf(" extend[%zu]", pos);
    printf(" %d", exts[0].lit);
    if (size > 1)
        fputs(" :", stdout);
    for (size_t i = 1; i < size; i++)
        printf(" %d", exts[i].lit);
    end_logging();
}

void kissat_log_unsigneds(kissat *solver, size_t size, const unsigned *lits, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    printf(" size %zu unsigned literals clause", size);
    for (size_t i = 0; i < size; i++)
        printf(" %u", lits[i]);
    end_logging();
}

static void log_clause(kissat *solver, clause *c)
{
    fputc(' ', stdout);
    if (c == &solver->conflict) {
        fputs("static ", stdout);
        fputs(c->redundant ? "redundant" : "irredundant", stdout);
        fputs(" binary conflict clause", stdout);
    } else {
        if (c->hyper) {
            assert(c->size == 3);
            fputs("hyper ", stdout);
        }
        if (c->redundant)
            printf("redundant glue %u", c->glue);
        else
            fputs("irredundant", stdout);
        printf(" size %u", c->size);
        if (c->reason)
            fputs(" reason", stdout);
        if (c->garbage)
            fputs(" garbage", stdout);
        fputs(" clause", stdout);
        if (kissat_clause_in_arena(solver, c)) {
            reference ref = kissat_reference_clause(solver, c);
            printf("[%u]", ref);
        }
    }
}

void kissat_log_clause(kissat *solver, clause *c, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    log_clause(solver, c);
    log_lits(solver, c->size, c->lits);
    end_logging();
}

static void log_binary(kissat *solver, unsigned a, unsigned b)
{
    printf(" binary clause %s %s", LOGLIT(a), LOGLIT(b));
}

void kissat_log_binary(kissat *solver, unsigned a, unsigned b, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    log_binary(solver, a, b);
    end_logging();
}

void kissat_log_unary(kissat *solver, unsigned a, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    printf(" unary clause %s", LOGLIT(a));
    end_logging();
}

static void log_ref(kissat *solver, reference ref)
{
    clause *c = kissat_dereference_clause(solver, ref);
    log_clause(solver, c);
    log_lits(solver, c->size, c->lits);
}

void kissat_log_ref(kissat *solver, reference ref, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    log_ref(solver, ref);
    end_logging();
}

void kissat_log_watch(kissat *solver, unsigned lit, watch watch, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    if (watch.type.binary)
        log_binary(solver, lit, watch.binary.lit);
    else
        log_ref(solver, watch.large.ref);
    end_logging();
}

void kissat_log_xor(kissat *solver, unsigned lit, unsigned size, unsigned *lits, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    begin_logging(solver, fmt, &ap);
    va_end(ap);
    printf(" size %u XOR gate ", size);
    fputs(kissat_log_lit(solver, lit), stdout);
    printf(" =");
    for (unsigned i = 0; i < size; i++) {
        if (i)
            fputs(" ^ ", stdout);
        else
            fputc(' ', stdout);
        fputs(kissat_log_lit(solver, lits[i]), stdout);
    }
    end_logging();
}

#else

int kissat_log_dummy_to_avoid_pedantic_warning;

#endif
