/***
 * Bitwuzla: Satisfiability Modulo Theories (SMT) solver.
 *
 * Copyright (C) 2022 by the authors listed in the AUTHORS file at
 * https://github.com/bitwuzla/bitwuzla/blob/main/AUTHORS
 *
 * This file is part of Bitwuzla under the MIT license. See COPYING for more
 * information at https://github.com/bitwuzla/bitwuzla/blob/main/COPYING
 */

#ifndef BZLA_PREPROCESS_PREPROCESSOR_H_INCLUDED
#define BZLA_PREPROCESS_PREPROCESSOR_H_INCLUDED

#include "backtrack/assertion_stack.h"
#include "backtrack/pop_callback.h"
#include "preprocess/assertion_tracker.h"
#include "preprocess/pass/contradicting_ands.h"
#include "preprocess/pass/elim_extract.h"
#include "preprocess/pass/elim_lambda.h"
#include "preprocess/pass/elim_udiv.h"
#include "preprocess/pass/embedded_constraints.h"
#include "preprocess/pass/flatten_and.h"
#include "preprocess/pass/normalize.h"
#include "preprocess/pass/rewrite.h"
#include "preprocess/pass/skeleton_preproc.h"
#include "preprocess/pass/variable_substitution.h"
#include "solver/result.h"

namespace bzla {

class SolvingContext;
class Env;

namespace util {
class Logger;
}

namespace preprocess {

class Preprocessor
{
  friend class BacktrackCallback;

 public:
  Preprocessor(SolvingContext& context);

  /**
   * Exclude variables from being substituted in the variable substitution
   * pass.
   */
  void exclude(const std::unordered_set<Node>& exclude);

  /** Preprocess current set of assertions. */
  Result preprocess();

  /** Preprocess given term based on last preprocess() call. */
  Node process(const Node& term);

  /**
   * Post-process unsat core with preprocessed assertions to get unsat core in
   * terms of original assertions.
   */
  std::vector<Node> post_process_unsat_core(
      const std::vector<Node>& assertions,
      const std::unordered_set<Node>& original_assertions) const;

  /** Get current map of active substitutions. */
  const std::unordered_map<Node, Node>& substitutions() const;

  /**
   * Get all preprocessed assertions origiting from given assertion.

   * @param assertion The assertion to trace back.
   * @return The preprocessed assertions.
   */
  std::vector<Node> preprocessed_assertions(const Node& assertion) const;

  /** Do not terminate early due to inconsistent assertions. */
  void set_early_terminate(bool early_terminate)
  {
    d_early_terminate = early_terminate;
  }

 private:
  /** Is the current set of assertions inconsistent? */
  bool is_inconsistent() const;

  /** Apply all preprocessing passes to assertions until fixed-point. */
  void apply(AssertionVector& assertions);

  /** Synchronize d_backtrack_mgr up to given level. */
  void sync_scope(size_t level);

  /** Helper function to print verbose message statistics. */
  void print_statistics_header() const;
  void print_statistics(const std::string& pass);
  void print_statistics(const PreprocessingPass& pass,
                        const AssertionVector& assertions);

  Env& d_env;
  util::Logger& d_logger;
  uint64_t d_num_preprocess = 0;
  /** Terminate preprocessing as soon as assertions are inconsistent. */
  bool d_early_terminate = true;

  /** Current set of assertions. */
  backtrack::AssertionView& d_assertions;

  /** Preprocessor backtrack manager. */
  backtrack::BacktrackManager d_backtrack_mgr;

  /** Global backtrack manager of solving context. */
  const backtrack::BacktrackManager& d_global_backtrack_mgr;

  /** Callback to sync d_backtrack_mgr with d_global_backtrack_mgr on pop(). */
  backtrack::PopCallback d_pop_callback;

  /** Assertion tracking for unsat cores. */
  std::unique_ptr<AssertionTracker> d_assertion_tracker;

  /** Preprocessing passes */
  pass::PassRewrite d_pass_rewrite;
  pass::PassContradictingAnds d_pass_contr_ands;
  pass::PassElimLambda d_pass_elim_lambda;
  pass::PassElimUdiv d_pass_elim_bvudiv;
  pass::PassEmbeddedConstraints d_pass_embedded_constraints;
  pass::PassVariableSubstitution d_pass_variable_substitution;
  pass::PassFlattenAnd d_pass_flatten_and;
  pass::PassSkeletonPreproc d_pass_skeleton_preproc;
  pass::PassNormalize d_pass_normalize;
  pass::PassElimExtract d_pass_elim_extract;

  /** Counter for how often a statistics line was printed. */
  uint64_t d_num_printed_stats = 0;

  struct Statistics
  {
    Statistics(util::Statistics& stats);
    util::TimerStatistic& time_preprocess;
    util::TimerStatistic& time_process;
    uint64_t& num_iterations;
  } d_stats;
};

}  // namespace preprocess
}  // namespace bzla

#endif
