#include <cmph.h>
#include <string.h>
#include <assert.h>

#define DICT_STEP 10000
#define FILE_STEP 10000
#define DICWORD_LENGTH 32

#pragma pack(1)
typedef struct {
  char word[DICWORD_LENGTH];   /*  Dictionary word  */
  char tag[DICWORD_LENGTH];   /*  Corresponding tag  */
  int max_length;  /*  Length of the longest word  */
} dictionary_t;
#pragma pack(0)

typedef struct {
  unsigned int token_counter;
  char *tokenbuf;
} document_t;

dictionary_t *load_from_file (const char *filename, unsigned int totlines);

dictionary_t *load_from_file (const char *filename, unsigned int totlines) {
  dictionary_t *d, *dd;
  unsigned int dict_length, line_counter;
  FILE *pf;
  if (totlines != 0) dict_length = totlines;
  else dict_length = DICT_STEP;

  pf = fopen (filename, "r");
  if (pf == NULL) return NULL;  
  d = (dictionary_t *) malloc (dict_length * sizeof (dictionary_t));
  if (d == NULL) return d;
  line_counter = 0;
  while (!feof (pf)) {
    if (line_counter >= dict_length) {
      dict_length += DICT_STEP;
      dd = (dictionary_t *) realloc (d, dict_length * sizeof (dictionary_t));
      if (dd == NULL) return NULL;
      d = dd;
    }
    fscanf (pf , "%[^\t]\t%[^\t]\t%d\n", d[line_counter].word, d[line_counter].tag, &d[line_counter].max_length);
    line_counter ++;
  }
  fclose (pf);
  d = (dictionary_t *) realloc (d, (line_counter + 1) * sizeof (dictionary_t));
  d[line_counter].word[0] = '\0';
  d[line_counter].tag[0] = '\0';
  d[line_counter].max_length = 0;
  return d;
}

document_t *load_file_to_tag (const char *infile) {
  FILE *ipf;
  unsigned int tokens_length, token_counter;
  char buffer[2048], *dd, *tokenbuf;
  int i,j;
  document_t *doc;
  doc = (document_t *) malloc (sizeof (document_t));
  if (doc == NULL) return NULL;
  tokens_length = FILE_STEP;
  token_counter = 0;
  tokenbuf = (char *) malloc (tokens_length * (DICWORD_LENGTH * sizeof (char)));
  if (tokenbuf == NULL) return NULL;

  if ((ipf = fopen (infile, "r")) == NULL) return NULL;
  while(fgets (buffer, 2048, ipf) != NULL) {
    i = 0;
    j = 0;
    while (buffer[i] != '\0') {
      if (buffer[i] != ' ' && buffer[i] != '\n') {
	tokenbuf[(token_counter * DICWORD_LENGTH) + j] = buffer[i];
	j ++;  /*  Further considerations should be done if j > DICWORD_LENGTH  */
	assert (j <= DICWORD_LENGTH);
      }
      else {
	for (; j < DICWORD_LENGTH; j ++)   /*  Add padding  */
	  tokenbuf[(token_counter * DICWORD_LENGTH) + j] = '\0';
	token_counter ++;
	j = 0;
	/*  Check if memory should be extended  */
	if (token_counter >= tokens_length) {
	  tokens_length += FILE_STEP;
	  dd = (char *) realloc (tokenbuf, tokens_length * (DICWORD_LENGTH * sizeof (char)));
	  if (dd == NULL) return NULL;
	  tokenbuf = dd;
	}
      }
      i ++;
    }
  }
  /*  Update the last word if the file does not finish with \n  */
  if (j != 0) {
    for (; j < DICWORD_LENGTH; j ++)   /*  Add padding  */
      tokenbuf[(token_counter * DICWORD_LENGTH) + j] = '\0';
    token_counter ++;
  }
  /* resize memory  */
  tokenbuf = (char *) realloc (tokenbuf, token_counter * (DICWORD_LENGTH * sizeof (char)));
  if (tokenbuf == NULL) return NULL;
  fclose (ipf);
  doc->token_counter = token_counter;
  doc->tokenbuf = tokenbuf;
  return doc;
}

void dump (document_t *doc) {
  int i;
  for (i = 0; i < doc->token_counter * DICWORD_LENGTH; i ++) {
    if (doc->tokenbuf[i] == '\0') printf (" ");
    else printf ("%c",doc->tokenbuf[i]);
    if (((i + 1) % DICWORD_LENGTH) == 0) printf ("\n");
  }
}

void create_key (char *key, char *tokenbuf, int tot_words) {
  int i, j;
  j = 0;
  for (i = 0; i < DICWORD_LENGTH; i ++) key[i] = '\0';
  for (i = 0; i < tot_words * DICWORD_LENGTH; i ++) {
    if ((i % DICWORD_LENGTH) == 0 && i != 0) {
      key[j] = ' ';
      j ++;  /*  Insert space */
    } 
    if (tokenbuf[i] != '\0') {
      key[j] = tokenbuf[i];
      j ++;
    }
  }
}

void tag_document (document_t *doc, dictionary_t *dict, cmph_t *hash) {
  char *word, key[DICWORD_LENGTH];
  int hash_index, i, j, hash_multi; 
  word = doc->tokenbuf;
  for (i = 0; i < doc->token_counter; i ++) {
    hash_index = cmph_search (hash, word, DICWORD_LENGTH);
    if (strcmp (dict[hash_index].word, word) == 0) {
      printf ("<%s --> %s>\n", word, dict[hash_index].tag);
      /*  It does not loop if the tag is not prefix of others  */
      for (j = 1; j < dict[hash_index].max_length; j ++) {
	if (i + j >= doc->token_counter) continue; /*  prevent going outside the doc */
	create_key (key, word, j + 1);
	hash_multi = cmph_search (hash, key, DICWORD_LENGTH);
	if (strcmp (dict[hash_multi].word, key) == 0) {
	  printf ("<%s --> %s>\n", key, dict[hash_multi].tag);
	}
      }
    }
    else printf ("%s\n", word);
    word += DICWORD_LENGTH;
  }
}


int main (int argc, char **argv) {   
  dictionary_t *dict;
  unsigned int nkeys;
  FILE* mphf_fp;
  cmph_t *hash;
  char buff[256];
  document_t *doc;

  /*  Load sorted data  */
  sprintf (buff, "%s.sort", argv[1]);
  dict = load_from_file (buff, 0);  /*  Load keys  */
  for (nkeys = 0; dict[nkeys].max_length != 0; nkeys ++);  /*  Count keys  */
  /*  Load hash  */
  sprintf (buff, "%s.mph", argv[1]);
  mphf_fp = fopen (buff, "r");
  hash = cmph_load (mphf_fp);

  /*  do the job  */
  doc = load_file_to_tag (argv[2]);

  tag_document (doc, dict, hash);

  /* Destroy hash  */
  cmph_destroy (hash);
  fclose (mphf_fp);
  return 0;
}
