#include <stdio.h>
#include "term.h"
#include "error.h"
#define MAXSTRING 100



termlist parseargs(FILE *infile, int *length);
int skipspaces(FILE *infile);
void printtermlist(termlist tl);
void freeterm(term t);
void freelist(termlist tl);
term parseterm(FILE *infile);
char *stringCat(char *s1, char*s2);
char *renamevar(char *var, int suffix);
term renametermvars(term t,int suffix);
termlist renametlistvars(termlist tl,int suffix);
term parselist(FILE *infile);
void printlist(term t);
int skiptoendofcomment(FILE *infile);

termlist parseargs(FILE *infile, int *length) {
  char next;
  termlist returnlist;
  
  
  

  next=skipspaces(infile);
  if (next == ')') {
    *length = 0;
    return NULL;
  }
  ungetc(next,infile);
  returnlist = (termlist) malloc(sizeof(struct _termlist));
  returnlist->first = parseterm(infile);
  next = skipspaces(infile);
  if (next == ',') {
    returnlist->rest = parseargs(infile,length);
    *length += 1;
  } else if (next == ')' || next == '.') {
    returnlist->rest = NULL;
    *length = 1;
    ungetc(next,infile);
  } else {
    error("Parse Error \n");
  }
  return returnlist;
}

int skiptoendofcomment(FILE *infile) {
  int next;
  next = getc(infile);
  for(;;) {
    if (next == EOF) return EOF;
    if (next == '*') {
      next = getc(infile);
      if (next == EOF) return EOF;
      if (next == '/') return getc(infile);
    } else {
      next = getc(infile);
    }
  }
}

int skipspaces(FILE *infile) {
  int next, next2;
  do {
    next = getc(infile);
    if (next==EOF) return EOF;
    if (next == '/') {
      if ((next2 = getc(infile)) == '*') {
	next = skiptoendofcomment(infile);
	if (next == EOF) return EOF;
      } else {
	ungetc(next2,infile);
	return next;
      }
    }
  } while (next == '\n' || next == ' ' || next == '\t');
  return next;
}


term parseterm(FILE *infile) {
  
  char buffer[MAXSTRING];
  char next;
  term returnterm;
  int i = 0;
  term tlistelem;

  next = skipspaces(infile);

  if (next == '[') {
    return parselist(infile);
  }
  


  do {
    buffer[i++] = next;
    next = getc(infile);
  } while (next != '\n' && next != ' ' && next != '\t' &&
	   next != ')' && next != '(' && next != '.' && next != ',' && next != ']' && next != '|');
  buffer[i++] = '\0';
  returnterm = (term) malloc(sizeof(struct _term));
  returnterm->name = (char *) malloc(sizeof(char)*i);
  strcpy(returnterm->name,buffer);
  if ((returnterm->name[0] >= 'A' && returnterm->name[0] <= 'Z') || returnterm->name[0] == '_') {
    returnterm->variable = 1;
    returnterm->arity = 0;
    ungetc(next,infile);
    return returnterm;
  } else {
    returnterm->variable = 0;
  }
  if (next == '(') {
    returnterm->args = parseargs(infile, &(returnterm->arity));
    if (skipspaces(infile) != ')')
      error("Parse Error:  ) expected\n");
  } else {
    returnterm->args = NULL;
    returnterm->arity = 0;
    ungetc(next,infile);
  }
  return returnterm;
}

void printtermlist(termlist tl) {
  if (tl != NULL) {
    printterm(tl->first);
    if (tl->rest != NULL) {
      printf(",");
      printtermlist(tl->rest);
    }
  }
}

void printterm(term t) {
  if (t == NULL) {
    printf("Empty Term");
  } else {
    if (strcmp(t->name,".") == 0) {
      printf("[");
      printlist(t);
      printf("]");
    } else {
      printf("%s",t->name);
      if (t->arity != 0) {
	printf("(");
	printtermlist(t->args);
	printf(")");
      }
    }
  }
}


void freelist(termlist tl) {
  if (tl != NULL) {
    freeterm(tl->first);
    freelist(tl->rest);
    free(tl);
  }
}

void freeterm(term t) {
  if (t!= NULL) {
    free(t->name);
    freelist(t->args);
    free(t);
  }

}

termlist gettermlist(FILE *infile) {
  termlist tl;
  int dummy;
  dummy = skipspaces(infile);
  if (dummy == EOF)
    return NULL;
  ungetc(dummy,infile);
  tl = parseargs(infile,&dummy);
  if (skipspaces(infile) != '.') {
    error("Parse error: . expected\n");
  }
  return tl;

}

term getterm(FILE *infile) {
  term t;
  t = parseterm(infile);
  if (skipspaces(infile) != '.') {
    error("Parse error: . expected\n");
  }
  return t;
}


