/**
 * Compiles up the patterns for use by the add_command system.
 * @see /global/new_parse->add_command()
 * @author Pinkfish
 * @change 29-Jul-97, Jeremy
 *         changed default search order to ME-HERE, since
 *         this is what the "look" and "locate" commands use.
 */
#include <user_parser.h>
#define PATTERN_CACHE_SIZE 200
nosave mapping patterns;
nosave mapping pattern_short;
nosave string *pattern_order;
nosave int hits, reqs, cache_callout;
nomask mixed *compile_pattern(string str);
void create() {
   patterns = ([ "" : ({ 100 }) ]);
   pattern_short = ([ "" : "" ]);
   pattern_order = ({ });
} /* create() */
void clean_cache() {
  int i;
  for(i=0; i<(sizeof(pattern_order)-PATTERN_CACHE_SIZE); i++) {
    map_delete(patterns, pattern_order[i]);
    map_delete(pattern_short, pattern_order[i]);
  }
  pattern_order = pattern_order[i..];
  cache_callout = 0;
}
/**
 * Returns the compiled pattern to the caller.   This checks to see if they
 * pattern is in its internal cache and if it is, it uses that.
 * @param pattern the pattern to compiler
 * @return the compiler pattern
 * @see /include/user_parser.h
 */
nomask mixed *query_pattern(string pattern) {
  if (!patterns[pattern]) {
    patterns[pattern] = compile_pattern(pattern);
  } else
    hits++;
  pattern_order -= ({ pattern });
  pattern_order += ({ pattern });
  if(!cache_callout && (sizeof(pattern_order) > PATTERN_CACHE_SIZE))
    cache_callout = call_out("clean_cache", 300);
  reqs++;
  
  return patterns[pattern];
} /* query_pattern() */
/**
 * Compiles the pattern.   Does no cache checking.  This returns
 * only the compiled pattern, it also creates the short pattern as a
 * side effect.  The short pattern can be queried by using
 * query_short_pattern()
 * @param str the pattern to compile
 * @return the compiled pattern
 * @see /include/user_parser.h
 * @see query_short_pattern()
 * @see /global/new_parse.c
 */
