#define INLINE_SORT

#include "inline.h"
#include "sort.c"

void kissat_remove_blocking_watch(kissat *solver, watches *watches, reference ref)
{
    assert(solver->watching);
    watch *begin = BEGIN_WATCHES(*watches);
    watch *end = END_WATCHES(*watches);
    watch *q = begin;
    const watch *p = q;
#ifndef NDEBUG
    bool found = false;
#endif
    while (p != end) {
        const watch head = *q++ = *p++;
        if (head.type.binary)
            continue;
        const watch tail = *q++ = *p++;
        if (tail.raw != ref)
            continue;
#ifndef NDEBUG
        assert(!found);
        found = true;
#endif
        q -= 2;
    }
    assert(found);
    watches->size -= 2;
    const watch empty = {
        .raw = INVALID_VECTOR_ELEMENT
    };
    end[-2] = end[-1] = empty;
    assert(solver->vectors.usable < MAX_SECTOR - 2);
    solver->vectors.usable += 2;
    kissat_check_vectors(solver);
}

void kissat_flush_large_watches(kissat *solver)
{
    assert(solver->watching);
    LOG("flush large clause watches");
    watches *all_watches = solver->watches;
    for (all_literals(lit)) {
        watches *lit_watches = all_watches + lit;
        watch *begin = BEGIN_WATCHES(*lit_watches), *q = begin;
        const watch *end = END_WATCHES(*lit_watches), *p = q;
        while (p != end)
            if (!(*q++ = *p++).type.binary)
                q--;
        SET_END_OF_WATCHES(*lit_watches, q);
    }
}

void kissat_watch_large_clauses(kissat *solver)
{
    LOG("watching all large clauses");
    assert(solver->watching);

    const value *values = solver->values;
    const assigned *assigned = solver->assigned;
    watches *watches = solver->watches;
    const word *arena = BEGIN_STACK(solver->arena);

    for (all_clauses(c)) {
        if (c->garbage)
            continue;

        unsigned *lits = c->lits;
        kissat_sort_literals(solver, values, assigned, c->size, lits);
        c->searched = 2;

        const reference ref = (word *)c - arena;
        const unsigned l0 = lits[0];
        const unsigned l1 = lits[1];

        kissat_push_blocking_watch(solver, watches + l0, l1, ref);
        kissat_push_blocking_watch(solver, watches + l1, l0, ref);
    }
}

void kissat_connect_irredundant_large_clauses(kissat *solver)
{
    assert(!solver->watching);
    LOG("connecting all large irredundant clauses");

    clause *last_irredundant = kissat_last_irredundant_clause(solver);

    const value *values = solver->values;
    watches *all_watches = solver->watches;
    const word *arena = BEGIN_STACK(solver->arena);

    for (all_clauses(c)) {
        if (last_irredundant && c > last_irredundant)
            break;
        if (c->redundant)
            continue;
        if (c->garbage)
            continue;
        bool satisfied = false;
        assert(!solver->level);
        for (all_literals_in_clause(lit, c)) {
            const value value = values[lit];
            if (value <= 0)
                continue;
            satisfied = true;
            break;
        }
        if (satisfied) {
            kissat_mark_clause_as_garbage(solver, c);
            continue;
        }
        const reference ref = (word *)c - arena;
        kissat_inlined_connect_clause(solver, all_watches, c, ref);
    }
}

void kissat_flush_large_connected(kissat *solver)
{
    assert(!solver->watching);
    LOG("flushing large connected clause references");
    size_t flushed = 0;
    for (all_literals(lit)) {
        watches *watches = &WATCHES(lit);
        watch *begin = BEGIN_WATCHES(*watches), *q = begin;
        const watch *end_watches = END_WATCHES(*watches), *p = q;
        while (p != end_watches) {
            const watch head = *p++;
            if (head.type.binary)
                *q++ = head;
            else
                flushed++;
        }
        SET_END_OF_WATCHES(*watches, q);
    }
    LOG("flushed %zu large clause references", flushed);
    (void)flushed;
}
