/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

/*
 * Defines TADS vocabulary (player command parser) functionality
 */

#ifndef GLK_TADS_TADS2_VOCABULARY
#define GLK_TADS_TADS2_VOCABULARY

#include "common/util.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/tads2/property.h"
#include "glk/tads/tads2/run.h"

namespace Glk {
namespace TADS {
namespace TADS2 {

/*
 *   Cover macro for parser errors.  Any parser error should be covered
 *   with this macro for documentation and search purposes.  (The macro
 *   doesn't do anything - this is just something to search for when we're
 *   trying to enumerate parser error codes.) 
 */
#define VOCERR(errcode) errcode

/* maximum number of objects matching an ambiguous word */
#define VOCMAXAMBIG 200

/* size of input buffer */
#define VOCBUFSIZ 128

/*
 *   Vocabulary relation structure - this structure relates a vocabulary
 *   word to an object and part of speech.  A list of these structures is
 *   attached to each vocabulary word structure to provide the word's
 *   meanings.
 */
struct vocwdef {
    uint   vocwnxt;      /* index of next vocwdef attached to the same word */
    objnum vocwobj;                      /* object associated with the word */
    uchar  vocwtyp;   /* property associated with the word (part of speech) */
    uchar  vocwflg;                                   /* flags for the word */
#define VOCFCLASS  1                          /* word is for a class object */
#define VOCFINH    2                 /* word is inherited from a superclass */
#define VOCFNEW    4                          /* word was added at run-time */
#define VOCFDEL    8                               /* word has been deleted */
};

/* vocabulary word structure */
struct vocdef {
    vocdef *vocnxt;                         /* next word at same hash value */
    uchar   voclen;                                   /* length of the word */
    uchar   vocln2;          /* length of second word (0 if no second word) */
    uint    vocwlst;      /* head of list of vocwdef's attached to the word */
    uchar   voctxt[1];                                  /* text of the word */
};

/* vocabulary inheritance cell */
struct vocidef {
    uchar            vocinsc;   /* # of superclasses (gives size of record) */
    union {
        struct {
            uchar    vociusflg;                          /* flags for entry */
#define VOCIFCLASS  1  /* entry refers to a class object (loc records only) */
#define VOCIFVOC    2                 /* entry has vocabulary words defined */
#define VOCIFXLAT   4  /* superclasses must be translated from portable fmt */
#define VOCIFLOCNIL 8                  /* location is explicitly set to nil */
#define VOCIFNEW    16       /* object was allocated at run-time with "new" */
            objnum   vociusloc;                   /* location of the object */
            objnum   vociusilc;                       /* inherited location */
            objnum   vociussc[1];                  /* array of superclasses */
        } vocius;
        vocidef     *vociunxt;
    } vociu;
#define   vociflg vociu.vocius.vociusflg
#define   vociloc vociu.vocius.vociusloc
#define   vociilc vociu.vocius.vociusilc
#define   vocisc  vociu.vocius.vociussc
#define   vocinxt vociu.vociunxt
};

/* size of a page in a vocabulary pool */
#define VOCPGSIZ  8192

/* number of bytes in an inheritance cell page */
#define VOCISIZ 8192

/* maximum number of inheritance pages */
#define VOCIPGMAX 32

/* maximum number of inheritance pages (256 objects per page) */
#define VOCINHMAX 128

/* size of vocabulary hash table */
#define VOCHASHSIZ  256

/* size of a template structure */
#define VOCTPLSIZ 10

/* new-style template structure */
#define VOCTPL2SIZ  16


/*
 *   vocwdef's are fixed in size.  They're allocated in a set of arrays
 *   (the voccxwp member of the voc context has the list of arrays).  Each
 *   array is of a fixed number of vocwdef entries; a maximum number of
 *   vocwdef arrays is possible. 
 */
#define VOCWPGSIZ   2000                   /* number of vocwdef's per array */
#define VOCWPGMAX   16                  /* maximum number of vocwdef arrays */

/*
 *   To find a vocwdef entry given its index, divide the index by the
 *   number of entries per array to find the array number, and use the
 *   remainder to find the index within that array. 
 */
/*#define VOCW_IN_CACHE*/
#ifdef VOCW_IN_CACHE
vocwdef *vocwget(struct voccxdef *ctx, uint idx);
#else
#define vocwget(ctx, idx) \
    ((idx) == VOCCXW_NONE ? (vocwdef *)0 : \
      ((ctx)->voccxwp[(idx)/VOCWPGSIZ] + ((idx) % VOCWPGSIZ)))
#endif

/*
 *   Special values for vocdtim - these values indicate that the daemon
 *   does not have a normal turn-based expiration time. 
 */
#define VOCDTIM_EACH_TURN  0xffff            /* the daemon fires every turn */

/* daemon/fuse/alarm slot */
struct vocddef {
    objnum   vocdfn;             /* object number of function to be invoked */
    runsdef  vocdarg;                  /* argument for daemon/fuse function */
    prpnum   vocdprp;             /* property number (used only for alarms) */
    uint     vocdtim;  /* time for fuses/alarms (0xffff -> each-turn alarm) */
};

/* vocabulary object list entry */
struct vocoldef {
    objnum  vocolobj;                           /* object matching the word */
    const char *vocolfst;     /* first word in cmd[] that identified object */
    const char *vocollst;      /* last word in cmd[] that identified object */
    char   *vocolhlst;      /* hypothetical last word, if we trimmed a prep */
    int     vocolflg;                           /* special flags (ALL, etc) */
};

/* vocabulary context */
struct voccxdef {
    errcxdef  *voccxerr;                          /* error handling context */
    tiocxdef  *voccxtio;                                /* text i/o context */
    runcxdef  *voccxrun;                               /* execution context */
    mcmcxdef  *voccxmem;                          /* memory manager context */
    objucxdef *voccxundo;                                   /* undo context */
    uchar     *voccxpool;                  /* next free byte in vocdef pool */
    vocdef    *voccxfre;                        /* head of vocdef free list */
    char      *voccxcpp;                   /* pointer to compound word area */
    int        voccxcpl;                    /* length of compound word area */
    char      *voccxspp;                    /* pointer to special word area */
    int        voccxspl;                     /* length of special word area */
    uint       voccxrem;        /* number of bytes remaining in vocdef pool */
    vocidef  **voccxinh[VOCINHMAX];     /* vocidef page table: 256 per page */
    uchar     *voccxip[VOCIPGMAX];                 /* inheritance cell pool */
    vocidef   *voccxifr;              /* head of inheritance cell free list */
    uint       voccxiplst;          /* last inheritance cell page allocated */
    uint       voccxilst;      /* next unused byte in last inheritance page */
    int        voccxredo;                   /* flag: redo command in buffer */

