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

# ifndef LMT_CONDITIONAL_H
# define LMT_CONDITIONAL_H

/*tex

    The next list should be in sync with |if_branch_mapping| at the top of the |c| file with the
    same name. The next ones also go on the condition stack so we need to retain this order and
    organization.

    There is a catch here: the codes of the |if_test_cmd|, |fi_or_else_cmd| and |or_else_cmd| are
    all in this enumeration. This has to do with the history of not always checking for the cmd
    code in the fast skipping branches. We could change that but not now.

    Well, in the end I combined |if_test_cmd|, |fi_or_else_cmd| and |or_else_cmd| because they use
    the same chr range anyway and it also simplifies some of the testing (especially after some
    more robust cmd/chr checking was added, and after that the |fi_or_else_cmd| and |or_else_cmd|
    were combined. The main motivation is that we can have a more consistent \LUA\ token interface
    end. It is debatable as we divert from the original, but we already did that by introducing
    more conditionals, |\orelse| and the generic |\ifconditional| that also demandeed all kind of
    adaptations. Sorry. The comments are mostly the same, including references to the older cmd
    codes (pre 2.07 there used to be some switch/case statements in places but these were flattened).

    Btw, the |\unless| prefix is kept out of this because it relates to expansion and prefixes are
    separate anyway. It would make the code less pretty.

    One reason for a split in cmd codes is performance but we didn't loose on the change.

*/

typedef enum if_test_codes {
    /*tex These are private chr codes: */

    no_if_code,             /*tex We're not in a condition. */
    if_code,                /*tex We have a condition. */

    /*tex These are public chr codes: */

    fi_code,                /*tex |\fi| */
    else_code,              /*tex |\else| */
    or_code,                /*tex |\or| */
    or_else_code,           /*tex |\orelse| */
    or_unless_code,         /*tex |\orunless| */

    /*tex 
        Here come the \if... codes. Some are just there to minimize tracing and are not faster, 
        like |\ifzerodim| (we can use |\ifcase| instead but not with |\unless|). 

        For historic reasons we see a mixture if |int| and |num| here but I see no reason the 
        change that now.  
    */

    if_char_code,            /*tex |\if| */
    if_cat_code,             /*tex |\ifcat| */
    if_int_code,             /*tex |\ifnum| */
    if_abs_int_code,         /*tex |\ifabsnum| */ 
    if_zero_int_code,        /*tex |\ifzeronum|*/
    if_interval_int_code,
    if_posit_code,    
    if_abs_posit_code,
    if_zero_posit_code,
    if_interval_posit_code,
    if_dim_code,             /*tex |\ifdim| */
    if_abs_dim_code,         /*tex |\ifabsdim| */
    if_zero_dim_code,        /*tex |\ifzerodim| */
    if_interval_dim_code,
    if_odd_code,             /*tex |\ifodd| */
    if_vmode_code,           /*tex |\ifvmode| */
    if_hmode_code,           /*tex |\ifhmode| */
    if_mmode_code,           /*tex |\ifmmode| */
    if_inner_code,           /*tex |\ifinner| */
    if_void_code,            /*tex |\ifvoid| */
    if_hbox_code,            /*tex |\ifhbox| */
    if_vbox_code,            /*tex |\ifvbox| */
    if_tok_code,             /*tex |\iftok| */
    if_cstok_code,           /*tex |\ifcstok| */
    if_x_code,               /*tex |\ifx| */
    if_true_code,            /*tex |\iftrue| */
    if_false_code,           /*tex |\iffalse| */
    if_defined_code,         /*tex |\ifdefined| */
    if_csname_code,          /*tex |\ifcsname| */
    if_in_csname_code,       /*tex |\ifincsname| */
    if_font_char_code,       /*tex |\iffontchar| */
    if_condition_code,       /*tex |\ifcondition| */
    if_flags_code,           /*tex |\ifflags| */
    if_empty_code,           /*tex |\ifempty| */
    if_relax_code,           /*tex |\ifrelax| */
    if_boolean_code,         /*tex |\ifboolean| */
    if_numexpression_code,   /*tex |\ifnumexpression| */
    if_dimexpression_code,   /*tex |\ifdimexpression| */
    if_last_named_cs_code,   /*tex |\iflastnamedcs| */
    if_has_tok_code,         /*tex |\ifhastok| */
    if_has_toks_code,        /*tex |\ifhastoks| */
    if_has_xtoks_code,       /*tex |\ifhasxtoks| */
    if_has_char_code,        /*tex |\ifhaschar| */
    if_insert_code,          /*tex |\ifinsert| */
    if_in_alignment_code,    /*tex |\ifinalignment| */
    if_cramped_code,         /*tex |\ifcrampedmathstyle| */
    if_list_code,            /*tex |\iflist| */
    if_specification_code,   /*tex |\ifspecification| */
    if_bitwise_and_code,     /*tex |\ifbitwiseand| */
    /* case results */
    if_case_code,            /*tex |\ifcase| */
    if_chk_int_code,         /*tex |\ifchknum| */
    if_chk_integer_code,     /*tex |\ifchknumber| */
    if_chk_intexpr_code,     /*tex |\ifnumexpr| */
    if_val_int_code,         /*tex |\ifnumval| */
    if_cmp_int_code,         /*tex |\ifcmpnum| */
    if_chk_dim_code,
    if_chk_dimension_code,   /*tex |\ifchkdimension| */
    if_chk_dimexpr_code,     /*tex |\ifdimexpr| */
 /* if_chk_dimexpression_code, */
 /* if_chk_dimensionexpr_code, */
    if_val_dim_code,         /*tex |\ifdimval| */
    if_cmp_dim_code,         /*tex |\ifcmpdim| */
    if_math_parameter_code,  /*tex |\ifmathparameter| */
    if_math_style_code,      /*tex |\ifmathstyle| */
    if_arguments_code,       /*tex |\ifarguments| */
    if_parameters_code,      /*tex |\ifparameters| */
    if_parameter_code,       /*tex |\ifparameter| */
    /* till here */
} if_test_codes;

