#include <time.h>
#include "state.h"
#include "memory.h"
#include "tptp_parser.h"
#include "array.h"
#include "fol.h"
#include "folsubst.h"
#include "trail.h"
#include "kbo.h"
#include "twfo.h"
#include "csolver.h"
#include "distrn.h"
#include "subsume.h"
#include "condense.h"
#include "fredund.h"
#include "groundgen.h"
#include "fcnf.h"
#include "lmodel.h"
#include <signal.h>
#include <unistd.h>
#include "folsubst.h"
#include "meta.h"
SCLSTATE top_STATE = (SCLSTATE)NULL;
BOOL top_FINISH = FALSE;
#define top_VERSION "0.1.1"
static void top_SigHandler(int Signal)
{
  if (Signal == SIGSEGV) {
    fflush(stdout);
    puts("\n\n\tSPASS-SCL-FOL is going to crash. This is probably caused by a");
    puts("\tbug.  Please send a copy of the input file(s) together");
    puts("\twith the used options to spass@mpi-inf.mpg.de, and someone will");
    puts("\ttry to fix the problem.\n");
    abort();
  }
  if ((Signal == SIGINT || Signal == SIGTERM || Signal == SIGALRM) && !top_FINISH) {
    printf("\n\n%% SZS status Timeout\n");
    printf("\n%% SPASS-SCL-FOL %s ", top_VERSION);
    puts("\n% SPASS beiseite: Ran out of time or SPASS was killed.");
    if (top_STATE) {
      clock_StopPassedTime(state_GetClockOverall(top_STATE), clock_OVERALL);
      state_PrintStatistics(top_STATE);
      printf("\n%% SCL allocated %lu KBytes.", memory_DemandedBytes(state_GetMemory(top_STATE))/1024);
      fputs("\n% SCL spent\t", stdout);
      clock_PrintTime(state_GetClockOverall(top_STATE), clock_OVERALL);
      fputs(" on the problem.\n", stdout);
    }
  }
  exit(EXIT_FAILURE);
}
void top_TestDistr(SCLSTATE State)
{
  unsigned i,j;
  CLAUSE clause;
  DISTRN dtree;
  MEMORY mem;
  ARRAY result;
  mem = state_GetMemory(State);
  dtree = distrn_Create(mem);
  result = array_Create(mem, 100);
  for (i=0;i<array_GetSize(state_GetClauses(State));i++) {
    clause = (CLAUSE)array_GetElement(state_GetClauses(State), i);
    for (j=0; j<clause_GetFolLiterals(clause);j++) {
      distrn_Insert(mem, dtree, state_GetTermstate(State), (TERM)clause[j], NULL, (intptr_t)clause[j]);
    }
  }
  for(j=0;j<array_GetSize(state_GetGroundAtoms(State));j++) {
    printf("\n%% Searching for "); term_PrintPrefix(state_GetTermstate(State), (TERM)state_GetGroundAtoms(State)[j]);
    distrn_SearchGeneralizations(mem, dtree, state_GetTermstate(State), (TERM)state_GetGroundAtoms(State)[j], &result);
    for (i=0;i<array_GetSize(result);i++) {
      printf("\n%% "); term_PrintPrefix(state_GetTermstate(State), (TERM)result[i]);
    }
    array_SetSize(result,0);
  }
  for (i=1;i<array_GetSize(state_GetClauses(State));i++) {
    clause = (CLAUSE)array_GetElement(state_GetClauses(State), i);
    for (j=0; j<clause_GetFolLiterals(clause);j++) {
      distrn_Remove(dtree, state_GetTermstate(State), (TERM)clause[j], NULL, (intptr_t)clause[j]);
    }
  }
  for(j=0;j<array_GetSize(state_GetGroundAtoms(State));j++) {
    printf("\n%% Searching for "); term_PrintPrefix(state_GetTermstate(State), (TERM)state_GetGroundAtoms(State)[j]);
    distrn_SearchGeneralizations(mem, dtree, state_GetTermstate(State), (TERM)state_GetGroundAtoms(State)[j], &result);
    for (i=0;i<array_GetSize(result);i++) {
      printf("\n%% "); term_PrintPrefix(state_GetTermstate(State), (TERM)result[i]);
    }
    array_SetSize(result,0);
  }
  array_Delete(mem, result);
  distrn_Delete(mem, dtree);
}
void top_ShareClauses(SCLSTATE State, SHARER Sharer, ARRAY Clauses)
{
  CLAUSE clause;
  unsigned i, inputlits;
  inputlits = 0;
  for (i=0; i<array_GetSize(Clauses); i++) {
    clause = (CLAUSE)Clauses[i];
    inputlits += clause_GetFolLiterals(clause);
    fred_ShareClause(Sharer, clause);
  }
  state_SetNumberInputLits(State, inputlits);
}
void print_usage(void) {
  printf("SPASS-SCL-FOL [options] <file>");
  printf("\n -t <nat>                      Time limit in seconds, -1 (default) for unlimited.");
  printf("\n --scl                         Parse SCL clauses.");
  printf("\n --tptp                        Parse TPTP formulae (supports fof and cnf). [DEFAULT]");
  printf("\n --cnf                         Only print the generated clause normal form (in TPTP syntax).");
  printf("\n --heuristics_normal           Use normal VSIDS heuristics. [DEFAULT]");
  printf("\n --heuristics_resolution_1     Use resolution heuristics type 1.");
  printf("\n --heuristics_resolution_2     Use resolution heuristics type 2.");
  printf("\n --heuristics_resolution_3     Use resolution heuristics type 3.");
  printf("\n --heuristics_lmodel_grow      Use lmodel grow heuristics.");
  printf("\n --heuristics_lcl              Use LCL heuristics.");
  printf("\n --abstraction                 Enable propositional abstraction.");
  printf("\n --version                     Print version and exit.");
  printf("%s", FCNF_SKOLEM_LEVEL_DOCSTRING);
  printf("\n");
}
void top_TransferTPTP(SCLSTATE Sclstate, TPTP_PARSESTATE Parsstate,
        TPTPSTATE Tptpstate, ARRAY Formulae, int SkolemLevel)
{
  FCNFSTATE fcnfstate;
  MEMORY mem;
  ARRAY clauses;
  TPTP_FORMULA formula;
  unsigned i;
  mem = state_GetMemory(Sclstate);
  fcnfstate = fcnf_Init(Sclstate);
  state_SetNamehash(Sclstate, tptparse_StateGetNamehash(Parsstate));
  tptparse_StateSetNamehash(Parsstate, NULL);
  state_SetId(Sclstate, tptparse_StateGetId(Parsstate));
  state_SetNumberInputLits(Sclstate, tptparse_StateGetNumberInputLits(Parsstate));
  state_SetPredicates(Sclstate, tptparse_StateGetPredicates(Parsstate));
  tptparse_StateSetPredicates(Parsstate, NULL);
  state_SetConstants(Sclstate, tptparse_StateGetFolConstants(Parsstate));
  tptparse_StateSetFolConstants(Parsstate, NULL);
  state_SetFunctions(Sclstate, tptparse_StateGetFolFunctions(Parsstate));
  tptparse_StateSetFolFunctions(Parsstate, NULL);
  state_SetTptpstate(Sclstate, Tptpstate);
  clauses = array_Create(mem, 100);
  for (i=0; i<array_GetSize(Formulae); ++i) {
    formula = (TPTP_FORMULA)array_GetElement(Formulae, i);
    if (strcmp(tptp_GetRole(formula), TPTP_RoleConjecture) == 0)
      state_SetInputConjecture(Sclstate, TRUE);
    tptp_StoreFileRecord(Tptpstate, formula);
    tptp_NegateConjectureIfNecessary(Tptpstate, formula);
    if (strcmp(TPTP_LangCNF, tptp_GetLanguage(formula)) == 0) {
      CLAUSE clause = tptp_GetClause(formula);
      clauses = array_Add(mem, clauses, (intptr_t)clause);
      tptp_RegisterClauseName(Tptpstate,
         clause_GetId(clause),
         tptp_GetName(formula));
      tptp_SetName(formula, NULL);
      tptp_SetProofstepByClauseID(Tptpstate,
                                  clause_GetId(clause),
                                  tptp_GetProofstep(formula));
      tptp_FreeFormula(tptp_GetMemory(Tptpstate), formula);
    }
    else if (strcmp(TPTP_LangFOF, tptp_GetLanguage(formula)) == 0) {
      clauses = fcnf_CNFWithTPTPDerivations(fcnfstate, formula, SkolemLevel, clauses);
    }
#ifdef SPASS_CHECK
    else {
      misc_StartErrorReport();
      misc_ErrorReport("\n In %s: not fof or cnf language", __func__);
      misc_FinishErrorReport();
    }
#endif
  }
  state_SetClauses(Sclstate, clauses);
  tptp_DetermineGenericClauseNamePrefix(Tptpstate);
  fcnf_Free(fcnfstate);
  array_Delete(mem, Formulae);
}
int main(int argc, char** argv)
{
  SCLSTATE state;
  MEMORY mem;
  FILE *fileinput;
  SYMBSTATE symbstate;
  TERMSTATE termstate;
  FOLSTATE folstate;
  SUBSTSTATE subststate;
  CCBS_STATE congrstate;
  TPTP_PARSESTATE tptpParsstate;
  TPTPSTATE tptpstate;
  KBOSTATE kstate;
  unsigned i, j, maxvar;
  int seconds;
  char *inputfile;
  char *currentParam;
  SHARER sharer;
  ARRAY fcnfClauses;
  CLOCK clock;
  BOOL parseTPTP, onlycnf, abstraction;
  int skolemLevel;
  CLAUSE clause;
  TERM atom;
  METAHEUR heuristics_mode;
  mem = memory_Init(memory_Create());
  clock = clock_Create(mem);
  clock_Start(clock, clock_OVERALL);
  fileinput = (FILE*)NULL;
  inputfile = (char *)NULL;
  parseTPTP = TRUE;
  onlycnf = FALSE;
  skolemLevel = FCNF_SKOLEM_LEVEL_DEFAULT;
  seconds = -1;
  tptpstate = NULL;
  tptpParsstate = NULL;
  abstraction = FALSE;
  if (argc < 2) {
    print_usage();
    return 0;
  }
  heuristics_mode = META_NONE;
  for(i = 1; i < (unsigned)argc; i++) {
    currentParam = argv[i];
    if (0 == strcmp(currentParam, "--tptp")) {
      parseTPTP = TRUE;
    }
    else if (0 == strcmp(currentParam, "--scl")) {
      parseTPTP = FALSE;
    }
    else if (0 == strcmp(currentParam, "-t")) {
      i++;
      currentParam = argv[i];
      string_StringToInt(currentParam, TRUE, &seconds);
    }
    else if (0 == strcmp(currentParam, "--cnf")) {
      onlycnf = TRUE;
    }
    else if (0 == strcmp(currentParam, "--heuristics_lmodel_grow")) {
      heuristics_mode = META_LMODEL_GROW;
    }
    else if (0 == strcmp(currentParam, "--heuristics_lcl")) {
      heuristics_mode = META_LCL;
    }
    else if (0 == strcmp(currentParam, "--abstraction")) {
      abstraction = TRUE;
    }
    else if (string_IsPrefix(FCNF_SKOLEM_LEVEL_PREFIX, currentParam)) {
      skolemLevel = fcnf_ParseSkolemOption(currentParam);
    }
    else if (0 == strcmp(currentParam, "--version")) {
#ifndef GIT_HASH
#define GIT_HASH "no git build"
#endif
      printf("SPASS-SCL-FOL %s (%s) ", top_VERSION, GIT_HASH);
#ifdef SPASS_CHECK
      printf("[SPASSCHECK]");
#endif
      clock_Delete(mem, clock);
      memory_Delete(mem);
      printf("\n");
      return 0;
    }
    else {
      fileinput = fopen(argv[i], "r");
      inputfile = argv[i];
      if (!fileinput) {
 misc_StartUserErrorReport();
 misc_UserErrorReport("\n In %s:", __func__);
 misc_UserErrorReport("\n Unrecognized option or nonexistant file: %s\n", argv[i]);
 misc_FinishUserErrorReport();
      }
    }
  }
  if (!fileinput) {
    misc_StartUserErrorReport();
    misc_UserErrorReport("\n In %s:", __func__);
    misc_UserErrorReport("\n No input file among arguments!\n");
    misc_FinishUserErrorReport();
  }
  symbstate = symbol_Init(mem, 10, 10, 100);
  termstate = term_Init(symbstate, 5);
  folstate = fol_Init(termstate);
  subststate = subst_StateCreate(mem,symbol_GetMaxVars(symbstate)+1);
  congrstate = congrbs_Init(mem, termstate, folstate, symbstate);
  array_Init();
  sharer = sharing_Init(termstate);
  state = state_Create(mem, folstate, termstate, symbstate, subststate, congrstate, clock, sharer);
  top_STATE = state;
  signal(SIGINT, top_SigHandler);
  signal(SIGTERM, top_SigHandler);
  signal(SIGSEGV, top_SigHandler);
  signal(SIGALRM, top_SigHandler);
  state_SetSeconds(state, seconds);
  if (state_GetSeconds(state)>0)
    alarm(state_GetSeconds(state)+1);
  state_SetFilename(state, inputfile);
  state_SetAbstraction(state, abstraction);
  if (parseTPTP) {
    memory_SetOutOfMemString(mem, "SZS status MemoryOut");
    tptpParsstate = tptparse_Init(folstate, subststate, state_GetFolType(state), state_GetNamehash(state));
    tptpstate = tptp_Init(tptparse_StateGetMemory(tptpParsstate), folstate);
    tptp_SetProblemName(tptpstate, inputfile);
    ARRAY formulae = tptparse_ParseFile(tptpParsstate, fileinput, inputfile);
    top_TransferTPTP(state, tptpParsstate, tptpstate, formulae, skolemLevel);
    tptparse_Delete(tptpParsstate);
  }
  else {
    misc_StartUserErrorReport();
    misc_UserErrorReport("\n In %s:", __func__);
    misc_UserErrorReport("\n SPASS-SCL-FOL does not support classical parsing.\n");
  }
  state_SetNumberInputClauses(state, array_GetSize(state_GetClauses(state)));
  fclose(fileinput);
  if (onlycnf) {
    for (i = 0; i < array_GetSize(state_GetClauses(state)); ++i) {
      CLAUSE cla = (CLAUSE)array_GetElement(state_GetClauses(state), i);
      printf("\n");
      tptp_FPrintCNF(stdout, folstate, cla,
       tptp_GetClauseName(tptpstate, clause_GetId(cla)),
       TPTP_RolePlain);
    }
    printf("\n");
    exit(0);
  }
  for (i = 0; i < array_GetSize(state_GetClauses(state)); ++i) {
    clause = (CLAUSE)array_GetElement(state_GetClauses(state), i);
    for (j = 0; j < clause_GetFolLiterals(clause); ++j) {
      term_ResetPropertyRec(clause_GetAtom(clause, j));
    }
  }
  maxvar = 0;
  for (i=0;i<array_GetSize(state_GetClauses(state));i++)
    maxvar = (clause_GetVariables((CLAUSE)array_GetElement(state_GetClauses(state), i)) > maxvar ? clause_GetVariables((CLAUSE)array_GetElement(state_GetClauses(state), i)) : maxvar);
  if (maxvar * 3 > subst_MaxVars(subststate))
    subst_StateUpdate(mem, symbstate, subststate, maxvar * 3);
  if (array_GetSize(state_GetConstants(state)) == 0)
    state_SetConstants(state, array_Add(state_GetMemory(state), state_GetConstants(state), symbol_CreateConstant(state_GetSymbstate(state), fol_TypeTop(state_GetFolstate(state)), "a")));
  if (array_GetSize(state_GetClauses(state)) != 0) {
    if (array_GetSize(state_GetConstants(state)) == 0)
      state_AddConstant(state,symbol_CreateConstant(symbstate, fol_TypeTop(folstate), "mina"));
    for(i=0;i<array_GetSize(state_GetConstants(state));i++) {
      symbol_SetWeight(symbstate, (SYMBOL)state_GetConstants(state)[i], 1);
      symbol_SetPrecedence(symbstate, (SYMBOL)state_GetConstants(state)[i], i+1);
    }
    j=i;
    for(i=0;i<array_GetSize(state_GetFunctions(state));i++,j++) {
      symbol_SetWeight(symbstate, (SYMBOL)state_GetFunctions(state)[i], 1);
      symbol_SetPrecedence(symbstate, (SYMBOL)state_GetFunctions(state)[i], j+1);
    }
    for(i=0;i<array_GetSize(state_GetPredicates(state));i++,j++) {
      symbol_SetWeight(symbstate, (SYMBOL)state_GetPredicates(state)[i], 1);
      symbol_SetPrecedence(symbstate, (SYMBOL)state_GetPredicates(state)[i], j+1);
    }
    kstate = kbo_Init(termstate, (SYMBOL)array_GetElement(state_GetConstants(state),0), symbol_GetMaxVars(symbstate), 1);
    state_AnalyzeInputClauses(state);
    if (state_IsEqualityProblem(state)) {
      fcnfClauses = state_GetClauses(state);
      for (i=0;i<array_GetSize(fcnfClauses);i++) {
 clause = (CLAUSE)array_GetElement(fcnfClauses, i);
 for (j=0;j<clause_GetFolLiterals(clause); j++) {
   atom = clause_GetAtom(clause,j);
   if (symbol_Equal(term_TopSymbol(atom), fol_Equal(folstate)) &&
       term_IsGround(termstate, atom) &&
       kbo_Smaller(kstate, term_FirstArgument(atom), term_SecondArgument(atom))) {
     term_SwapArguments(atom, 0, 1);
   }
 }
      }
    }
    printf("\n%% Input Clauses:");
    for (i=0;i<array_GetSize(state_GetClauses(state));i++) {
      printf("\n%% ");
      clause = (CLAUSE)array_GetElement(state_GetClauses(state), i);
      clause_Print(termstate, clause);
    }
    printf("\n");
    printf("\n%% Predicates: ");
    for (i=0;i<array_GetSize(state->predicates);i++) {
      symbol_Print(state->symbstate, (SYMBOL)(state->predicates)[i]); printf(" ");
    }
    printf("\n%% Fol Constants: ");
    for (i=0;i<array_GetSize(state->constants);i++) {
      symbol_Print(state->symbstate, (SYMBOL)(state->constants)[i]); printf(" ");
    }
    printf("\n%% Fol Functions: ");
    for (i=0;i<array_GetSize(state->functions);i++) {
      symbol_Print(state->symbstate, (SYMBOL)(state->functions)[i]); printf(" ");
    }
    printf("\n");
    state_PrintProblemProperties(state);
    if (state_IsEqualityProblem(state)) {
      printf("\n\n%% SZS status GaveUp\n");
      printf("\n%% SPASS beiseite: SPASS does currently not support equality problems.\n");
      state_PrintStatistics(state);
      for (i=0; i<array_GetSize(state_GetClauses(state));i++)
        clause_Delete(mem, termstate, (CLAUSE)(state_GetClauses(state))[i]);
      array_Delete(mem, state_GetClauses(state));
      state_SetClauses(state, NULL);
      kbo_Free(kstate);
      state_Delete(state);
      fol_Free(folstate);
      term_TermstateFree(termstate);
      symbol_Free(symbstate);
      subst_StateDelete(mem, subststate);
      congrbs_Done(congrstate);
#ifdef SPASS_CHECK
      memory_Print(mem, '%');
      memory_PrintLeaks(mem);
#endif
      memory_Delete(mem);
      return 2;
    }
    state_SetCandidates(state, array_Create(mem, array_GetSize(state_GetClauses(state))/2));
    fred_InitialStateRedundancy(state);
    printf("\n%% Reduced Input Axiom Clauses:");
    printf("\n");
    for (i=0;i<array_GetSize(state_GetClauses(state));i++) {
      clause = (CLAUSE)array_GetElement(state_GetClauses(state), i);
      if (clause_IsMarkedConjecture(clause))
 state_AddConClause(state, clause);
      else {
 printf("%% ");
 clause_Print(termstate, clause);
 printf("\n");
      }
    }
    printf("\n%% Reduced Input Conjecture Clauses:");
    for (i=0;i<array_GetSize(state_GetConClauses(state));i++) {
      printf("\n%% ");
      clause = (CLAUSE)array_GetElement(state_GetConClauses(state), i);
      clause_Print(termstate, clause);
    }
    printf("\n");
    if (!state_GetEmptyClause(state) && array_GetSize(state_GetClauses(state)) > 0) {
      state_SetUsables(state, array_Create(mem, array_GetSize(state_GetClauses(state))/2));
      state_SetWorkedOff(state, array_Create(mem, array_GetSize(state_GetClauses(state))/2));
      state_SetLastClauseId(state, state_GetId(state));
      top_ShareClauses(state, sharer, state->clauses);
      if (heuristics_mode == META_NONE) {
        heuristics_mode = meta_PickHeuristic(state);
      }
      csolver_Solve(state, kstate, &top_FINISH, heuristics_mode);
      printf("\n%% Solver is done. Empty clause found\n");
      if (state_GetTptpstate(state) != NULL && state_GetEmptyClause(state)) {
        tptp_PrintDerivationWrapper(state_GetTptpstate(state),
                                    state_GetClauses(state),
                                    state_GetRedClauses(state),
                                    state_GetEmptyClause(state));
      }
    }
    else {
      if (array_GetSize(state_GetClauses(state)) > 0) {
 printf("\n%% SZS status %s\n", state_GetSZSUnsatStatus(state));
        if (state_GetTptpstate(state) != NULL) {
          tptp_PrintDerivationWrapper(state_GetTptpstate(state),
                                      state_GetClauses(state),
                                      state_GetRedClauses(state),
                                      state_GetEmptyClause(state));
        }
      }
      else
 printf("\n%% SZS status %s\n", state_GetSZSSatStatus(state));
      state_PrintStatistics(state);
      for (i=0; i<array_GetSize(state_GetClauses(state));i++)
 clause_Delete(mem, termstate, (CLAUSE)(state_GetClauses(state))[i]);
      array_Delete(mem, state_GetClauses(state));
      state_SetClauses(state, NULL);
    }
    kbo_Free(kstate);
  }
  clock_StopPassedTime(clock, clock_OVERALL);
  printf("\n%% Needed:       ");
  clock_PrintTime(clock, clock_OVERALL);
  printf("\n");
  top_STATE = (SCLSTATE)NULL;
  state_Delete(state);
  fol_Free(folstate);
  term_TermstateFree(termstate);
  symbol_Free(symbstate);
  subst_StateDelete(mem, subststate);
  congrbs_Done(congrstate);
  memory_Print(mem, '%');
#ifdef SPASS_CHECK
  memory_Print(mem, '%');
  memory_PrintLeaks(mem);
#endif
  memory_Delete(mem);
}
