/* strmain.c
 * Neil Moore <neil@cs.uky.edu>
 *  1 Sep 1999
 *
 * Part of lpsms.  See COPYRIGHT for copyright information.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <lp.h>
#include <lp_lists.h>
#include <lp_smrd.h>
#include <lp_smpr.h>
#include <lp_util.h>
#include <lp_graph.h>

#ifndef VERSION
#define VERSION "unknown"
#endif

static int stratify(struct lp *prog, struct lp_rlist ***strata);
static void usage(void);
static void version(void);

int main(int argc, char *argv[]) 
{
	char *infilename = NULL;
	struct lp_rlist **strata=NULL;
	FILE *infile=NULL, *outfile=NULL, *strfile=NULL;
	int nstrata, len;
	clock_t start, end;
	int verbosity=1;
	int timing=1;

	char *outfilebase = NULL;
	char *outfilename = NULL;
	char *strfilename = NULL;

	struct lp *prog = NULL;

	while (*++argv != NULL) {
		if(!strcmp(*argv, "-o") || !strcmp(*argv, "--output")) {
			++argv;
			if(*argv == NULL) {
				usage();
				return 1;
			}
			outfilebase = *argv;
		} else if(!strncmp(*argv, "-o", 2)) {
			outfilebase = *argv + 2;
		} else if(!strncmp(*argv, "--output=", 9)) {
			outfilebase = *argv + 9;
		} else if(!strcmp(*argv, "-v") || !strcmp(*argv, "--version")) {
			version();
			return 0;
		} else if(!strcmp(*argv, "-h") || !strcmp(*argv, "--help")) {
			usage();
			return 0;
		} else if(!strcmp(*argv, "-V") || !strcmp(*argv, "--verbose")) {
			verbosity = 2;
		} else if(!strcmp(*argv, "-m") || !strcmp(*argv, "--messages")) {
			verbosity = 1;
		} else if(!strcmp(*argv, "-q") || !strcmp(*argv, "--quiet")) {
			verbosity = 0;
		} else if(!strcmp(*argv, "-t") || !strcmp(*argv, "--timing")) {
			timing = 1;
		} else if(!strcmp(*argv, "-n") || !strcmp(*argv, "--no-time")) {
			timing = 0;
		} else if(!strcmp(*argv, "-")) {
			infilename = NULL;
		} else if ((*argv)[0] == '-') {
			usage();
			goto failure;
		} else {
			infilename = *argv;
		}		
	}

	if(verbosity > 1 && infilename)
		fprintf(stderr, "Opening %s for reading...\n", infilename);
	
	if(infilename == NULL) {
		infile = stdin;
	} else if((infile = fopen(infilename, "r")) == NULL) {
		if(verbosity > 0)
			fprintf(stderr, "cannot open %s: %s\n", infilename, strerror(errno));
		goto failure;
	}

	if(outfilebase == NULL) {
		outfile = stdout;
	} else {
		len = strlen(outfilebase);
		outfilename = lp_malloc(sizeof(char) * (len + 5));
		strncpy(outfilename,outfilebase,len+1);
		strcat(outfilename, ".ord");
		
		if(verbosity > 1)
			fprintf(stderr, "Opening %s for writing...\n", outfilename);
	
		if((outfile = fopen(outfilename, "w")) == NULL) {
			if(verbosity > 0)
				fprintf(stderr, "cannot open %s: %s\n", outfilename, strerror(errno));
			goto failure;
		}
	}

	if(outfilebase == NULL) {
		if(infilename == NULL) {
			strfilename = "stratify.str";
		} else {
			len = strlen(infilename);
			strfilename = lp_malloc(sizeof(char) * (len + 5));
			strncpy(strfilename,infilename,len+1);
			strcat(strfilename, ".str");
		}
	} else {
		len = strlen(outfilebase);
		strfilename = lp_malloc(sizeof(char) * (len + 5));
		strncpy(strfilename,outfilebase,len+1);
		strcat(strfilename, ".str");
	}
	
	if(verbosity > 1)
		fprintf(stderr, "Opening %s for writing...\n", strfilename);
	
	if((strfile = fopen(strfilename, "w")) == NULL) {
		if(verbosity > 0)
			fprintf(stderr, "cannot open %s: %s\n", strfilename, strerror(errno));
		goto failure;
	}
	
	start=clock();
	prog = lp_malloc(sizeof(struct lp));
	
	if(verbosity > 1)
		fprintf(stderr, "Reading program...\n");
	if(lp_sm_read_file(infile, prog) < 0) {
		if(verbosity > 0) {
			const char *msg = lp_smrd_error();
			if(msg)
				fprintf(stderr, "%s\n", msg);
			fprintf(stderr, "error in input; output not generated\n");
		}
		goto failure;
	}

	if(verbosity > 1)
		fprintf(stderr, "Computing strata...\n");
	nstrata = stratify(prog, &strata);
	
	if(verbosity > 1)
		fprintf(stderr, "Outputting program...\n");
	lp_sm_print_program_str(outfile, strfile, prog, strata, nstrata);
	
	end = clock();
	if(timing)
		fprintf(stderr,"stratification: %f seconds\n", ((double)end - start)/ CLOCKS_PER_SEC);

	if(verbosity > 1)
		fprintf(stderr, "Cleaning up...\n");
	
	if (infile != stdin)
		fclose(infile);
	if(outfile != stdout)
		fclose(outfile);
	fclose(strfile);
	lp_free_lp(prog);
	return 0;
	
 failure:
	if(verbosity > 1)
		fprintf(stderr, "Cleaning up...\n");
	
	if(infile && infile != stdin)
		fclose(infile);
	if(outfile && outfile != stdout)
		fclose(outfile);
	if(strfile)
		fclose(strfile);
	if(prog)
		lp_free_lp(prog);
	return 1;
}

int stratify(struct lp *prog, struct lp_rlist ***strata)
{
	struct lp_vlist **components, *j;
	struct lp_graph *graph;
	
	int ncomps, nstrata=0, i, k;
	
	graph = lp_construct_strat_graph(prog);

	for(i=0; i<prog->natoms; ++i) {
		for(k=i+1; k<prog->natoms; ++k) {
			const char *ai = prog->atoms[i].name;
			const char *ak = prog->atoms[k].name;
			if((ai[0] == '!' && !strcmp(ai+1,ak)) ||
			   (ak[0] == '!' && !strcmp(ai,ak+1))) {
				lp_vl_add(&graph->vertices[i + prog->nrules].edges, &graph->vertices[k + prog->nrules]);
				lp_vl_add(&graph->vertices[k + prog->nrules].edges, &graph->vertices[i + prog->nrules]);
			}
		}
	}
	
	ncomps = lp_find_scc(graph, &components);
	*strata = lp_malloc(sizeof(struct rule *) * ncomps);
	
	for(i=0; i<ncomps; i++) {
		(*strata)[i] = NULL;
		
		for(j=components[i]; j!=NULL; j=j->next) {
			if(j->vertex->type == LP_DT_RULE)
				lp_rl_add(&((*strata)[nstrata]),(struct lp_rule *)j->vertex->data);			
		}
		if((*strata)[nstrata] != NULL)
			nstrata++;
	}

	for(i=0; i<ncomps; i++)
		lp_vl_free(components[i]);

	*strata = lp_realloc(*strata, sizeof(struct lp_rule *) * nstrata);
	
	free(components);
	lp_free_graph(graph);
	
	return nstrata;
}

void usage(void) 
{
	printf("usage: stratify [options] [filename]\n");
	printf("Compute relaxed stratification of a logic program.\n\n");
	
	printf(" -o, --output=file    output goes to file.{ord,str} (defaults to stdout for\n");
	printf("                      .ord, <input>.str for .str (or \"stratify.str\" if input\n");
	printf("                      is stdin))\n");
	printf(" -t, --time           print timing information to stderr (default)\n");
	printf(" -n, --no-time        do not print timing information\n");
	printf(" -V, --verbose        print additional information to stderr\n");
	printf(" -m, --messages       print status messages to stderr (default)\n");
	printf(" -q, --quiet          do not print information (other than timing)\n");
	printf(" -v, --version        print version information to stdout\n");
	printf(" -h, --help           display this message\n\n");
	
	printf("Input (in smodels format) is read from filename (defaults to stdin).\n");
}

void version(void) 
{
	printf("stratify %s, from lpsms %s, using liblp %s\n", VERSION, lp_sms_version(), lp_version());
}
