#include <kernel/kernel.h>

#include <phantasmal/parser.h>
#include <phantasmal/lpc_names.h>

private mapping article_map;
private mapping adverb_map;
private mapping iverb_map;
private mapping tverb_map;
private mapping noun_map;
private mapping adj_map;

        int     wordmap_init;
private mapping word_type_map;

        int     regenerate_grammar;

/*
 * We need to create a token-grammar which lists all appropriate words
 * by what parts of speech they are.  Specifically, it should have a
 * number of categories like 'words that are intransitive verbs,
 * nouns, and verbs taking an object, but nothing else'.  To do that
 * we'll need a list of all parts of speech in the listing, and to
 * know whether any given word is one or another part of speech.  */

string* parts_of_speech;


/* This stuff should correspond nicely to the parts_of_speech array.
   If not, you'll see some weird autogenerated grammars coming from
   the ParseD... */
#define ARTICLE_BIT       1
#define ADVERB_BIT        2
#define IVERB_BIT         4
#define TVERB_BIT         8
#define NOUN_BIT         16
#define ADJ_BIT          32

static void create(varargs int clone) {
}

void upgraded(varargs int clone) {
  mapping fake_bothverb_map;

  /* Add parts of speech carefully -- each one increases the amount of
     time you spend building the list.  To add one, you'll need to
     update this array.  You'll also need to add an XXX_BIT constant
     for it and add entries to several functions further down in this
     file.  Search for things like noun_map and NOUN_BIT to find
     them. */
  parts_of_speech = ({ "article", "adverb", "iverb", "tverb", "noun",
			 "adjective" });


  /*
   * Articles will probably merge with determiners later...
   * Possessive nouns, numbers, things like that.  That assumes
   * there's no overlap, but possessive nouns should all have an
   * apostrophe, and numbers and possessive pronouns should be a
   * known, fixed set.
   *
   * We could give them their own part of speech, but remember that
   * there's one step of making the grammar that takes time
   * exponential in the number of parts of speech.  Best not to be too
   * liberal in adding them.  Having, say, twelve or fourteen
   * different parts of speech could become too slow and violate the
   * rlimits() for the upgrade routine. */

  article_map = ([
		  "a"      : 1,
		  "the"    : 1,
		  ]);

  adverb_map = ([
		      "slowly"     : 1,
		      "quickly"    : 1,
		      "fast"       : 1,
		      "really"     : 1,
		      ]);

  fake_bothverb_map = ([
			"glance"   : 1,
			"look"     : 1,
			"exa"      : 1,
			"examine"  : 1,
			]);

  iverb_map = ([
		     "users"       : 1,
		     "who"         : 1,
		     "whoami"      : 1,
		     "typo"        : 1,
		     "bug"         : 1,
		     "idea"        : 1,
		     "inv"         : 1,
		     "inventory"   : 1,
		     ]) + fake_bothverb_map
    + ([
	"north"      : 1,
	"south"      : 1,
	"east"       : 1,
	"west"       : 1,
	]);

  tverb_map = ([
		     "get"         : 1,
		     "grab"        : 1,
		     "take"        : 1,
		     "drop"        : 1,
		     "put"         : 1,
		     "place"       : 1,
		     "remove"      : 1,
		     ]) + fake_bothverb_map;

  if(!noun_map) noun_map = ([ ]);
  if(!adj_map) adj_map = ([ ]);

  regenerate_grammar = 1;
}

int registered_as_word(string word) {
  if(!SYSTEM() && !COMMON() && !GAME())
    return 0;

  return !!word_type_map[word];
}

private void add_pos_to_wordmap(mapping wordmap, mapping posmap, int bits) {
  string* ind;
  int     ctr;

  ind = map_indices(posmap);
  for(ctr = 0; ctr < sizeof(ind); ctr++) {
    if(wordmap[ind[ctr]]) {
      wordmap[ind[ctr]] |= bits;
    } else {
      wordmap[ind[ctr]] = bits;
    }
  }
}

static mapping pvt_get_wordmap(void) {
  return word_type_map;
}

