# Lifts the flag enums out of include/enums.h into a lookup table for the Flag
# Tracer. Generated into the build tree so it can't drift from enums.h.

function(_flagnames_parse_enum ENUM_NAME OUT_VAR)
    set(_entries "")
    set(_in_enum FALSE)
    set(_next 0)

    foreach(_line IN LISTS FLAGNAMES_LINES)
        if(NOT _in_enum)
            if(_line MATCHES "^[ \t]*enum[ \t]+${ENUM_NAME}[ \t]*(\\{)?[ \t]*$")
                set(_in_enum TRUE)
            endif()
            continue()
        endif()

        if(_line MATCHES "^[ \t]*\\}")
            break()
        endif()

        string(REGEX REPLACE "//.*$" "" _clean "${_line}")
        string(REGEX REPLACE "/\\*.*\\*/" "" _clean "${_clean}")

        if(_clean MATCHES "^[ \t]*([A-Za-z_][A-Za-z0-9_]*)[ \t]*(=[ \t]*(0[xX][0-9a-fA-F]+|[0-9]+))?[ \t]*,?[ \t]*$")
            set(_ident "${CMAKE_MATCH_1}")
            set(_explicit "${CMAKE_MATCH_3}")
            if(NOT _explicit STREQUAL "")
                math(EXPR _next "${_explicit}")
            endif()
            list(APPEND _entries "    { ${_next}, \"${_ident}\" },")
            math(EXPR _next "${_next} + 1")
        endif()
    endforeach()

    if(_entries STREQUAL "")
        message(FATAL_ERROR "GenerateFlagNames: no entries parsed for 'enum ${ENUM_NAME}'! Has enums.h changed shape?")
    endif()

    string(REPLACE ";" "\n" _joined "${_entries}")
    set(${OUT_VAR} "${_joined}" PARENT_SCOPE)
endfunction()

function(_flagnames_emit_table ENUM_NAME TABLE_NAME OUT_VAR)
    _flagnames_parse_enum("${ENUM_NAME}" _rows)
    set(${OUT_VAR}
        "// ${ENUM_NAME}\nstatic const FlagName ${TABLE_NAME}[] = {\n${_rows}\n};\n"
        PARENT_SCOPE)
endfunction()

set(FLAGNAMES_HEADER "${CMAKE_BINARY_DIR}/generated/FlagNames.generated.h")
set(FLAGNAMES_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/include/enums.h")

file(STRINGS "${FLAGNAMES_SOURCE}" FLAGNAMES_LINES)

set(FLAGNAMES_BODY
"// GENERATED at configure time by cmake/GenerateFlagNames.cmake from include/enums.h.
#pragma once

struct FlagName {
    int index;
    const char* name;
};
")

foreach(_pair "file_progress_e:kFileProgressNames" "volatile_flags_e:kVolatileNames" "level_flags_e:kLevelNames")
    string(REPLACE ":" ";" _pair "${_pair}")
    list(GET _pair 0 _enum)
    list(GET _pair 1 _table)
    _flagnames_emit_table("${_enum}" "${_table}" _table_text)
    string(APPEND FLAGNAMES_BODY "\n${_table_text}")
endforeach()

set(FLAGNAMES_MAP_GROUPS
    "sm_specific_flags:kSmNames:0xB"
    "mm_specific_flags:kMmNames:0x1"
    "ttc_specific_flags:kTtcNames:0x2"
    "bgs_specific_flags:kBgsNames:0x4"
    "fp_specific_flags:kFpNames:0x5"
    "mmm_specific_flags:kMmmNames:0xA"
    "rbb_main_specific_flags:kRbbMainNames:0x9"
    "rbb_boat_room_specific_flags:kRbbBoatNames:0x9"
    "ccw_zubba_specific_flags:kCcwZubbaNames:0x8"
    "ccw_winter_specific_flags:kCcwWinterNames:0x8"
    "lair_specific_flags:kLairNames:0x6"
)

set(FLAGNAMES_GROUP_ROWS "")
foreach(_entry IN LISTS FLAGNAMES_MAP_GROUPS)
    string(REPLACE ":" ";" _parts "${_entry}")
    list(GET _parts 0 _enum)
    list(GET _parts 1 _table)
    list(GET _parts 2 _level)
    _flagnames_emit_table("${_enum}" "${_table}" _table_text)
    string(APPEND FLAGNAMES_BODY "\n${_table_text}")
    list(APPEND FLAGNAMES_GROUP_ROWS
         "    { ${_level}, ${_table}, (int)(sizeof(${_table}) / sizeof(${_table}[0])) },")
endforeach()

string(REPLACE ";" "\n" FLAGNAMES_GROUP_ROWS "${FLAGNAMES_GROUP_ROWS}")
string(APPEND FLAGNAMES_BODY
"
// Map-specific lookup needs the loaded level to disambiguate.
struct MapFlagGroup {
    int level;
    const FlagName* names;
    int count;
};

static const MapFlagGroup kMapFlagGroups[] = {
${FLAGNAMES_GROUP_ROWS}
};
")

set(_existing "")
if(EXISTS "${FLAGNAMES_HEADER}")
    file(READ "${FLAGNAMES_HEADER}" _existing)
endif()
if(NOT _existing STREQUAL "${FLAGNAMES_BODY}")
    file(WRITE "${FLAGNAMES_HEADER}" "${FLAGNAMES_BODY}")
    message(STATUS "Generated ${FLAGNAMES_HEADER}")
endif()

set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${FLAGNAMES_SOURCE}")