    /* 
     *   redo buffer - if voccxredo is set, and this buffer is not empty,
     *   we'll redo the command in this buffer rather than the one in our
     *   internal stack buffer 
     */
    char       voccxredobuf[VOCBUFSIZ];

    /*
     *   "again" buffer - when we save the last command for repeating via
     *   the "again" command, we'll save the direct and indirect object
     *   words here, so that they can be recovered if "again" is used 
     */
    char       voccxagainbuf[VOCBUFSIZ];

    vocdef    *voccxhsh[VOCHASHSIZ];                          /* hash table */

#ifdef VOCW_IN_CACHE
    mcmon      voccxwp[VOCWPGMAX];        /* list of pages of vocab records */
    mcmon      voccxwplck;                  /* locked page of vocab records */
    vocwdef   *voccxwpgptr;             /* pointer to currently locked page */
#else
    vocwdef   *voccxwp[VOCWPGMAX];                  /* vocabulary word pool */
#endif

    uint       voccxwalocnt;             /* number of vocwdef's used so far */
    uint       voccxwfre;            /* index of first vocwdef in free list */
#define VOCCXW_NONE  ((uint)(-1))     /* index value indicating end of list */

    vocddef   *voccxdmn;                           /* array of daemon slots */
    uint       voccxdmc;                 /* number of slots in daemon array */
    vocddef   *voccxfus;                             /* array of fuse slots */
    uint       voccxfuc;                   /* number of slots in fuse array */
    vocddef   *voccxalm;                            /* array of alarm slots */
    uint       voccxalc;                  /* number of slots in alarm array */
    char       voccxtim[26];            /* game's timestamp (asctime value) */
    
