#include "error.h"
#include "inline.h"
#include "internal.h"
#include "logging.h"
#include "require.h"

void kissat_traverse_units (kissat *solver, void *state,
                            void (*traverse) (void *, int)) {
  kissat_require_initialized (solver);
  kissat_require (traverse, "invalid zero traverse function");
  if (solver->inconsistent) {
    LOG ("inconsistent solver thus not traversing any units");
    return;
  }
  imports *imports = &solver->import;
  size_t size_import = SIZE_STACK (*imports);
  assert (size_import < UINT_MAX);
  size_t traversed = 0;
  const value *values = solver->values;
  const import *import = imports->begin + 1;
  const unsigned solver_level = solver->level;
  for (unsigned eidx = 1; eidx != size_import; eidx++, import++) {
    assert (import == imports->begin + eidx);
    if (!import->imported)
      continue;
    if (import->eliminated)
      continue;
    const unsigned ilit = import->lit;
    const value tmp = values[ilit];
    if (!tmp)
      continue;
    const unsigned level = solver_level ? LEVEL (ilit) : 0;
    if (level)
      continue;
    int elit = tmp < 0 ? -eidx : eidx;
    LOG ("traversing internal unit %s as eternal %d", LOGLIT (ilit), elit);
    traverse (state, elit);
    traversed++;
  }
  LOG ("traversed %zu unit clauses", traversed);
}

static size_t traverse_binary_clauses (kissat *solver, void *state,
                                       void (*traverse) (void *,
                                                         const int *)) {
  assert (solver->watching);
  size_t traversed = 0;
  const value *values = solver->values;
  const unsigned solver_level = solver->level;
  for (all_variables (iidx)) {
    unsigned ilit = LIT (iidx);
    if (values[ilit] && (!solver_level || !LEVEL (ilit)))
      continue;
    for (unsigned sign = 0; sign != 2; sign++) {
      ilit = NOT (ilit);
      for (all_binary_blocking_watches (watch, WATCHES (ilit))) {
        if (!watch.type.binary)
          continue;
        const unsigned iother = watch.binary.lit;
        if (ilit > iother)
          continue;
        if (values[iother] && (!solver_level || !LEVEL (iother)))
          continue;
        LOGBINARY (ilit, iother, "traversing");
        int elit = kissat_export_literal (solver, ilit);
        int eother = kissat_export_literal (solver, iother);
        int elits[3] = {elit, eother, 0};
        traverse (state, elits);
        traversed++;
      }
    }
  }
  return traversed;
}

static size_t traverse_large_clauses (kissat *solver, void *state,
                                      void (*traverse) (void *,
                                                        const int *)) {
  size_t traversed = 0;
  ints elits;

  INIT_STACK (elits);

  const value *values = solver->values;
  const unsigned solver_level = solver->level;

  clause *last_irredundant = kissat_last_irredundant_clause (solver);

  for (all_clauses (c)) {
    if (last_irredundant && c > last_irredundant)
      break;
    if (c->garbage)
      continue;
    if (c->redundant)
      continue;
    assert (EMPTY_STACK (elits));
    bool satisfied = false;
    for (all_literals_in_clause (ilit, c)) {
      const value ilit_value = values[ilit];
      if (ilit_value && (!solver_level || !LEVEL (ilit))) {
        if (ilit_value < 0)
          continue;
        if (ilit_value > 0) {
          LOGCLS (c, "not traversing %s root-satisfied clause",
                  LOGLIT (ilit));
          satisfied = true;
          break;
        }
      }
      int elit = kissat_export_literal (solver, ilit);
      PUSH_STACK (elits, elit);
    }
    if (!satisfied) {
      PUSH_STACK (elits, 0);
      LOGCLS (c, "traversing");
      traverse (state, elits.begin);
      traversed++;
    }
    CLEAR_STACK (elits);
  }
  RELEASE_STACK (elits);
  return traversed;
}

void kissat_traverse_clauses (kissat *solver, void *state,
                              void (*traverse) (void *, const int *)) {
  kissat_require_initialized (solver);
  kissat_require (traverse, "invalid zero traverse function");
  size_t traversed = 0;
  if (solver->inconsistent) {
    int sentinel = 0;
    LOG ("solver inconsistent thus traversing only empty clause");
    traverse (state, &sentinel);
    traversed++;
  } else {
    traversed += traverse_binary_clauses (solver, state, traverse);
    traversed += traverse_large_clauses (solver, state, traverse);
  }
  LOG ("traversed %zu non-unit clauses", traversed);
}

void kissat_traverse_extension (kissat *solver, void *state,
                                void (*traverse) (void *, const int *)) {
  kissat_require_initialized (solver);
  kissat_require (traverse, "invalid zero traverse function");
  size_t traversed = 0;
  if (solver->inconsistent)
    LOG ("solver inconsistent thus not traversing extension");
  else {
    ints elits;
    INIT_STACK (elits);
    const extension *const begin = BEGIN_STACK (solver->extend);
    extension const *c = END_STACK (solver->extend);
    while (c-- != begin) {
      assert (EMPTY_STACK (elits));
      const extension *const end = c;
      while (!c->blocking)
        assert (c != begin), c--;
      for (const extension *p = c; p != end; p++) {
        const int elit = p->lit;
        PUSH_STACK (elits, elit);
      }
#ifdef LOGGING
      if (GET_OPTION (log)) {
        const size_t size_elits = SIZE_STACK (elits);
        LOGINTS (size_elits, elits.begin, "traversing extension");
      }
#endif
      PUSH_STACK (elits, 0);
      traverse (state, elits.begin);
      traversed++;
      CLEAR_STACK (elits);
    }
    RELEASE_STACK (elits);
  }
  LOG ("traversed %zu clauses", traversed);
}
