/*
    See license.txt in the root of this project.
*/

# ifndef LMT_PRIMITIVE_H
# define LMT_PRIMITIVE_H

/*tex

    This is a list of origins for primitive commands. The engine starts out with hardly anything
    enabled so as a first step one should enable the \TEX\ primitives, and additional \ETEX\ and
    \LUATEX\ primitives. Maybe at some moment we should just enable all by default.

*/

typedef enum command_origin {
    tex_command        = 0x01,
    etex_command       = 0x02,
    luatex_command     = 0x04,
    luametatex_command = 0x08,
    no_command         = 0x10
} command_origin;

/*tex 

    Take |\fontdimen| which makes no sense any more. We can just replace it and for old times 
    sake handle it but a macro package can then block it. Or all the display math related 
    features that we don't use in \CONTEXT: we can optionally wipe them runtime. Commands like
    |\accent| make no sense any more. The same is true for the old school math font and pre 
    \UNICODE\ definitions and commands. The array variants can replace the singular ones. The \
    ignored ones are just \unknown\ ignored. 

*/

typedef enum command_legacy {
    no_legacy       = 0x00,
    /* these can be disabled (low level token blocked to) */
    text_legacy     = 0x01, /* makes no sense in new fonts    : accent             */
    math_legacy     = 0x02, /* sort of limited to eight bit   : left delimiter     */
    /* these are just tags (for manuals etc) */
    callback_legacy = 0x04, /* not implemented, callback      : font shipout       */
    ignored_legacy  = 0x08, /* doesn't do anything any more   : outer long         */
    /* but we can actually consider disabling these if we want */
    single_legacy   = 0x10, /* replaced by more advanced      : mark widowpenalty  */
    display_legacy  = 0x20, /* display math features          : a lot              */
    aliased_legacy  = 0x40, /* just handy for various reasons : notexpanded         */
} command_legacy;

typedef struct hash_state_info {
    memoryword   *hash;       /*tex The hash table. */
    memory_data   hash_data;
    memoryword   *eqtb;       /*tex The equivalents table. */
    memory_data   eqtb_data;
    int           no_new_cs;  /*tex Are new identifiers legal? */
    int           misses;
    unsigned char destructors[number_tex_commands]; 
} hash_state_info ;

extern hash_state_info lmt_hash_state;

/*tex

    We use no defines as a |hash| macro will clash with lua hash. Most hash accessors are in a few
    places where it makes sense to be explicit anyway.

    The only reason why we have a dedicated primitive hash is because we can selectively enable 
    them but at some point we might just always do that. There is never a runtiem lookup (asuming 
    a format). It also gives is access to some primitive metadata. 

*/

# define cs_next(a) lmt_hash_state.hash[(a)].half0 /*tex link for coalesced lists */
# define cs_text(a) lmt_hash_state.hash[(a)].half1 /*tex string number for control sequence name */

# define undefined_primitive    0
# define primitives_size     2048 /*tex maximum number of primitives (quite a bit more than needed) */
# define primitives_prime    1741 /*tex about 85 percent of |primitive_size| */

typedef enum primitive_permissions {
    primitive_permitted = 0x00,
    primitive_inhibited = 0x01,
    primitive_permanent = 0x02,
    primitive_warned    = 0x04,
} primitive_permissions;

typedef struct primitive_info {
    halfword       subids;      /*tex number of name entries */
    halfword       offset;      /*tex offset to be used for |chr_code|s */
    strnumber     *names;       /*tex array of names */
    unsigned char *permissions; /*tex array of bitsets */
} primitive_info;

typedef struct primitive_state_info {
    memoryword      prim     [primitives_size + 1];
    memoryword      prim_eqtb[primitives_size + 1];
    primitive_info  prim_data[last_cmd + 1];
    halfword        prim_used;
    /* alignment */
    int        padding;
} primitive_state_info;

extern primitive_state_info lmt_primitive_state;

# define prim_next(a)        lmt_primitive_state.prim[(a)].half0         /*tex Link for coalesced lists. */
# define prim_text(a)        lmt_primitive_state.prim[(a)].half1         /*tex String number for control sequence name. */
# define prim_origin(a)      lmt_primitive_state.prim_eqtb[(a)].single02
# define prim_legacy(a)      lmt_primitive_state.prim_eqtb[(a)].single03

# define prim_eq_type(a)     lmt_primitive_state.prim_eqtb[(a)].quart00  /*tex Command code for equivalent. */
# define prim_equiv(a)       lmt_primitive_state.prim_eqtb[(a)].half1    /*tex Equivalent value. */

# define get_prim_eq_type(p) prim_eq_type(p)
# define get_prim_equiv(p)   prim_equiv(p)
# define get_prim_text(p)    prim_text(p)
# define get_prim_origin(p)  prim_origin(p)
# define get_prim_legacy(p)  prim_legacy(p)

extern void      tex_initialize_primitives (void);
extern void      tex_initialize_hash_mem   (void);
extern halfword  tex_primitive_lookup      (strnumber s);
extern halfword  tex_primitive_lookup_only (strnumber s);
extern void      tex_primitive             (int origin, int legacy, const char *ss, singleword cmd, halfword chr, halfword offset);
extern void      tex_primitive_def         (const char *str, size_t length, singleword cmd, halfword chr);
extern void      tex_print_cmd_chr         (singleword cmd, halfword chr);
extern void      tex_dump_primitives       (dumpstream f);
extern void      tex_undump_primitives     (dumpstream f);
extern void      tex_dump_hashtable        (dumpstream f);
extern void      tex_undump_hashtable      (dumpstream f);
extern halfword  tex_string_locate         (const char *s, size_t l, int create);
extern halfword  tex_string_locate_only    (const char *s, size_t l);
extern halfword  tex_located_string        (const char *s);
extern halfword  tex_id_locate             (int j, int l, int create);
extern halfword  tex_id_locate_only        (int j, int l);
extern int       tex_id_locate_steps       (const char *s);
extern void      tex_print_cmd_flags       (halfword cs, halfword cmd, int flags, int escape);
extern int       tex_primitive_found       (const char *name, halfword *cmd, halfword *chr);
extern int       tex_primitive_index       (halfword cmd, halfword chr);
extern int       tex_inhibit_primitive     (halfword cmd, halfword chr, int permanent);
extern strnumber tex_primitive_name        (halfword cmd, halfword ch);

# endif