    objnum     voccxvtk;                /* object number of "take" deepverb */
    objnum     voccxme;                      /* object number of "Me" actor */
    objnum     voccxme_init;                     /* initial setting of "Me" */
    objnum     voccxstr;                       /* object number of "strObj" */
    objnum     voccxnum;                       /* object number of "numObj" */
    objnum     voccxit;                                  /* last "it" value */
    objnum     voccxhim;                                /* last "him" value */
    objnum     voccxher;                                /* last "her" value */
    objnum     voccxthc;                   /* count of items in "them" list */
    objnum     voccxthm[VOCMAXAMBIG];            /* list of items in "them" */
    objnum     voccxprd;                 /* "pardon" function object number */
    objnum     voccxpre;               /* "preparse" function object number */
    objnum     voccxppc;            /* "preparseCmd" function object number */
    objnum     voccxpre2;           /* "preparseExt" function object number */
    objnum     voccxvag;                             /* "again" verb object */
    objnum     voccxini;                                 /* "init" function */
    objnum     voccxper;             /* "parseError" function object number */
    objnum     voccxprom;             /* "cmdPrompt" function object number */
    objnum     voccxpostprom;     /* "cmdPostPrompt" function object number */
    objnum     voccxpdis;                         /* parseDisambig function */
    objnum     voccxper2;                           /* parseError2 function */
    objnum     voccxperp;                       /* parseErrorParam function */
    objnum     voccxpdef;                          /* parseDefault function */
    objnum     voccxpdef2;                      /* parseDefaultExt function */
    objnum     voccxpask;                           /* parseAskobj function */
    objnum     voccxpask2;                     /* parseAskobjActor function */
    objnum     voccxpask3;                  /* parseAskobjIndirect function */
    objnum     voccxinitrestore;    /* "initRestore" function object number */
    objnum     voccxpuv;         /* parseUnknownVerb function object number */
    objnum     voccxpnp;          /* parseNounPhrase function object number */
    objnum     voccxpostact;           /* postAction function object number */
    objnum     voccxprecmd;            /* preCommand function object number */
    objnum     voccxendcmd;            /* endCommand function object number */

    /* current command word list values */
    vocoldef  *voccxdobj;                /* current direct object word list */
    vocoldef  *voccxiobj;              /* current indirect object word list */

    /* current command objects */
    objnum     voccxactor;                                 /* current actor */
    objnum     voccxverb;                       /* current command deepverb */
    objnum     voccxprep;                    /* current command preposition */
    
    /* previous command values - used by "again" */
    objnum     voccxlsa;                                  /* previous actor */
    objnum     voccxlsv;                                   /* previous verb */
    vocoldef   voccxlsd;                          /* previous direct object */
    vocoldef   voccxlsi;                        /* previous indirect object */
    objnum     voccxlsp;                                     /* preposition */
    int        voccxlssty;              /* style (new/old) of last template */
    uchar      voccxlst[VOCTPL2SIZ];                            /* template */

    objnum     voccxpreinit;                            /* preinit function */

    /* special flags */
    uchar      voccxflg;
#define VOCCXFCLEAR    1      /* ignore remainder of command line (restore) */
#define VOCCXFVWARN    2                /* generate redundant verb warnings */
#define VOCCXFDBG      4           /* debug mode:  show parsing information */
#define VOCCXAGAINDEL  8             /* "again" lost due to object deletion */

    /* number of remaining unresolved unknown words in the command */
    int        voccxunknown;

    /* total number of unresolved words in the last command */
    int        voccxlastunk;

