/* lp_dtpr.c
 * Neil Moore <neil@cs.uky.edu>
 *  2 Mar 2000
 *
 * Part of liblp.  See COPYRIGHT for copyright information.
 */
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <lp.h>
#include <lp_dtpr.h>
#include <lp_util.h>

static void lp_dt_print_headers(FILE *dt, FILE *thc, FILE *dc, const char *in, const char *base);

static char *errmsg=0;
static int err_alloc=0;

/* Print a logic program as a default theory.  Returns 0 on success,
 * nonzero on failure.  infilename is the name of the input file (used
 * only in generating headers); basename is the base name of the output
 * files to generate.  Files named basename.dt (default theory),
 * basename.dc (defaults), and basename.thc (theory) are generated.
 *
 * Each rule in prog of the form:
 *   a :- b1, . . ., bn, not c1, . . ., not cm.
 * becomes a default of the form:
 *   b1 & . . . & bn : !c1, . . ., !c2
 *           -> a
 * (split across multiple lines).
 *
 * The DeReS documentation has more information on the format of the
 * various files.
 *
 */
int lp_dt_print_program(struct lp *prog, const char *infilename, const char *basename)
{
	char *buff;
	int fnlen, bnlen;
	FILE *f, *w, *d;
	int i, len;

	lp_assert(prog);
	lp_assert(infilename);
	lp_assert(basename);
	
	bnlen = strlen(basename);
	fnlen = bnlen + strlen(".thc");
	
	buff = lp_malloc(fnlen + 1);
	strncpy(buff, basename, bnlen + 1);
	
	strncpy(buff + bnlen, ".dt", 4);
	if((f = fopen(buff, "w")) == NULL) {
		/* we assume strerror() produces no more than 1024 characters */
		len = strlen("could not open  for writing:  ") + bnlen + 3 + 1024 + 1;
		
		if(err_alloc)
			free(errmsg);
		err_alloc=1;
		errmsg = lp_malloc(len);
		sprintf(errmsg, "could not open %s for writing: %s", buff, strerror(errno));
		goto failure;
	}

	strncpy(buff + bnlen, ".dc", 4);
	if((d = fopen(buff, "w")) == NULL) {
		/* we assume strerror() produces no more than 1024 characters */
		len = strlen("could not open  for writing:  ") + bnlen + 3 + 1024 + 1;
		
		if(err_alloc)
			free(errmsg);
		err_alloc=1;
		errmsg = lp_malloc(len);
		sprintf(errmsg, "could not open %s for writing: %s", buff, strerror(errno));
		goto failure;
	}
	
	strncpy(buff + bnlen, ".thc", 5);
	if((w = fopen(buff, "w")) == NULL) {
		/* we assume strerror() produces no more than 1024 characters */
		len = strlen("could not open  for writing:  ") + bnlen + 4 + 1024 + 1;
		
		if(err_alloc)
			free(errmsg);
		err_alloc=1;
		errmsg = lp_malloc(len);
		sprintf(errmsg, "could not open %s for writing: %s\n", buff, strerror(errno));
		goto failure;
	}

	lp_dt_print_headers(f, w, d, infilename, basename);
	
	for(i=0; i<prog->nrules; i++) {
		lp_dt_print_default(d, prog, &prog->rules[i]);
	}

	fclose(w);
	fclose(d);
	fclose(f);
	
	free(buff);
	return 0;
 failure:
	free(buff);
	return -1;
}


/* Print the headers to each of the dt, thc, and dc files.  This is all
 * that needs to be done for the dt file.
 */
void lp_dt_print_headers(FILE *dt, FILE *thc, FILE *dc, const char *in, const char *base) 
{
	fprintf(dt, "%% Output of sm2dt on input %s\n", in);
	fprintf(dt, " w = %s\n", base);
	fprintf(dt, " d = %s\n", base);

	fprintf(thc, "%% Output of sm2dt on input %s\n\n", in);
	
	fprintf(dc, "%% Output of sm2dt on input %s\n\n", in);
}


/* Print a rule as a default.  See lp_dt_print_program for the format.
 */
void lp_dt_print_default(FILE *d, struct lp *prog, struct lp_rule *rule) 
{
	int i;

	if(rule->npos)
		fprintf(d, "%s", rule->pos[0]->name);
	if(rule->npos > 1)
		for(i=1; i<rule->npos; i++)
			fprintf(d, " && %s", rule->pos[i]->name);

	fprintf(d, " : ");
	
	if(rule->nneg) {
		if(rule->neg[0]->name[0] != '!')
			fprintf(d, "!%s", rule->neg[0]->name);
		else
			fprintf(d, "%s", rule->neg[0]->name+1);
	}

	if(rule->nneg > 1) {
		for(i=1; i<rule->nneg; i++)
			if(rule->neg[i]->name[0] != '!')
				fprintf(d, ", !%s", rule->neg[i]->name);
			else
				fprintf(d, ", %s", rule->neg[i]->name+1);
	}

	fprintf(d, "\n      -> %s;\n\n", rule->head->name);
}


/* Return the error message generated by the last call to lp_dt_print_program.
 */
const char *lp_dtpr_error(void) 
{
	return errmsg;
}