static void init_wordmap(void) {
  if(wordmap_init)
    return;

  word_type_map = ([ ]);

  add_pos_to_wordmap(word_type_map, article_map, ARTICLE_BIT);
  add_pos_to_wordmap(word_type_map, adverb_map, ADVERB_BIT);
  add_pos_to_wordmap(word_type_map, iverb_map, IVERB_BIT);
  add_pos_to_wordmap(word_type_map, tverb_map, TVERB_BIT);
  add_pos_to_wordmap(word_type_map, noun_map, NOUN_BIT);
  add_pos_to_wordmap(word_type_map, adj_map, ADJ_BIT);

  wordmap_init = 1;
}

static string make_string_from_pos_bits(int bits) {
  string *roots;

  if(!bits) return "";

  roots = ({ });
  if(bits & ARTICLE_BIT)
    roots += ({ "ar" });
  if(bits & ADVERB_BIT)
    roots += ({ "av" });
  if(bits & IVERB_BIT)
    roots += ({ "iv" });
  if(bits & TVERB_BIT)
    roots += ({ "tv" });
  if(bits & NOUN_BIT)
    roots += ({ "n" });
  if(bits & ADJ_BIT)
    roots += ({ "ad" });

  return implode(roots, "_");
}


/**** Functions to support word registration ****/

private void update_wordmap(string word, int bits, int do_add) {
  if(word_type_map[word]) {
    if(do_add) {
      word_type_map[word] |= bits;
    } else {
      word_type_map[word] &= ~bits;

      if(word_type_map[word] == 0)
	word_type_map[word] = nil;
    }
  } else {
    if(do_add) {
      word_type_map[word] = bits;
    } else {
      error("Can't remove a nonexistent word from word_type_map!");
    }
  }
}

/* TODO: some security for these functions */

void ref_noun(string noun) {
  if(noun_map[noun]) {
    noun_map[noun]++;
    return;
  }

  if(wordmap_init) {
    regenerate_grammar = 1;

    update_wordmap(noun, NOUN_BIT, 1);
  }
  noun_map[noun] = 1;
}

void unref_noun(string noun) {
  int val;

  if(!noun_map[noun] || (val = noun_map[noun]) < 1) {
    error("Unreferencing nonexistent noun '" + (noun ? "(nil)" : noun)
	  + "'!");
  }
  if(val == 1) {
    /* Remove entry */
    noun_map[noun] = nil;
    if(wordmap_init) {
      regenerate_grammar = 1;
      update_wordmap(noun, NOUN_BIT, 0);
    }
  } else {
    noun_map[noun]--;
  }
}

void ref_adj(string adj) {
  if(adj_map[adj]) {
    adj_map[adj]++;
    return;
  }

  if(wordmap_init) {
    regenerate_grammar = 1;
    update_wordmap(adj, ADJ_BIT, 1);
  }
  adj_map[adj] = 1;
}

void unref_adj(string adj) {
  int val;

  if(!adj_map[adj] || (val = adj_map[adj]) < 1) {
    error("Unreferencing nonexistent adjective '" + (adj ? "(nil)" : adj)
	  + "'!");
  }
  if(val == 1) {
    /* Remove entry */
    adj_map[adj] = nil;
    if(wordmap_init) {
      regenerate_grammar = 1;
      update_wordmap(adj, ADJ_BIT, 0);
    }
  } else {
    adj_map[adj]--;
  }
}

void register_verb(string verb, int transitive) {
  if(!SYSTEM() && !COMMON() && !GAME())
    error("Only privileged code can register new verbs!");

  if(transitive & VERB_TRANSITIVE) {
    tverb_map[verb] = 1;
    update_wordmap(verb, TVERB_BIT, 1);
  }

  if(transitive & VERB_INTRANSITIVE) {
    iverb_map[verb] = 1;
    update_wordmap(verb, IVERB_BIT, 1);
  }

  regenerate_grammar = 1;
}

void unregister_verb(string verb) {
  if(!SYSTEM() && !COMMON() && !GAME())
    error("Only privileged code can unregister verbs!");

  iverb_map[verb] = nil;
  tverb_map[verb] = nil;

  update_wordmap(verb, IVERB_BIT | TVERB_BIT, 0);

  regenerate_grammar = 1;
}