nomask mixed *compile_pattern(string str) {
   mixed *pattern;
   mixed *bits;
   int i;
   int failed;
   int type;
   int weight;
   mixed env;
   string short;
   string short_bit;
   int pos;
   int pos2;
   int j;
   int k;
   bits = explode(str, " ") - ({ 0, "" });
   pattern = ({ });
   short = "";
   for (i = 0; i < sizeof(bits) && !failed; i++) {
      switch (bits[i][0]) {
      case '<' :
         pos = strsrch(bits[i], "'");
         if (pos != -1) {
             // Try to find the next one...
             pos2 = strsrch(bits[i][pos + 1..], "'", pos + 1);
             if (pos2 == -1) {
                /* Oh dear...  Look for it man! */
                for (j = i + 1; j < sizeof(bits); j++) {
                   pos2 = strsrch(bits[j], "'");
                   if (pos2 != -1) {
                      break;
                   }
                }
                if (j < sizeof(bits)) {
                   // Ok, found the dreadful thing.
                   for (k = i + 1; k <= j; k++) {
                      bits[i] += " " + bits[k];
                   }
                   // Nip up to the new one.
                   bits[j] = bits[i];
                   i = j;
                   pos2 = strsrch(bits[i], "'", -1);
                }
             } else {
                pos2 += pos + 1;
             }
             // We have it!  Cut out chunks.
             if (pos2 != -1) {
                short_bit = bits[i][pos+1..pos2-1];
                bits[i] = bits[i][0..pos-1] + bits[i][pos2+1..];
             } else {
                short_bit = 0;
             }
         } else {
             short_bit = 0;
         }
         bits[i] = bits[i][1..<2];
         bits[i] = explode(bits[i], ":");
         if (!bits[i]) {
            bits[i] = ({ "" });
         }
         switch (bits[i][0]) {
         case "direct" :
            weight += 7;
            if (sizeof(bits[i]) > 1) {
               switch (bits[i][1]) {
               case "living" :
                  type = LIVING;
                  break;
               case "distant-living" :
                  type = DISTANT_LIVING;
                  break;
               case "any-living" :
                  type = ANY_LIVING;
                  break;
               case "object" :
                  type = ANY_OBJECT;
                  break;
               case "player" :
                  type = TARGET_PLAYER;
                  break;
               default :
                  failed = 1;
                  printf("Unknown type of direct (%s).\n", bits[i][1]);
                  break;
               }
               if (!short_bit) {
                   short += "<"+bits[i][1]+"> ";
               } else {
                   short += "<" + short_bit + "> ";
               }
            } else {
               type = ANY_OBJECT;
               bits[i] += ({ "object" });
               if (!short_bit) {
                  short += "<object> ";
               } else {
                   short += "<" + short_bit + "> ";
               }
            }
            //env = ENV_HERE_ME;
            env = ENV_ME_HERE;
            if (sizeof(bits[i]) > 2)
               switch (bits[i][2]) {
               case "here" :
                  env = ENV_HERE;
                  break;
               case "me" :
                  env = ENV_ME;
                  break;
               case "direct-obs" :
                  env = ENV_DIRECT_OBS;
                  break;
               case "me-here" :
                  env = ENV_ME_HERE;
                  break;
               case "here-me" :
                  env = ENV_HERE_ME;
                  break;
               default :
                  env = bits[i][2];
                  break;
               }
            pattern += ({ DIRECT_OBJECT, type, env });
            break;
         case "indirect" :
            weight += 7;
            if (sizeof(bits[i]) > 1) {
               switch (bits[i][1]) {
               case "living" :
                  type = LIVING;
                  break;
               case "distant-living" :
                  type = DISTANT_LIVING;
                  break;
               case "any-living" :
                  type = ANY_LIVING;
                  break;
               case "object" :
                  type = ANY_OBJECT;
                  break;
               case "player" :
                  type = TARGET_PLAYER;
                  break;
               case "wiz-present" :
                  type = WIZ_PRESENT_TARGET;
                  break;
               default :
                  failed = 1;
                  printf("Unknown type of indirect (%s).\n", bits[i][1]);
                  break;
               }
               if (!short_bit) {
                  short += "<"+bits[i][1]+"> ";
               } else {
                   short += "<" + short_bit + "> ";
               }
            } else {
               type = ANY_OBJECT;
               bits[i] += ({ "object" });
               if (!short_bit) {
                  short += "<object> ";
               } else {
                   short += "<" + short_bit + "> ";
               }
            }
            //env = ENV_HERE_ME;
            env = ENV_ME_HERE;
            if (sizeof(bits[i]) > 2)
               switch (bits[i][2]) {
               case "here" :
                  env = ENV_HERE;
                  break;
               case "me" :
                  env = ENV_ME;
                  break;
               case "direct-obs" :
                  env = ENV_DIRECT_OBS;
                  break;
               case "me-here" :
                  env = ENV_ME_HERE;
                  break;
               case "here-me" :
                  env = ENV_HERE_ME;
                  break;
               default :
                  env = bits[i][2];
                  break;
               }
            pattern += ({ INDIRECT_OBJECT, type, env });
            break;
         case "string" :
            if (sizeof(bits[i]) > 1) {
               switch (bits[i][1]) {
               case "small" :
                  if (!short_bit) {
                     short += "<string> ";
                  } else {
                     short += "<" + short_bit + "> ";
                  }
                  weight += 6;
                  pattern += ({ SHORT_STRING });
                  break;
               case "long" :
                  if (!short_bit) {
                     short += "<string> ";
                  } else {
                     short += "<" + short_bit + "> ";
                  }
                  weight += 5;
                  pattern += ({ STRING });
                  break;
               case "quoted" :
                  if (!short_bit) {
                     short += "\"<string>\" ";
                  } else {
                     short += "\"<" + short_bit + ">\" ";
                  }
                  weight += 8;
                  pattern += ({ QUOTED_STRING });
                  break;
               default :
                  printf("Invalid type of string (%s).\n", bits[i][1]);
                  failed = 1;
                  break;
               }
            } else {
               if (!short_bit) {
                  short += "<string> ";
               } else {
                  short += "<" + short_bit + "> ";
               }
               weight += 5;
               pattern += ({ STRING });
            }
            break;
         case   "number" :
            if (!short_bit) {
               short += "<number> ";
            } else {
               short += "<" + short_bit + "> ";
            }
            weight += 10;
            pattern += ({ NUMBER });
            break;
         case "word" :
            if (!short_bit) {
               short += "<word> ";
            } else {
               short += "<" + short_bit + "> ";
            }
            weight += 7;
            pattern += ({ SINGLE_WORD });
            break;
         case "fraction" :
            if (!short_bit) {
               short += "<fraction> ";
            } else {
               short += "<" + short_bit + "> ";
            }
            weight += 10;
            pattern += ({ FRACTION });
            break;
         default :
            /* Only allows for made up word lists. */
            weight += 10;
            bits[i] = implode(bits[i], ":");
            if (master()->query_word_list(bits[i]) 
                  || this_player()->query_word_list(bits[i])) {
               if (!short_bit) {
                  short += "<"+bits[i]+"> ";
               } else {
                  short += "<" + short_bit + "> ";
               }
               pattern += ({ WORD_LIST, bits[i] });
            } else {
               printf("Unknown word list name (%s)\n", bits[i]);
               failed = 1;
            }
            /*
               else
               pattern += ({ WORD_LIST, explode(bits[i], "|") });
               */
            break;
         }
         break;
      case '{' :
         weight += 10;
         if (bits[i][<1] == '}') {
            pattern += ({ WORD_LIST, explode(bits[i][1..<2], "|") });
            short += bits[i]+" ";
         } else {
            int old = i;
            string elm, *res;
            for (++i; bits[i][<1] != '}'; i++);
            res = ({});
            foreach (elm in explode(implode(bits[old..i], " ")[1..<2], "|"))
               res += ({ explode(elm, " ") });
            pattern += ({ WORD_LIST_SPACES, res });
            short += implode(bits[old..i], " ")+" ";
         }
         break;
      case '[' :
         weight += 4;
         if (bits[i][1] == '<') {
            /* This is a possible wombat.   Hmm, what do we allow inside here? */
            /* For now don't allow things which have a return value */
            if (MASTER->query_word_list(bits[i][2..<3])
                  || this_player()->query_word_list(bits[i][2..<3]))
               pattern += ({ OPTIONAL, bits[i][2..<3] });
            else {                
               printf("Unknown word list name (%s)\n", bits[i]);
               failed = 1;
               pattern += ({ OPTIONAL, bits[i][2..<3] });
            }
            short += bits[i]+" ";
         } else if (bits[i][1] == '{') {
            if (bits[i][<1] == ']') {
               pattern += ({ OPTIONAL, explode(bits[i][2..<3], "|") });
               short += bits[i]+" ";
            } else {
               int old = i;
               string elm, *res;
               for (++i; bits[i][<1] != ']'; i++);
               res = ({});
               foreach (elm in explode(implode(bits[old..i], " ")[2..<3], "|"))
                  res += ({ explode(elm, " ") });
               pattern += ({ OPTIONAL_SPACES, res });
               short += implode(bits[old..i], " ")+" ";
            }
         } else if (bits[i][<1] == ']') {
            pattern += ({ OPTIONAL, explode(bits[i][1..<2], "|") });
            short += bits[i]+" ";
         } else {
            int old = i;
            string elm, *res;
            for (++i; bits[i][<1] != ']'; i++);
            res = ({});
            foreach (elm in explode(implode(bits[old..i], " ")[1..<2], "|"))
               res += ({ explode(elm, " ") });
            pattern += ({ OPTIONAL_SPACES, res });
            short += implode(bits[old..i], " ")+" ";
         }
         break;
      default :
         /* This is one of the few allowable things to stop a variable size 
             argument if they do not put {}'s around it.   It is as-is */
         weight += 10;
         short += bits[i]+" ";
         pattern += ({ WORD_LIST, ({ bits[i] }) });
         break;
      } /* switch() */
   } /* for() */
   if (failed) {
      return 0;
   }
   pattern_short[str] = short;
   return ({ weight }) + pattern;
} /* compile_pattern() */
/**
 * Returns the short pattern for the given pattern string.   The short pattern
 * is the message which is shown to the players.
 * @param str the pattern to get the short for
 * @return the short pattern
 */
string query_short_pattern(string str) {
  reqs++;
  if(!pattern_short[str])
    compile_pattern(str);
  else
    hits++;
  
  return pattern_short[str];
} /* query_short_pattern() */
mixed *stats() {
  return  ({
    ({ "patterns", sizeof(keys(pattern_short)), }),
        ({ "requests", reqs, }),
          ({ "cache hit percent", (hits * 100) / reqs, }),
            });
}