/*
   Copyright (C) 2004 - 2014 by Guillaume Melquiond <guillaume.melquiond@inria.fr>
   Part of the Gappa tool http://gappa.gforge.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 <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 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_sequence::const_iterator i = dichotomies.begin(),
       i_end = dichotomies.end(); i != i_end; ++i)
  {
    for (dvar_vect::const_iterator j = i->src.begin(),
         j_end = i ->src.end(); j != j_end; ++j)
    {
      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 (preal_vect::const_iterator i = missing_paths.begin(),
       i_end = missing_paths.end(); i != i_end; ++i)
  {
    std::cerr << "Warning: no path was found for " << dump_real(*i) << ".\n";
  }
  bool globally_proven = true;
  undefined_map umap;
  graph_t *g = new graph_t(NULL, context);
  g->populate(property_tree(), dichotomies, 100*1000*1000, &umap);
  bool has_results = false;
  if (!umap.empty()) {
    std::cerr << "Results:\n";
    has_results = true;
    change_io_format dummy(IO_FULL);
    for (undefined_map::const_iterator i = umap.begin(),
         i_end = umap.end(); i != i_end; ++i)
    {
      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;
}