    /* parser stack area */
    uchar *voc_stk_ptr;
    uchar *voc_stk_cur;
    uchar *voc_stk_end;
};

/* allocate and push a list, returning a pointer to the list's memory */
uchar *voc_push_list_siz(voccxdef *ctx, uint lstsiz);

/* push a list of objects from a vocoldef array */
void voc_push_vocoldef_list(voccxdef *ctx, vocoldef *objlist, int cnt);

/* push a list of objects from an objnum array */
void voc_push_objlist(voccxdef *ctx, objnum objlist[], int cnt);

/* change the player character ("Me") object */
void voc_set_me(voccxdef *ctx, objnum new_me);

/* add a vocabulary word */
void vocadd(voccxdef *ctx, prpnum p, objnum objn,
            int classflag, char *wrdval);

/* internal addword - must already be split into two words and lengths */
void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg,
             uchar *wrd1, int len1, uchar *wrd2, int len2);

/* delete vocabulary for a given object */
void vocdel(voccxdef *ctx, objnum objn);

/* lower-level vocabulary deletion routine */
void vocdel1(voccxdef *ctx, objnum objn, char *wrd, prpnum prp,
             int really_delete, int revert, int keep_undo);

/* delete all inherited vocabulary */
void vocdelinh(voccxdef *ctx);

/* allocate space for an inheritance record if needed */
void vocialo(voccxdef *ctx, objnum obj);

/* add an inheritance/location record */
void vociadd(voccxdef *ctx, objnum obj, objnum loc,
             int numsc, objnum *sc, int flags);

/* delete inheritance records for an object */
void vocidel(voccxdef *ctx, objnum chi);

/* renumber an object's inheritance records - used for 'modify' */
void vociren(voccxdef *ctx, objnum oldnum, objnum newnum);

/* caller-provided context structure for vocffw/vocfnw searches */
struct vocseadef {
    vocdef  *v;
    vocwdef *vw;
    const uchar *wrd1;
    int      len1;
    const uchar *wrd2;
    int      len2;
};

/* find first word matching a given word */
vocwdef *vocffw(voccxdef *ctx, const char *wrd, int len, const char *wrd2, int len2,
                int p, vocseadef *search_ctx);

/* find next word */
vocwdef *vocfnw(voccxdef *voccx, vocseadef *search_ctx);

/* read a line of input text */
int vocread(voccxdef *ctx, objnum actor, objnum verb,
            char *buf, int bufl, int type);
#define VOCREAD_OK    0
#define VOCREAD_REDO  1

/* compute size of a vocoldef list */
int voclistlen(vocoldef *lst);

/* tokenize an input buffer */
int voctok(voccxdef *ctx, char *cmd, char *outbuf,
           char **wrd, int lower, int cvt_ones, int show_errors);

/* get types for a word list */
int vocgtyp(voccxdef *ctx, char **cmd, int *types, char *orgbuf);

/* execute a player command */
int voccmd(voccxdef *ctx, char *cmd, uint cmdlen);

/* disambiguator */
int vocdisambig(voccxdef *ctx, vocoldef *outlist, vocoldef *inlist,
                prpnum defprop, prpnum accprop, prpnum verprop,
                char *cmd[], objnum otherobj, objnum cmdActor,
                objnum cmdVerb, objnum cmdPrep, char *cmdbuf,
                int silent);

/* display a multiple-object prefix */
void voc_multi_prefix(voccxdef *ctx, objnum objn,
                      int show_prefix, int multi_flags,
                      int cur_index, int count);

/* low-level executor */
int execmd(voccxdef *ctx, objnum actor, objnum prep,
           char *vverb, char *vprep, vocoldef *dolist, vocoldef *iolist,
           char **cmd, int *typelist,
           char *cmdbuf, int wrdcnt, uchar **preparse_list, int *next_start);

/* recursive command execution */
int execmd_recurs(voccxdef *ctx, objnum actor, objnum verb,
                  objnum dobj, objnum prep, objnum iobj,
                  int validate_dobj, int validate_iobj);

/* try running preparseCmd user function */
int try_preparse_cmd(voccxdef *ctx, char **cmd, int wrdcnt,
                     uchar **preparse_list);

/*
 *   Handle an unknown verb or sentence structure.  We'll call this when
 *   we encounter a sentence where we don't know the verb word, or we
 *   don't know the combination of verb and verb preposition, or we don't
 *   recognize the sentence structure (for example, an indirect object is
 *   present, but we don't have a template defined using an indirect
 *   object for the verb).
 *   
 *   'wrdcnt' is the number of words in the cmd[] array.  If wrdcnt is
 *   zero, we'll automatically count the array entries, with the end of
 *   the array indicated by a null pointer entry.
 *   
 *   If do_fuses is true, we'll execute the fuses and daemons if the
 *   function exists and doesn't throw an ABORT error, or any other
 *   run-time error other than EXIT.
 *   
 *   This function calls the game-defined function parseUnknownVerb, if it
 *   exists.  If the function doesn't exist, we'll simply display the
 *   given error message, using the normal parseError mechanism.  The
 *   function should use "abort" or "exit" if it wants to cancel further
 *   processing of the command.
 *   
 *   We'll return true if the function exists and executes successfully,
 *   in which case normal processing should continue with any remaining
 *   command on the command line.  We'll return false if the function
 *   doesn't exist or throws an error other than EXIT, in which case the
 *   remainder of the command should be aborted.  
 */
int try_unknown_verb(voccxdef *ctx, objnum actor,
                     char **cmd, int *typelist, int wrdcnt, int *next_start,
                     int do_fuses, int err, const char *msg, ...);

/* find a template */
int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep,
              uchar *tplout, int *newstyle);

/* build a printable name for an object from the words in a command list */
void voc_make_obj_name(voccxdef *ctx, char *namebuf, char *cmd[],
                       int firstwrd, int lastwrd);
void voc_make_obj_name_from_list(voccxdef *ctx, char *namebuf,
                                 char *cmd[], const char *firstwrd, const char *lastwrd);

/*
 *   check noun - determines whether the next set of words is a valid noun
 *   phrase.  No complaint is issued if not; this check is generally made
 *   to figure out what type of sentence we're dealing with.  This is
 *   simple; we just call vocgobj() with the complaint flag turned off.
 */
/* int vocchknoun(voccxdef *ctx, char **cmd, int *typelist, int cur,
                  int *next, vocoldef *nounlist, int chkact); */
#define vocchknoun(ctx, cmd, typelist, cur, next, nounlist, chkact) \
 vocgobj(ctx, cmd, typelist, cur, next, FALSE, nounlist, TRUE, chkact, 0)
#define vocchknoun2(ctx, cmd, typlst, cur, next, nounlist, chkact, nomatch) \
 vocgobj(ctx, cmd, typlst, cur, next, FALSE, nounlist, TRUE, chkact, nomatch)

/*
 *   get noun - reads an object list.  We simply call vocgobj() with the
 *   complaint and multiple-noun flags turned on.
 */
/* int vocgetnoun(voccxdef *ctx, char **cmd, int *typelist, int cur,
                  int *next, vocoldef *nounlist); */
#define vocgetnoun(ctx, cmd, typelist, cur, next, nounlist) \
 vocgobj(ctx, cmd, typelist, cur, next, TRUE, nounlist, TRUE, FALSE, 0)

/* get object */
int vocgobj(voccxdef *ctx, char **cmd, int *typelist, int cur,
            int *next, int complain, vocoldef *nounlist,
            int multi, int chkact, int *nomatch);

/* tokenize a string - TADS program code interface */
void voc_parse_tok(voccxdef *ctx);

/* get token types - TADS program code interface */
void voc_parse_types(voccxdef *ctx);

/* get objects matching all of the given words - TADS program code interface */
void voc_parse_dict_lookup(voccxdef *ctx);

/* parse a noun list - TADS program code interface */
void voc_parse_np(voccxdef *ctx);

/* disambiguate a noun list - TADS program code interface */
void voc_parse_disambig(voccxdef *ctx);

/* replace the current command - TADS program code interface */
void voc_parse_replace_cmd(voccxdef *ctx);

/* check access to an object */
int vocchkaccess(voccxdef *ctx, objnum obj, prpnum verprop,
                 int seqno, objnum actor, objnum verb);

/* check to see if an object is visible */
int vocchkvis(voccxdef *ctx, objnum obj, objnum cmdActor);

/* display an appropriate message for an unreachable object */
void vocnoreach(voccxdef *ctx, objnum *list1, int cnt,
                objnum actor, objnum verb, objnum prep, prpnum defprop,
                int show_multi_prefix, int multi_flags,
                int multi_base_index, int multi_total_count);

/* set {numObj | strObj}.value, as appropriate */
void vocsetobj(voccxdef *ctx, objnum obj, dattyp typ, const void *val,
               vocoldef *inobj, vocoldef *outobj);

/* macros to read values out of templates */
#define voctplpr(tpl) ((objnum)osrp2(((uchar *)tpl)))        /* preposition */
#define voctplvi(tpl) ((prpnum)osrp2(((uchar *)tpl) + 2))      /* verIoVerb */
#define voctplio(tpl) ((prpnum)osrp2(((uchar *)tpl) + 4))         /* ioVerb */
#define voctplvd(tpl) ((prpnum)osrp2(((uchar *)tpl) + 6))      /* verDoVerb */
#define voctpldo(tpl) ((prpnum)osrp2(((uchar *)tpl) + 8))         /* doVerb */
#define voctplflg(tpl) (*(((uchar *)tpl) + 10))                    /* flags */

/* flag values for the voctplflg */
#define VOCTPLFLG_DOBJ_FIRST   0x01     /* disambiguate direct object first */


/* word type flags */
#define VOCT_ARTICLE  1
#define VOCT_ADJ      2
#define VOCT_NOUN     4
#define VOCT_PREP     8
#define VOCT_VERB     16
#define VOCT_SPEC     32            /* special words - "of", ",", ".", etc. */
#define VOCT_PLURAL   64
#define VOCT_UNKNOWN  128                                /* word is unknown */

/* special type flags */
#define VOCS_ALL     1                                             /* "all" */
#define VOCS_EXCEPT  2                                          /* "except" */
#define VOCS_IT      4                                              /* "it" */
#define VOCS_THEM    8                                            /* "them" */
#define VOCS_NUM     16                                         /* a number */
#define VOCS_COUNT   32                   /* a number being used as a count */
#define VOCS_PLURAL  64                                           /* plural */
#define VOCS_ANY     128                                           /* "any" */
#define VOCS_HIM     256                                           /* "him" */
#define VOCS_HER     512                                           /* "her" */
#define VOCS_STR     1024                                /* a quoted string */
#define VOCS_UNKNOWN 2048           /* noun phrase contains an unknown word */
#define VOCS_ENDADJ  4096        /* word matched adjective at end of phrase */
#define VOCS_TRUNC   8192    /* truncated match - word is leading substring */
#define VOCS_TRIMPREP 16384 /* trimmed prep phrase: assumed it was for verb */

/* special internally-defined one-character word flags */
#define VOCW_AND     ','
#define VOCW_THEN    '.'
#define VOCW_OF      'O'
#define VOCW_ALL     'A'
#define VOCW_BOTH    'B'
#define VOCW_IT      'I'
#define VOCW_HIM     'M'
#define VOCW_ONE     'N'
#define VOCW_ONES    'P'
#define VOCW_HER     'R'
#define VOCW_THEM    'T'
#define VOCW_BUT     'X'
#define VOCW_ANY     'Y'

/* structure for special internal word table */
struct vocspdef {
    const char *vocspin;
    char  vocspout;
};

/* check if a word is a special word - true if word is given special word */
/* int vocspec(char *wordptr, int speccode); */
#define vocspec(w, s) (*(w) == (s))

/* 
 *   Set a fuse/daemon/notifier.
 */
void vocsetfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop,
              uint tm, runsdef *val, int err);