# define first_if_case_code      if_case_code
# define last_if_case_code       if_parameter_code

# define first_if_test_code      fi_code
# define last_if_test_code       if_parameter_code

# define first_real_if_test_code if_char_code
# define last_real_if_test_code  if_parameter_code

# define condition_node_stack 0

typedef struct condition_state {
    quarterword if_limit;   /* 2 bytes */ /*tex upper bound on |fi_or_else| codes */
    quarterword cur_if;     /* 2 bytes */ /*tex type of conditional being worked on */
    singleword  cur_unless; /* 1 byte  */
    singleword  if_unless;  /* 1 byte  */
    singleword  if_step;    /* 1 byte  */
    singleword  unused;     /* 1 byte  */
    halfword    if_line;    /* 4 bytes */ /*tex line where that conditional began */
} condition_state;

# if condition_node_stack == 1

typedef struct condition_state_info {
    halfword        cond_ptr; /*tex top of the condition stack */
    condition_state state;
    halfword        level;
    halfword        skip_line;
    halfword        chk_integer;
    scaled          chk_dimension;
} condition_state_info ;

# else

typedef struct condition_state_info {
    union {
        halfword     level;    /*tex traditional tex speak */
        halfword     cond_ptr; /*tex top of the condition stack */
    };
    condition_state  state;
    halfword         skip_line;
    halfword         chk_integer;
    scaled           chk_dimension;
    /* todo: statistics, see scanner */
    condition_state *stack;
    int              stack_size;
    int              stack_step;
    int              stack_max;
    memory_data      stack_data;
} condition_state_info ;

# endif

extern condition_state_info lmt_condition_state;

extern void tex_conditional_if          (halfword code, int unless);
extern void tex_conditional_fi_or_else  (void);
extern void tex_conditional_unless      (void);
extern void tex_show_ifs                (void);
extern void tex_conditional_catch_up    (void);
/*     void tex_quit_fi                 (void); */
/*     void tex_conditional_after_fi    (void); */
extern void tex_initialize_conditionals (void);
extern int  tex_checkup_conditionals    (int badrun);
extern void tex_cleanup_conditionals    (void);

# endif