void printrule(rule t) {
  if (t == NULL) {
    printf("Empty Rule\n");
  } else {
    printterm(t->consequent);
    if (t->antecedent == NULL)
      printf(".");
    else {
      printf(":-");
      printtermlist(t->antecedent);
      printf(".");
    }
  }
}


void freerule(rule t) {
  if (t != NULL) {
    freeterm(t->consequent);
    freelist(t->antecedent);
    free(t);
  }
}


rule getrule(FILE *infile) {
  rule returnrule;
  termlist l;
  char next;
  int first;

  first = skipspaces(infile);
  if (first == EOF) return NULL;
  ungetc(first, infile);
  returnrule = (rule) malloc(sizeof(struct _rule));
  returnrule->consequent = parseterm(infile);
  next = skipspaces(infile);
  if (next == '.') {
    returnrule->antecedent = NULL;
    return returnrule;
  }
  if (next != ':' || (char) getc(infile) != '-') {
    error("Error in parsing rule:   :- expected\n");
    return NULL;
  }
  returnrule->antecedent = parseargs(infile,&(returnrule->numantecedent));
  if (skipspaces(infile) != '.') 
    error("Error in parsing rule:   . expected\n");
  if (returnrule->consequent->variable) {
    error("Consequent needs to be a term, not a variable \n");
  }
  
  for (l=returnrule->antecedent; l; l=l->rest) {
    if (l->first->variable)
      error("Antecedent cannot be a variable\n");
  }
  return returnrule;
}


rule renamevars(rule r) {
  static int currentnum = -1;
  
  rule returnrule;
  currentnum++;
  
  returnrule = (rule) malloc(sizeof(struct _rule));
  returnrule->consequent = renametermvars(r->consequent,currentnum);
  returnrule->antecedent = renametlistvars(r->antecedent,currentnum);
  return returnrule;
}


char *renamevar(char *var, int suffix) {
  char buffer[100];
  sprintf(buffer,"_%d",suffix);
  return stringCat(var,buffer);
}


char *stringCat(char *s1, char*s2){
  char *retval;

  retval = (char *) malloc(sizeof(char)*(strlen(s1)+strlen(s2)+1));
  strcpy(retval,s1);
  strcat(retval,s2);
  return retval;
}
term renametermvars(term t,int suffix) {
  term returnterm;

  if (t == NULL) return NULL;
  returnterm = (term) malloc(sizeof(struct _term));
  if (t->variable) {
    returnterm->variable = 1;
    returnterm->name = renamevar(t->name,suffix);
    returnterm->arity = 0;
    returnterm->args = NULL;
  } else {
    returnterm->variable = 0;
    returnterm->name = t->name;
    returnterm->arity = t->arity;
    returnterm->args = renametlistvars(t->args,suffix);
  }
  return returnterm;
}
termlist renametlistvars(termlist tl,int suffix){
  termlist returnlist;

  if (tl == NULL) return NULL;
  returnlist = (termlist) malloc(sizeof(struct _termlist));
  returnlist->first = renametermvars(tl->first,suffix);
  returnlist->rest = renametlistvars(tl->rest,suffix);
  return returnlist;
}

term parselist(FILE *infile) {
  term listelem;
  term listhead;
  char next;

  listhead =(term) malloc(sizeof(struct _term));
  next = skipspaces(infile);
  if (next == ']') {
    listhead->name = (char *) malloc(3 * sizeof(char));
    strcpy(listhead->name,"[]");
    listhead->variable  = 0;
    listhead->arity = 0;
    listhead->args = NULL;
  } else {
    ungetc(next,infile);
    listelem = parseterm(infile);
    listhead->name = (char *) malloc(2 * sizeof(char));
    strcpy(listhead->name,".");
    listhead->variable  = 0;
    listhead->arity = 2;
    listhead->args = (termlist) malloc(sizeof(struct _termlist));
    listhead->args->rest = (termlist) malloc(sizeof(struct _termlist));
    listhead->args->rest->rest = NULL;
    listhead->args->first = listelem;
    next = skipspaces(infile);
    if (next == ',') {
      listhead->args->rest->first = parselist(infile);
    } else if (next == '|') {
      listhead->args->rest->first = parseterm(infile);
      if (skipspaces(infile) != ']')
	error("Parse error: ] expected\n");
    } else if (next == ']') {
      ungetc(next,infile);
      listhead->args->rest->first = parselist(infile);      
    }

  }
  return listhead;
}

void printlist(term t) {
    if (strcmp(t->name,".") != 0) {
      printf("|");
      printterm(t);
      return;
    }
    printterm(t->args->first);
    if (strcmp(t->args->rest->first->name,"[]") != 0)
      printf(",");
      printlist(t->args->rest->first);
}