/* remove a fuse/daemon/notifier */
void vocremfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop,
              runsdef *val, int err);

/* count a turn (down all fuse/notifier timers) */
void vocturn(voccxdef *ctx, int turncnt, int do_fuses);

/* initialize voc context */
void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx,
            runcxdef *runctx, objucxdef *undoctx, int fuses,
            int daemons, int notifiers);

/* clean up the voc context - frees memory allocated by vocini() */
void vocterm(voccxdef *vocctx);

/* allocate/free fuse/daemon/notifier array for voc ctx initialization */
void vocinialo(voccxdef *ctx, vocddef **what, int cnt);
void voctermfree(vocddef *what);

/* get a vocidef given an object number */
/* vocidef *vocinh(voccxdef *ctx, objnum obj); */
#define vocinh(ctx, obj) ((ctx)->voccxinh[(obj) >> 8][(obj) & 255])

/* revert all objects back to original state, using inheritance records */
void vocrevert(voccxdef *ctx);

/* clear all fuses/daemons/notifiers (useful for restarting) */
void vocdmnclr(voccxdef *ctx);

/* display a parser error message */
void vocerr(voccxdef *ctx, int err, const char *f, ...);

/* 
 *   display a parser informational error message - this will display the
 *   message whether or not we're suppressing messages due to unknown
 *   words, and should be used when providing information, such as objects
 *   we're assuming by default 
 */
