# Build the DebugCmdConstants.h and DebugCmdInfoConstants.cc files from the
# DebugCmdInfoConstants.in file.
#
# We do this via a script rather than maintaining them directly because
# the struct is a little complicated, so has to be initialized from code,
# plus we want to make adding new constants somewhat less painful.
#
# The input filename should be supplied as an argument.
#
# DebugCmds are printed to DebugCmdConstants.h
# DebugCmdInfos are printed to DebugCmdInfoConstants.cc
#
# The input format is:
#
#     cmd: [DebugCmd]
#     names: [space delimited names of cmd]
#     resume: ['true' or 'false': should execution resume after this command?]
#     help: [some help text]
#
# Blank lines are skipped.
# Comments should start with // and should be on a line by themselves.

import sys

inputfile = sys.argv[1]

init_tmpl = """
\t{
\t\tDebugCmdInfo* info;
\t\t%(name_init)s
\t\tinfo = new DebugCmdInfo(%(cmd)s, names, %(num_names)s, %(resume)s, "%(help)s",
\t\t                                      %(repeatable)s);
\t\tg_DebugCmdInfos.push_back(info);
\t}
"""

enum_str = f"""
//
// This file was automatically generated from {inputfile}
// DO NOT EDIT.
//
enum DebugCmd : uint8_t {{
"""

init_str = f"""
//
// This file was automatically generated from {inputfile}
// DO NOT EDIT.
//

#include "zeek/util.h"
namespace zeek::detail {{\n
void init_global_dbg_constants () {{
"""


def outputrecord():
    global init_str, enum_str

    if dbginfo["names"]:
        dbginfo["name_init"] = (
            "const char * const names[] = {{\n\t\t\t{}\n\t\t}};\n"
        ).format(",\n\t\t\t".join(dbginfo["names"]))
    else:
        dbginfo["name_init"] = "const char * const names[] = { };\n"

    dbginfo["num_names"] = len(dbginfo["names"])

    # substitute into template
    init_str += init_tmpl % dbginfo

    enum_str += "\t{},\n".format(dbginfo["cmd"])


def initdbginfo():
    return {
        "cmd": "",
        "name_init": "",
        "num_names": 0,
        "names": [],
        "resume": "false",
        "help": "",
        "repeatable": "false",
    }


dbginfo = initdbginfo()

inputf = open(inputfile)
for line in inputf:
    line = line.strip()
    if not line or line.startswith("//"):  # skip empty lines and comments
        continue

    fields = line.split(":", 1)
    if len(fields) != 2:
        raise RuntimeError(f"Error in debug constant file on line: {line}")

    f1, f2 = fields
    f2 = f2.strip()

    if f1 == "cmd":
        if dbginfo[f1]:  # output the previous record
            outputrecord()
            dbginfo = initdbginfo()

        dbginfo[f1] = f2
    elif f1 == "names":
        # put quotes around the strings
        dbginfo[f1] = [f'"{n}"' for n in f2.split()]
    elif f1 == "help":
        dbginfo[f1] = f2.replace('"', '\\"')  # escape quotation marks
    elif f1 in ("resume", "repeatable"):
        dbginfo[f1] = f2
    else:
        raise RuntimeError(f"Unknown command: {line}")

# output the last record
outputrecord()

init_str += "\t\n}\n\n} // namespace zeek::detail\n"
enum_str += "\tdcLast\n};\n"

debugcmds = open("DebugCmdConstants.h", "w")
debugcmds.write(enum_str)
debugcmds.close()

debugcmdinfos = open("DebugCmdInfoConstants.cc", "w")
debugcmdinfos.write(init_str)
debugcmdinfos.close()
