/*
 *  SAD / Haigha / spass.c -- interface for the SPASS prover
 *  Copyright (C) 2001-2004  SAD Development Team (see ../README)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "main.h"

static FILE * in;
static FILE * out;

static void spass_in(Formula * fl, ui time);
static void spass_out();

void spass_call(int argc, char ** argv, Formula * fl, ui time)
{
    int fch1[2], fch2[2];
    char ** sp_argv; ui cpid;

    sp_argv = malloc((argc + 2)*sizeof(char*));
    memcpy(sp_argv, argv, argc*sizeof(char*));
    sp_argv[argc] = "-Stdin"; sp_argv[argc + 1] = NULL;

    if (pipe(fch1) == -1) { perror("spass_call"); return; }
    if (pipe(fch2) == -1) { perror("spass_call"); return; }

    if (!(cpid = fork()))
    {
        dup2(fch1[0], 0); close(fch1[1]);
        dup2(fch2[1], 1); close(fch2[0]);
        execvp(sp_argv[0],sp_argv);
        perror("spass_call"); return;
    }
    if (cpid == -1) { perror("spass_call"); return; }

    in  = fdopen(fch2[0], "r"); close(fch2[1]);
    out = fdopen(fch1[1], "w"); close(fch1[0]);
// out = stderr;
    spass_in(fl, time); spass_out();
}

static char * spass_sym(char c)
{
    switch (c) {
        case '+' : return "true";       case '-' : return "false";
        case '=' : return "equal";      case '!' : return "not";
        case '&' : return "and";        case '|' : return "or";
        case '>' : return "implies";    case '~' : return "equiv";
        case '@' : return "forall";     case '$' : return "exists"; }
    return NULL;
}

static void spass_term(Term * t)
{
    char * s, c = sym_get(t->name)[0];

    switch (c) {
        case ':' :  spass_term(ARG2(t)); break;
        case '@' : case '$' :
                    fprintf(out, "%s([%s_],", spass_sym(c), sym_get(ARG1(t)->name));
                    spass_term(ARG2(t)); fputc(')', out); break;
        default  :  s = spass_sym(c);
                    fputs(s ? s : sym_get(t->name), out);
                    if (t->vr) fputc('_', out);
                    if (!t->ch) break; fputc('(', out);
                    spass_term(t->ch); fputc(')', out);
    }
    if (t->br) { fputc(',', out); spass_term(t->br); }
}

static void spass_in(Formula * fl, ui time)
{
    symlist * l; Formula * f;
    fputs("begin_problem(SAD).list_of_descriptions. name({*SAD*}).\n", out);
    fputs("author({*Alice*}).status(unknown).description({*SAD*}).", out);
    fputs("end_of_list.\nlist_of_symbols.\n", out);
    if (funs) { fputs("functions[", out); forlist(l, funs) {
        fprintf(out, "(%s,%i)", l->str, l->arity);
        if (l->next) fputc(',', out); } fputs("].\n", out); }
    if (prds) { fputs("predicates[", out); forlist(l, prds) {
        fprintf(out, "(%s,%i)", l->str, l->arity);
        if (l->next) fputc(',', out); } fputs("].\n", out); }
    fputs("end_of_list.\nlist_of_formulae(axioms).\n", out);
    forlist(f,fl->next)
    {
        fputs("formula(", out); spass_term(f->fterm);
        fprintf(out, ",%s).\n", (f->mark[0] == '#') ? "_" : f->mark);
    }
    fputs("end_of_list.\nlist_of_formulae(conjectures).\n", out);
    fputs("formula(", out); spass_term(fl->fterm); fputs(",_).\n", out);
    fputs("end_of_list.\nlist_of_settings(SPASS).{*\n", out);
    fprintf(out, "set_flag(TimeLimit,%i).\n", time);
    fputs("*}end_of_list.\nend_problem.\n", out); fclose(out);
}

static void spass_out()
{
    char c, res = 0;
    int prv = 0, unp = 0, bnd = 0, tim = 0;
    char * prvs = "\nSPASS beiseite: Proof found.";
    char * unps = "\nSPASS beiseite: Completion found.";
    char * bnds = "\nSPASS beiseite: Maximal number of loops exceeded.";
    char * tims = "\nSPASS beiseite: Ran out of time.";

    while ((c = fgetc(in)) != EOF)
    {
        putchar(c); if (res) continue;
        if (c == prvs[prv]) { if (!prvs[++prv]) res = '+'; } else prv = 0;
        if (c == unps[unp]) { if (!unps[++unp]) res = '!'; } else unp = 0;
        if (c == bnds[bnd]) { if (!bnds[++bnd]) res = '_'; } else bnd = 0;
        if (c == tims[tim]) { if (!tims[++tim]) res = '-'; } else tim = 0;
    }
    printf("/\n%c\n", res);
}