void vocerr_info(voccxdef *ctx, int err, const char *f, ...);

/* client undo callback - undoes a daemon/fuse/notifier */
void vocdundo(void *ctx, uchar *data);

/* client undo size figuring callback - return size of client undo record */
ushort OS_LOADDS vocdusz(void *ctx, uchar *data);

/* save undo for object creation */
void vocdusave_newobj(voccxdef *ctx, objnum objn);

/* save undo for adding a word */
void vocdusave_addwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags,
                      char *wrd);

/* save undo for deleting a word */
void vocdusave_delwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags,
                      char *wrd);

/* save undo for object deletion */
void vocdusave_delobj(voccxdef *ctx, objnum objn);

/* save undo for changing the "Me" object */
void vocdusave_me(voccxdef *ctx, objnum old_me);

/* compute vocabulary word hash value */
uint vochsh(const uchar *t, int len);

/* TADS versions of isalpha, isspace, isdigit, etc */
#define vocisupper(c) ((uchar)(c) <= 127 && Common::isUpper((uchar)(c)))
#define vocislower(c) ((uchar)(c) <= 127 && Common::isLower((uchar)(c)))
#define vocisalpha(c) ((uchar)(c) > 127 || Common::isAlpha((uchar)(c)))
#define vocisspace(c) ((uchar)(c) <= 127 && Common::isSpace((uchar)(c)))
#define vocisdigit(c) ((uchar)(c) <= 127 && Common::isDigit((uchar)(c)))


