/*
   Copyright (C) 2004 - 2014 by Guillaume Melquiond <guillaume.melquiond@inria.fr>
   Part of the Gappa tool https://gappa.gitlabpages.inria.fr/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the CeCILL Free Software License Agreement or
   under the terms of the GNU General Public License version.

   See the COPYING and COPYING.GPL files for more details.
*/

#include <fstream>
#include <iostream>

#include "parameters.hpp"
#include "utils.hpp"
#include "backends/backend.hpp"
#include "numbers/interval_utility.hpp"
#include "parser/ast.hpp"
#include "proofs/proof_graph.hpp"
#include "proofs/schemes.hpp"

extern bool parameter_constrained, parameter_statistics, parameter_auto_split;
extern std::string parameter_schemes;
extern int yyparse(void);
extern ast_real const *get_bottleneck_real();
extern bool detailed_io;
extern backend *proof_generator;
dichotomy_sequence dichotomies;
property_tree context;

extern int
  stat_tested_real, stat_discarded_real,
  stat_tested_theo, stat_discarded_theo,
  stat_tested_app, stat_successful_app,
  stat_intersected_pred, stat_discarded_pred;

RUN_LAST(free_dichotomies) {
  for (dichotomy_hint const &i: dichotomies) {
    for (dichotomy_var const &j: i.src) {
      clear_splitter(j.splitter);
    }
  }
  dichotomies.clear();
}

int main(int argc, char **argv)
{
  parse_args_status pargs_status = parse_args(argc, argv);
  if (pargs_status != PARGS_CONTINUE)
    return pargs_status == PARGS_FAILURE ? EXIT_FAILURE : EXIT_SUCCESS;
  if (proof_generator) {
    if (!parameter_constrained) {
      std::cerr << "Error: unconstrained mode is not compatible with script generation, since proofs are left incomplete.\n";
      return EXIT_FAILURE;
    }
    proof_generator->initialize(std::cout);
  }
  if (yyparse()) return EXIT_FAILURE;
  if (parameter_auto_split) {
    undefined_map umap;
    context.get_undefined(umap);
    ast_real const *r;
    if (umap.empty() && (r = get_bottleneck_real())) {
      //std::cerr << "Splitting " << dump_real(r) << '\n';
      dichotomy_var v = { r, 0 };
      predicated_real unsat(normalize(ast_real(token_zero)), PRED_NZR);
      dichotomy_hint h =
        { dvar_vect(1, v), property_tree(property(unsat)), false };
      dichotomies.push_back(h);
    }
  }
  preal_vect missing_paths = generate_proof_paths();
  for (predicated_real const &i: missing_paths) {
    std::cerr << "Warning: no path was found for " << dump_real(i) << ".\n";
  }
  if (!parameter_schemes.empty()) {
    std::ofstream out(parameter_schemes.c_str());
    output_schemes(out);
  }
  bool globally_proven = true;
  undefined_map umap;
  graph_t *g = new graph_t(NULL, context);
  proof_context pctx { NULL, 1. };
  g->populate(property_tree(), dichotomies, pctx, &umap);
  bool has_results = false;
  if (!umap.empty()) {
    std::cerr << "Results:\n";
    has_results = true;
    change_io_format dummy(IO_FULL);
    for (auto const &i: umap) {
      std::cerr << "  " << dump_property_nice(i.second) << '\n';
    }
  }
  if (node *n = g->get_contradiction()) {
    if (g->get_undefined(umap)) {
      if (!has_results) std::cerr << "Results:\n";
      std::cerr << "  remaining results are pointless, anything can be proved.\n";
    }
    if (proof_generator) {
      enlarger(n);
      instances = &umap;
      proof_generator->theorem(n);
    }
  } else {
    g->show_negative();
    globally_proven = false;
  }
  g->show_dangling();
  delete g;
  if (proof_generator) proof_generator->finalize();
  if (parameter_statistics) {
    std::cerr <<
      "Statistics:\n"
      "  " << stat_tested_real << " expressions were considered,\n"
      "    but then " << stat_discarded_real << " of those got discarded.\n"
      "  " << stat_tested_theo << " theorems were considered,\n"
      "    but then " << stat_discarded_theo << " of those got discarded.\n"
      "  " << stat_tested_app << " applications were tried. Among those,\n"
      "    " << stat_successful_app << " were successful,\n"
      "    yet " << stat_discarded_pred << " proved useless\n"
      "    and " << stat_intersected_pred << " improved existing results.\n";
  }
  return globally_proven ? EXIT_SUCCESS : EXIT_FAILURE;
}
