#! /usr/bin/env python3
# This script generates crates/sre_engine/src/constants.rs from Lib/re/_constants.py.

SCRIPT_NAME = "scripts/generate_sre_constants.py"


def update_file(file, content):
    try:
        with open(file, "r") as fobj:
            if fobj.read() == content:
                return False
    except (OSError, ValueError):
        pass
    with open(file, "w") as fobj:
        fobj.write(content)
    return True


sre_constants_header = f"""\
/*
 * Secret Labs' Regular Expression Engine
 *
 * regular expression matching engine
 *
 * Auto-generated by {SCRIPT_NAME} from
 * Lib/re/_constants.py.
 *
 * Copyright (c) 1997-2001 by Secret Labs AB.  All rights reserved.
 *
 * See the sre.c file for information on usage and redistribution.
 */

"""


def dump_enum(d, enum_name, derives, strip_prefix=""):
    """Generate Rust enum definitions from a Python dictionary.

    Args:
        d (list): The list containing the enum variants.
        enum_name (str): The name of the enum to generate.
        derives (str): The derive attributes to include.
        strip_prefix (str, optional): A prefix to strip from the variant names. Defaults to "".

    Returns:
        list: A list of strings representing the enum definition.
    """
    items = sorted(d)
    print(f"items is {items}")
    content = [f"{derives}\n"]
    content.append("#[repr(u32)]\n")
    content.append("#[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n")
    content.append(f"pub enum {enum_name} {{\n")
    for i, item in enumerate(items):
        name = str(item).removeprefix(strip_prefix)
        content.append(f"    {name} = {i},\n")
    content.append("}\n\n")
    return content


def dump_bitflags(d, prefix, derives, struct_name, int_t):
    """Generate Rust bitflags definitions from a Python dictionary.

    Args:
        d (dict): The dictionary containing the bitflag variants.
        prefix (str): The prefix to strip from the variant names.
        derives (str): The derive attributes to include.
        struct_name (str): The name of the struct to generate.
        int_t (str): The integer type to use for the bitflags.

    Returns:
        list: A list of strings representing the bitflags definition.
    """
    items = [(value, name) for name, value in d.items() if name.startswith(prefix)]
    content = ["bitflags! {\n"]
    content.append(f"{derives}\n") if derives else None
    content.append(f"    pub struct {struct_name}: {int_t} {{\n")
    for value, name in sorted(items):
        name = str(name).removeprefix(prefix)
        content.append(f"        const {name} = {value};\n")
    content.append("    }\n")
    content.append("}\n\n")
    return content


def main(
    infile="Lib/re/_constants.py",
    outfile_constants="crates/sre_engine/src/constants.rs",
):
    ns = {}
    with open(infile) as fp:
        code = fp.read()
    exec(code, ns)

    content = [sre_constants_header]
    content.append("use bitflags::bitflags;\n\n")
    content.append(f"pub const SRE_MAGIC: usize = {ns['MAGIC']};\n")
    content.extend(
        dump_enum(
            ns["OPCODES"],
            "SreOpcode",
            "#[derive(num_enum::TryFromPrimitive, Debug, PartialEq, Eq)]",
        )
    )
    content.extend(
        dump_enum(
            ns["ATCODES"],
            "SreAtCode",
            "#[derive(num_enum::TryFromPrimitive, Debug, PartialEq, Eq)]",
            "AT_",
        )
    )
    content.extend(
        dump_enum(
            ns["CHCODES"],
            "SreCatCode",
            "#[derive(num_enum::TryFromPrimitive, Debug)]",
            "CATEGORY_",
        )
    )

    content.extend(
        dump_bitflags(
            ns,
            "SRE_FLAG_",
            "#[derive(Debug, PartialEq, Eq, Clone, Copy)]",
            "SreFlag",
            "u16",
        )
    )
    content.extend(dump_bitflags(ns, "SRE_INFO_", "", "SreInfo", "u32"))

    update_file(outfile_constants, "".join(content))


if __name__ == "__main__":
    import sys

    main(*sys.argv[1:])