/*
 *   Undo types for voc subsystem 
 */
#define VOC_UNDO_DAEMON   1                    /* fuse/daemon status change */
#define VOC_UNDO_NEWOBJ   2                              /* object creation */
#define VOC_UNDO_DELOBJ   3                              /* object deletion */
#define VOC_UNDO_ADDVOC   4                  /* add vocabulary to an object */
#define VOC_UNDO_DELVOC   5             /* delete vocabulary from an object */
#define VOC_UNDO_SETME    6                          /* set the "Me" object */


/*
 *   Our own stack.  We need to allocate some fairly large structures
 *   (for the disambiguation lists, mostly) in a stack-like fashion, and
 *   we don't want to consume vast quantities of the real stack, because
 *   some machines have relatively restrictive limitations on stack usage.
 *   To provide some elbow room, we'll use a stack-like structure of our
 *   own: we'll allocate out of this structure as needed, and whenever we
 *   leave a C stack frame, we'll also leave our own stack frame. 
 */

/* re-initialize the stack, allocating space for it if needed */
void  voc_stk_ini(voccxdef *ctx, uint siz);

/* enter a stack frame, marking our current position */
#define voc_enter(ctx, marker)  (*(marker) = (ctx)->voc_stk_cur)

/* leave a stack frame, restoring the entry position */
#define voc_leave(ctx, marker)  ((ctx)->voc_stk_cur = marker)

