#!/usr/bin/env python3
#
# Parses linux/input.h scanning for #define KEY_FOO 134
# Prints C header files or Python files that can be used as
# mapping and lookup tables.
#

import re
import sys


class Bits(object):
    def __init__(self):
        self.max_codes = {}


prefixes = [
    "EV_",
    "REL_",
    "ABS_",
    "KEY_",
    "BTN_",
    "LED_",
    "SND_",
    "MSC_",
    "SW_",
    "FF_",
    "SYN_",
    "REP_",
    "INPUT_PROP_",
    "MT_TOOL_",
]

duplicates = [
    "EV_VERSION",
    "BTN_MISC",
    "BTN_MOUSE",
    "BTN_JOYSTICK",
    "BTN_GAMEPAD",
    "BTN_DIGI",
    "BTN_WHEEL",
    "BTN_TRIGGER_HAPPY",
    "SW_MAX",
    "REP_MAX",
]

btn_additional = [
    [0, "BTN_A"],
    [0, "BTN_B"],
    [0, "BTN_X"],
    [0, "BTN_Y"],
]

code_prefixes = [
    "REL_",
    "ABS_",
    "KEY_",
    "BTN_",
    "LED_",
    "SND_",
    "MSC_",
    "SW_",
    "FF_",
    "SYN_",
    "REP_",
]


def print_bits(bits, prefix):
    if not hasattr(bits, prefix):
        return
    print("static const char * const %s_map[%s_MAX + 1] = {" % (prefix, prefix.upper()))
    for val, name in sorted(list(getattr(bits, prefix).items())):
        print("    [%s] = \"%s\"," % (name, name))
    if prefix == "key":
        for val, name in sorted(list(getattr(bits, "btn").items())):
            print("    [%s] = \"%s\"," % (name, name))
    print("};")
    print("")


def print_map(bits):
    print("static const char * const * const event_type_map[EV_MAX + 1] = {")

    for prefix in prefixes:
        if prefix in ["BTN_", "EV_", "INPUT_PROP_", "MT_TOOL_"]:
            continue
        print("    [EV_%s] = %s_map," % (prefix[:-1], prefix[:-1].lower()))

    print("};")
    print("")

    print("#if __clang__")
    print("#pragma clang diagnostic push")
    print("#pragma clang diagnostic ignored \"-Winitializer-overrides\"")
    print("#elif __GNUC__")
    print("#pragma GCC diagnostic push")
    print("#pragma GCC diagnostic ignored \"-Woverride-init\"")
    print("#endif")
    print("static const int ev_max[EV_MAX + 1] = {")
    for val in range(bits.max_codes["EV_MAX"] + 1):
        if val in bits.ev:
            prefix = bits.ev[val][3:]
            if prefix + "_" in prefixes:
                print("    %s_MAX," % prefix)
                continue
        print("    -1,")
    print("};")
    print("#if __clang__")
    print("#pragma clang diagnostic pop /* \"-Winitializer-overrides\" */")
    print("#elif __GNUC__")
    print("#pragma GCC diagnostic pop /* \"-Woverride-init\" */")
    print("#endif")
    print("")


def print_lookup(bits, prefix):
    if not hasattr(bits, prefix):
        return

    names = sorted(list(getattr(bits, prefix).items()))
    if prefix == "btn":
        names = names + btn_additional

    # We need to manually add the _MAX codes because some are
    # duplicates
    maxname = "%s_MAX" % (prefix.upper())
    if maxname in duplicates:
        names.append((bits.max_codes[maxname], maxname))

    for val, name in sorted(names, key=lambda e: e[1]):
        print("    { .name = \"%s\", .value = %s }," % (name, name))


def print_lookup_table(bits):
    print("struct name_entry {")
    print("    const char *name;")
    print("    unsigned int value;")
    print("};")
    print("")
    print("static const struct name_entry tool_type_names[] = {")
    print_lookup(bits, "mt_tool")
    print("};")
    print("")
    print("static const struct name_entry ev_names[] = {")
    print_lookup(bits, "ev")
    print("};")
    print("")

    print("static const struct name_entry code_names[] = {")
    for prefix in sorted(code_prefixes, key=lambda e: e):
        print_lookup(bits, prefix[:-1].lower())
    print("};")
    print("")
    print("static const struct name_entry prop_names[] = {")
    print_lookup(bits, "input_prop")
    print("};")
    print("")


def print_mapping_table(bits):
    print("/* THIS FILE IS GENERATED, DO NOT EDIT */")
    print("")
    print("#ifndef EVENT_NAMES_H")
    print("#define EVENT_NAMES_H")
    print("")

    for prefix in prefixes:
        if prefix == "BTN_":
            continue
        print_bits(bits, prefix[:-1].lower())

    print_map(bits)
    print_lookup_table(bits)

    print("#endif /* EVENT_NAMES_H */")


def parse_define(bits, line):
    m = re.match(r"^#define\s+(\w+)\s+(\w+)", line)
    if m is None:
        return

    name = m.group(1)

    try:
        value = int(m.group(2), 0)
    except ValueError:
        return

    for prefix in prefixes:
        if not name.startswith(prefix):
            continue

        if name.endswith("_MAX"):
            bits.max_codes[name] = value

        if name in duplicates:
            return

        attrname = prefix[:-1].lower()

        if not hasattr(bits, attrname):
            setattr(bits, attrname, {})
        b = getattr(bits, attrname)
        b[value] = name


def parse(lines):
    bits = Bits()
    for line in lines:
        if not line.startswith("#define"):
            continue
        parse_define(bits, line)

    return bits


def usage(prog):
    print("Usage: %s <files>".format(prog))


if __name__ == "__main__":
    if len(sys.argv) <= 1:
        usage(sys.argv[0])
        sys.exit(2)

    from itertools import chain
    lines = chain(*[open(f).readlines() for f in sys.argv[1:]])
    bits = parse(lines)
    print_mapping_table(bits)
