#include <stdlib.h>
# include <stdio.h>
# include "util.h"
# include "strings.h"
# include "gmap.h"

# include "tree.h"
# include "bv.h"
# include "maps.h"
# include "table.h"

# include "tpmg.h"

static char * buffer;
static int buffer_size;
static int buffer_used;

static int buffer_pointer;

static int beginning_of_patterns, beginning_of_labels;

VOID read_file(fd) int fd; {
   int quantity_used;

   buffer_used = 0;
   buffer_size = 1024;

   buffer = (char *) MALLOC (buffer_size);
   quantity_used = read(fd, buffer + buffer_used, buffer_size-buffer_used);
   while (quantity_used > 0) {
       buffer_used += quantity_used;
       if (buffer_used == buffer_size) {
          int new_size = 2 * buffer_size;
          char * new_buffer = (char *) MALLOC (new_size);
	  buffer_size--;
	  while (buffer_size >= 0) {
	      new_buffer[buffer_size] = buffer[buffer_size];
	      buffer_size--;
	      }
	  FREE(buffer);
	  buffer_size = new_size;
	  buffer = new_buffer;
          }
       quantity_used = read(fd, buffer + buffer_used,
				buffer_size-buffer_used);
       }
   if (quantity_used != 0) {    
       perror("User screwed");
       exit(666);
       }
   buffer_pointer = 0;
   close(fd);
   }

char read_char() {
   if (buffer_pointer < buffer_used) return buffer[buffer_pointer++];
   buffer_pointer++; return '#';
   }

VOID read_labels() {
   int i;
   /* discard text before first sharp */
   while (read_sharp() == 0) read_char();
   
   beginning_of_labels = buffer_pointer;
   
   /* skip forward to the patterns to read the labels first */
   while (read_sharp() == 0) read_char();

   beginning_of_patterns = buffer_pointer;
   
   while (read_sharp() == 0) {
       i = read_string();
       /* i is a wildcard label */
       while (read_eol() == 0) read_char();
       }
       
   /* at this point, every string entered into string_map is a wildcard */
   max_wildcard_index = g_map_size(string_map);
   
   /* "*" is a special wildcard, and need not be considered in the usual way */
   g_map(string_map, "*");

   buffer_pointer = beginning_of_labels;
   
   i = read_string();
   while (i != 0) {
       /* i is a label */
       read_number(); /* skip the arity this time */
       i = read_string();
       }
   
   buffer_pointer = beginning_of_patterns;

   /* Pump it full of patterns */
   while (read_sharp() == 0) {
       read_string();
       read_sep();
       while (read_eol() == 0) {
          read_string();
          }
       }
   
   /* at this point, string_map is full */
   tree_init(g_map_size(string_map) + 1);
   
   /* re-scan the labels to get the arities */
   buffer_pointer = beginning_of_labels;
   
   i = read_string();
   while (i != 0) {
       /* i is a label */
       label_arities[i] = read_number();
       if (i <= (int) max_wildcard_index && label_arities[i] != 0) {
           printf(
	"Read_labels, detected wildcard with arity > 0 near character %d\n",
                   buffer_pointer);
	   exit(666);
           }
       i = read_string();
       }

   /* Create the wildcard tree for possible use later */
   matchall_tree = tree_new(g_inmap(string_map,"*"));
 }

TREE read_tree() {
   LABEL a = read_string();
   TREE t;
   ARITY i;
   ARITY l;

   if (a == 0) {
      printf("Premature end of pattern detected at character %d\n",
		buffer_pointer);
      exit(666);
      }
   t = tree_new(a);
   l = label_arities[a];
   for (i = 0; i < l; i++) {
       KID(t,i) = read_tree();
       }
   return t;
   }

VOID read_patterns() {
   TREE t;
   /* skip ahead to the patterns and read in all the trees.
      Add them to the map */
   
   buffer_pointer = beginning_of_patterns;
   while (read_sharp() == 0) {
       while (read_sep() == 0) read_char();
       /* We are looking at a pattern now */
       t = read_tree();
       while (read_eol() == 0) {
           printf("Extraneous information after pattern, character %d\n",
	           buffer_pointer);
           exit(666);
           }
       g_map(pf_map,t);
       tree_free(t);
       }
   }

VOID read_wildcards() {

   /* Here we re-scan the patterns.  For each one, we discover (via read tree)
      which patterns subsume this wildcard (if there is one), and set them in
      a bit vector.  We also need to allocate a few goodies for the sake of
      the wildcards.
   */

   UINT i;
   TREE t;
   UINT wc;
   int change = 1;
   
   BV temp = bv_new();
   
   wc_subsumers = (BV *) MALLOC ((max_wildcard_index + 1) * sizeof (BV));
   wc_trees = (UINT *) MALLOC ((max_wildcard_index + 1) * sizeof (UINT));

   for (i = 1; i <= max_wildcard_index; i++) {
	wc_subsumers[i] = bv_new();
	t = tree_new(i);
	wc_trees[i] = g_inmap(pf_map, t);
	tree_free(t);
	}

   buffer_pointer = beginning_of_patterns;
   while (read_sharp() == 0) {
       /* wc is a label */
       wc = read_string();
       while (read_sep() == 0) read_char();
       /* We are looking at a pattern now */
       t = read_tree();
       i = g_inmap(pf_map,t);
       tree_free(t);
       if (wc != 0) {
/*        printf("Adding pattern %d as a subsumer for wildcard %d\n",
			i, wc); */
          bv_ins(wc_subsumers[wc],i);
	  }
       while (read_eol() == 0) read_char();
       }
       
   /* Take a sleazy transitive closure */
   while (change) {
       int j;
       change = 0;
       for (i = 1; i <= max_wildcard_index; i++) {
           for (j = 1; j <= (int) max_wildcard_index; j++) {
	      if (bv_in(wc_subsumers[i], wc_trees[j])) {
	          bv_andto(wc_subsumers[j], wc_subsumers[i], temp);
		  if (bv_eq(temp, wc_subsumers[j]) == 0) {
		     change = 1;
		     bv_orto(wc_subsumers[j], 
			     wc_subsumers[i],
			     wc_subsumers[i]);
		     }
	          }
	      }
           }
       }
   
   }

int read_sharp() {
   read_white();
   if (read_char() == '#') return 1;
   buffer_pointer--;
   return 0;
   }

int read_eol() {
   char c;
   read_white();
   c = read_char();
   switch(c) {
      case ';':
      case '#':
      return 1;
      }
   buffer_pointer--;
   return 0;
   }

int read_sep() {
   char c;
   read_white();
   c = read_char();
   switch(c) {
      case ':':
      case '#':
      return 1;
      }
   buffer_pointer--;
   return 0;
   }

VOID read_white() {

loop: switch(read_char()) {
      case ' ':
      case '\t':
      case '\n':
      case '\014':
      goto loop;
      }

   buffer_pointer--;
   }

UINT read_string() {
   char c = 0; /* Gcc is kinda stoopid about its warnings. */
   char buff[1000];
   int i = 0;

   read_white();

loop:
   c = read_char();
   switch(c) {   
      case ' ': case '\t': case '\n': case '\014':
      case ';': case ':': case '#':
              goto dontloop;
      }
   buff[i++] = c;
   goto loop;

dontloop:
   buffer_pointer--;
   buff[i] = 0;
   if (i == 0) return 0;   
   return g_map(string_map,buff);
   }

UINT read_number() {
   int i = 0;
   char c;

   read_white();
   
loop: c = read_char();
   switch(c) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
	 i = i * 10 + (c - '0');
         goto loop;
      }
   buffer_pointer--;
   return i;
   }