/* return a value */
#define VOC_RETVAL(ctx, marker, retval) \
       voc_leave(ctx, marker); return retval

/* allocate space from the stack */
void *voc_stk_alo(voccxdef *ctx, uint siz);

/* allocation cover macros */
#define VOC_STK_ARRAY(ctx, typ, var, cnt) \
    (var = (typ *)voc_stk_alo(ctx, (uint)((cnt) * sizeof(typ))))

#define VOC_MAX_ARRAY(ctx, typ, var) \
    VOC_STK_ARRAY(ctx, typ, var, VOCMAXAMBIG)

/*
 *   Stack size for the vocab stack.  We'll scale our stack needs based
 *   on the size of the vocoldef structure, since this is the most common
 *   item to be allocated on the vocab stack.  We'll also scale based on
 *   the defined VOCMAXAMBIG parameter, since it is the number of elements
 *   usually allocated.  The actual amount of space needed depends on how
 *   the functions in vocab.c and execmd.c work, so this parameter may
 *   need to be adjusted for changes to the player command parser. 
 */
#define VOC_STACK_SIZE  (16 * VOCMAXAMBIG * sizeof(vocoldef))

/*
 *   Execute all fuses and daemons, then execute the endCommand user
 *   function.  Returns zero on success, or ERR_ABORT if 'abort' was
 *   thrown during execution.  This is a convenient cover single function
 *   to do all end-of-turn processing; this calls exefuse() and exedaem()
 *   as needed, trapping any 'abort' or 'exit' errors that occur.
 *   
 *   If 'do_fuses' is true, we'll run fuses and daemons.  Otherwise, 
 */
int exe_fuses_and_daemons(voccxdef *ctx, int err, int do_fuses,
                          objnum actor, objnum verb,
                          vocoldef *dobj_list, int do_cnt,
                          objnum prep, objnum iobj);

/*
 *   Execute any pending fuses.  Return TRUE if any fuses were executed,
 *   FALSE otherwise.  
 */
int exefuse(voccxdef *ctx, int do_run);

/*
 *   Execute daemons 
 */
void exedaem(voccxdef *ctx);

/*
 *   Get the number and size of words defined for an object.  The size
 *   returns the total byte count from all the words involved.  Do not
 *   include deleted words in the count.  
 */
void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz);

/*
 *   Iterate through all words for a particular object, calling a
 *   function with each vocwdef found.  If objn == MCMONINV, we'll call
 *   the callback for every word.  
 */
void voc_iterate(voccxdef *ctx, objnum objn,
                 void (*fn)(void *, vocdef *, vocwdef *), void *fnctx);

/* ------------------------------------------------------------------------ */
/*
 *   disambiguation status codes - used for disambigDobj and disambigIobj
 *   methods in the deepverb
 */

/* continue with disambiguation process (using possibly updated list) */
#define VOC_DISAMBIG_CONT     1

/* done - the list is fully resolved; return with (possibly updated) list */
#define VOC_DISAMBIG_DONE     2

/* error - abort the command */
#define VOC_DISAMBIG_ERROR    3

/* parse string returned in second element of list as interactive response */
#define VOC_DISAMBIG_PARSE_RESP  4

/* already asked for an interactive response, but didn't read it yet */
#define VOC_DISAMBIG_PROMPTED 5


/* ------------------------------------------------------------------------ */
/*
 *   parseNounPhrase status codes 
 */

/* parse error occurred */
#define VOC_PNP_ERROR    1

/* use built-in default parser */
#define VOC_PNP_DEFAULT  2

/* successful parse */
#define VOC_PNP_SUCCESS  3


/* ------------------------------------------------------------------------ */
/*
 *   parserResolveObjects usage codes 
 */
#define VOC_PRO_RESOLVE_DOBJ  1                            /* direct object */
#define VOC_PRO_RESOLVE_IOBJ  2                          /* indirect object */
#define VOC_PRO_RESOLVE_ACTOR 3                                    /* actor */

} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

#endif