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

# include "luametatex.h"

/*tex 

    We have a a limited set of codes (normal, empty, strut and virtual) but at the \LUA\ end one
    can have more subtypes, like outline or image. The four common ones have the same numbers but 
    the codes are (as usual) used in the primitive commands. 

    In theory we could win some on also supporting shorter keywords but in the current setup there 
    is little gain (e.g. 50K times nothing 0.004, one 0.007, two 0.010 and three 0.013, and in 
    practice scanning the dimension takes much of that). 

    \starttabulate[|Tl|Tl|]
    \NC wd  \NC dimen \NC \NR
    \NC ht  \NC dimen \NC \NR 
    \NC dp  \NC dimen  \NC \NR
    \NC hd  \NC dimen dimen \NC \NR
    \NC whd \NC dimen dimen dimen \NC \NR
    \stoptabulate 

    Virtual rules don't accept |left|, |right|,  |top|, |bottom|, |char|, |font| and |fam| keys
    because they use these fields for other purposes. 

    The default nullflags also serve as a signal, for instance un strut rules combined with char 
    keys. Because a strut is meant for vertical purposes we set the width to zero. 

*/

halfword tex_aux_scan_rule_spec(rule_types type, halfword code)
{
    /*tex |width|, |depth|, and |height| all equal |null_flag| now */
    halfword rule = tex_new_rule_node((quarterword) code); /* here code == subtype */
    halfword attr = node_attr(rule);
    if (code == strut_rule_code) { 
        rule_width(rule) = 0;
        switch (type) {
            case h_rule_type:
                rule_options(rule) |= rule_option_horizontal;
                break;
            case v_rule_type:
                rule_options(rule) |= rule_option_vertical;
                break;
            default: 
                break;
        }
        rule_options(rule) |= rule_option_keep_spacing;
    } else { 
        switch (type) {
            case h_rule_type:
                rule_height(rule) = default_rule;
                rule_depth(rule) = 0;
                rule_options(rule) |= rule_option_horizontal;
                break;
            case v_rule_type:
                rule_options(rule) |= rule_option_vertical;
                /* fall through */
            case m_rule_type:
                rule_width(rule) = default_rule;
                break;
        }
    }
    while (1) {
        switch (tex_scan_character("awhdkpxylrtbcfonsAWHDPKXYLRTBCFONS", 0, 1, 0)) {
            case 0:
                goto DONE;
            case 'a': case 'A':
                if (tex_scan_mandate_keyword("attr", 1)) {
                    attr = tex_scan_attribute(attr);
                }
                break;
            case 'w': case 'W':
                if (tex_scan_mandate_keyword("width", 1)) {
                    rule_width(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                }
                break;
            case 'h': case 'H':
                if (tex_scan_mandate_keyword("height", 1)) {
                    rule_height(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                }
                break;
            case 'n': case 'N':
                if (tex_scan_mandate_keyword("nosnapping", 1)) {
                    rule_options(rule) |= rule_option_no_snapping;
                }
                break;
            case 's': case 'S':
                if (tex_scan_mandate_keyword("snapping", 1)) {
                    rule_options(rule) |= rule_option_snapping;
                }
                break;
            case 'd': case 'D':
             // if (tex_scan_mandate_keyword("depth", 1)) {
             //     rule_depth(rule) = tex_scan_dimension(0, 0, 0, 0, NULL);
             // }
                switch (tex_scan_character("eiEI", 0, 0, 0)) {
                    case 'e': case 'E':
                        if (tex_scan_mandate_keyword("depth", 2)) {
                            rule_depth(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                        }
                        break;
                    case 'i': case 'I':
                        if (tex_scan_mandate_keyword("discardable", 2)) {
                            rule_options(rule) |= rule_option_discardable;
                        }
                        break;
                    default:
                        tex_aux_show_keyword_error("depth|discardable");
                        goto DONE;
                }
                break;
            case 'p': case 'P':
                if (tex_scan_mandate_keyword("pair", 1)) {
                    rule_height(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                    rule_depth(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                }
                break;
            case 'l': case 'L':
                if (code != virtual_rule_code) { 
                    if (tex_scan_mandate_keyword("left", 1)) {
                        rule_left(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                    }
                } else { 
                    goto DONE;
                }
                break;
            case 'r': case 'R':
                if (code != virtual_rule_code) { 
                    switch (tex_scan_character("uieUIE", 0, 0, 0)) {
                        case 'u': case 'U':
                            if (tex_scan_mandate_keyword("running", 2)) {
                                rule_width(rule) = null_flag;
                                rule_height(rule) = null_flag;
                                rule_depth(rule) = null_flag;
                            }
                            break;
                        case 'i': case 'I':
                            if (tex_scan_mandate_keyword("right", 2)) {
                                rule_right(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                            }
                            break;
                        case 'e': case 'E':
                            if (tex_scan_mandate_keyword("resetspacing", 2)) {
                                tex_remove_rule_option(rule, rule_option_keep_spacing);
                            }
                            break;
                        default:
                            tex_aux_show_keyword_error("right|running|resetspacing");
                            goto DONE;
                    }
                } else {
                    switch (tex_scan_character("ueUE", 0, 0, 0)) {
                        case 'u': case 'U':
                            if (tex_scan_mandate_keyword("running", 2)) {
                                tex_set_rule_font(rule, tex_scan_font_identifier(NULL));
                            }
                            break;
                        case 'e': case 'E':
                            if (tex_scan_mandate_keyword("resetspacing", 2)) {
                                tex_remove_rule_option(rule, rule_option_keep_spacing);
                            }
                            break;
                        default:
                            tex_aux_show_keyword_error("running|resetspacing");
                            goto DONE;
                    }
                }
                break;
            case 't': case 'T': /* just because it's nicer */
                if (code != virtual_rule_code) { 
                    if (tex_scan_mandate_keyword("top", 1)) {
                        rule_left(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                    }
                    break;
                } else {
                    goto DONE;
                }
            case 'b': case 'B': /* just because it's nicer */
                if (code != virtual_rule_code) { 
                    if (tex_scan_mandate_keyword("bottom", 1)) {
                        rule_right(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                    }
                    break;
                } else {
                    goto DONE;
                }
            case 'x': case 'X':
                if (tex_scan_mandate_keyword("xoffset", 1)) {
                    rule_x_offset(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                }
                break;
            case 'y': case 'Y':
                if (tex_scan_mandate_keyword("yoffset", 1)) {
                    rule_y_offset(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                }
                break;
            case 'k': case 'K':
                if (tex_scan_mandate_keyword("keepspacing", 1)) {
                    rule_options(rule) |= rule_option_keep_spacing;
                }
                break;
            case 'f': case 'F':
                if (code != virtual_rule_code) { 
                    switch (tex_scan_character("aoAO", 0, 0, 0)) {
                        case 'o': case 'O':
                            if (tex_scan_mandate_keyword("font", 2)) {
                                tex_set_rule_font(rule, tex_scan_font_identifier(NULL));
                            }
                            break;
                        case 'a': case 'A':
                            if (tex_scan_mandate_keyword("fam", 2)) {
                                tex_set_rule_family(rule, tex_scan_math_family_number());
                            }
                            break;
                        default:
                            tex_aux_show_keyword_error("font|fam");
                            goto DONE;
                    }
                    break;
                } else { 
                    goto DONE;
                }
            case 'o': case 'O':
                if (code == normal_rule_code) { 
                    switch (tex_scan_character("nfNF", 0, 0, 0)) {
                        case 'n': case 'N':
                            rule_line_on(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                            break;
                        case 'f': case 'F':
                            if (tex_scan_mandate_keyword("off", 2)) {
                                rule_line_off(rule) = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
                            }
                            break;
                        default:
                            tex_aux_show_keyword_error("on|off");
                            goto DONE;
                    }
                    break;
                } else { 
                    goto DONE;
                }
            case 'c': case 'C':
                if (code == strut_rule_code) { 
                    if (tex_scan_mandate_keyword("char", 1)) {
                        rule_strut_character(rule) = tex_scan_char_number(0);
                    }
                    break;
                } else { 
                    goto DONE;
                }
            default:
                goto DONE;
        }
    }
  DONE:
  //  if (! attr) {
    if (attr) {
        /* Also bumps reference and replaces the one set. */
        tex_attach_attribute_list_attribute(rule, attr);
    }    
    switch (code) {
        case strut_rule_code:
            if (type == v_rule_type) {
                tex_aux_check_text_strut_rule(rule, text_style);
            }
            break;
        case virtual_rule_code:
            rule_virtual_width(rule) = rule_width(rule);
            rule_virtual_height(rule) = rule_height(rule);
            rule_virtual_depth(rule) = rule_depth(rule);
            rule_width(rule) = 0;
            rule_height(rule) = 0;
            rule_depth(rule) = 0;
            break;
    }
    if (type == h_rule_type) { 
        if (rule_width(rule) == null_flag) { 
            rule_options(rule) |= rule_option_running;
        }
    } else { 
        if (rule_height(rule) == null_flag && rule_width(rule) == null_flag) { 
            rule_options(rule) |= rule_option_running;
        }
    }
    return rule;
}

void tex_aux_run_vrule(void)
{
    tex_tail_append(tex_aux_scan_rule_spec(v_rule_type, cur_chr));
    if (! (rule_options(cur_list.tail) & rule_option_keep_spacing)) {
        cur_list.space_factor = default_space_factor;
    }
}

void tex_aux_run_hrule(void)
{
    tex_tail_append(tex_aux_scan_rule_spec(h_rule_type, cur_chr));
    cur_list.prev_depth = ignore_depth_criterion_par;
}

void tex_aux_run_mrule(void)
{
    tex_tail_append(tex_aux_scan_rule_spec(m_rule_type, cur_chr));
}

void tex_aux_check_math_strut_rule(halfword rule, halfword style)
{
    if (node_subtype(rule) == strut_rule_subtype) {
        scaled ht = rule_height(rule);
        scaled dp = rule_depth(rule);
        if (ht == null_flag || dp == null_flag) {
            halfword fnt = tex_get_rule_font(rule, style);
            halfword chr = rule_strut_character(rule);
            if (fnt > 0 && chr && tex_char_exists(fnt, chr)) {
                if (ht == null_flag) {
                    ht = tex_math_font_char_ht(fnt, chr, style);
                }
                if (dp == null_flag) {
                    dp = tex_math_font_char_dp(fnt, chr, style);
                }
            } else {
                if (ht == null_flag) {
                    ht = tex_get_math_y_parameter(style, math_parameter_rule_height);
                }
                if (dp == null_flag) {
                    dp = tex_get_math_y_parameter(style, math_parameter_rule_depth);
                }
            }
                rule_height(rule) = ht;
            rule_depth(rule) = dp;
        }
    }
}

void tex_aux_check_text_strut_rule(halfword rule, halfword style)
{
    if (node_subtype(rule) == strut_rule_subtype) {
        scaled ht = rule_height(rule);
        scaled dp = rule_depth(rule);
        if (ht == null_flag || dp == null_flag) {
            halfword fnt = tex_get_rule_font(rule, style);
            halfword chr = rule_strut_character(rule);
            if (fnt > 0 && chr && tex_char_exists(fnt, chr)) {
                scaledwhd whd = tex_char_whd_from_font(fnt, chr);
                if (ht == null_flag) {
                    rule_height(rule) = whd.ht;
                }
                if (dp == null_flag) {
                    rule_depth(rule) = whd.dp;
                }
            }
        }
    }
}

halfword tex_get_rule_font(halfword n, halfword style)
{
    if (node_subtype(n) != virtual_rule_subtype) {
        halfword fnt = rule_strut_font(n);
        if (fnt >= rule_font_fam_offset) {
            halfword fam = fnt - rule_font_fam_offset;
            if (fam_par_in_range(fam)) {
                fnt = tex_fam_fnt(fam, tex_size_of_style(style));
            }
       }
        if (fnt < 0 || fnt >= max_n_of_fonts) {
            return null_font;
        } else {
            return fnt;
        }
    } else { 
        return null_font;
    }
}

halfword tex_get_rule_family(halfword n)
{
    if (node_subtype(n) != virtual_rule_subtype) {
        halfword fnt = rule_strut_font(n);
        if (fnt >= rule_font_fam_offset) {
            halfword fam = fnt - rule_font_fam_offset;
            if (fam_par_in_range(fam)) {
                return fam;
            }
        }
    }
    return 0;
}

void tex_set_rule_font(halfword n, halfword fnt)
{
    if (node_subtype(n) != virtual_rule_subtype) {
        if (fnt < 0 || fnt >= rule_font_fam_offset) {
            rule_strut_font(n) = 0;
        } else {
            rule_strut_font(n) = fnt;
        }
    }
}

void tex_set_rule_family(halfword n, halfword fam)
{
    if (node_subtype(n) != virtual_rule_subtype) {
        if (fam < 0 || fam >= max_n_of_math_families) {
            rule_strut_font(n) = rule_font_fam_offset;
        } else {
            rule_strut_font(n) = rule_font_fam_offset + fam;
        }
    }
}

halfword tex_get_rule_left(halfword n)
{
    return node_subtype(n) == virtual_rule_subtype ? 0 : rule_left(n); 
}

halfword tex_get_rule_right(halfword n)
{
    return node_subtype(n) == virtual_rule_subtype ? 0 : rule_right(n); 
}

void tex_set_rule_left(halfword n, halfword value)
{
    if (node_subtype(n) != virtual_rule_subtype) {
        rule_left(n) = value; 
    }
}

void tex_set_rule_right(halfword n, halfword value)
{
    if (node_subtype(n) != virtual_rule_subtype) {
        rule_right(n) = value; 
    }
}

halfword tex_get_rule_on(halfword n)
{
    return node_subtype(n) == normal_rule_subtype ? rule_line_on(n) : 0; 
}

halfword tex_get_rule_off(halfword n)
{
    return node_subtype(n) == normal_rule_subtype ? rule_line_off(n) : 0; 
}

void tex_set_rule_on(halfword n, halfword value)
{
    if (node_subtype(n) == normal_rule_subtype) {
        rule_line_on(n) = value; 
    }
}

void tex_set_rule_off(halfword n, halfword value)
{
    if (node_subtype(n) == normal_rule_subtype) {
        rule_line_off(n) = value; 
    }
}