/*
 * Copyright © 2024 Pierre Le Marre <dev@wismill.eu>
 * SPDX-License-Identifier: MIT
 */
#pragma once

/* Use macro sorcery */
#include "src/messages-codes.h"

enum update_files {
    NO_UPDATE = 0,
    UPDATE_USING_TEST_INPUT,
    UPDATE_USING_TEST_OUTPUT
};

#define GOLDEN_TESTS_OUTPUTS "keymaps/merge-modes/"

#define keymap_common                                                         \
    "xkb_keymap {\n"                                                          \
    "  xkb_keycodes { include \"merge_modes\" };\n"                           \
    "  xkb_types {\n"                                                         \
    "    include \"basic\"\n"                                                 \
    /* Add only used levels; `types/extra` has some types uneeded here */     \
    "    virtual_modifiers LevelThree;\n"                                     \
    "    type \"FOUR_LEVEL\" {\n"                                             \
	"        modifiers = Shift+LevelThree;\n"                                 \
	"        map[None] = Level1;\n"                                           \
	"        map[Shift] = Level2;\n"                                          \
	"        map[LevelThree] = Level3;\n"                                     \
	"        map[Shift+LevelThree] = Level4;\n"                               \
	"        level_name[Level1] = \"Base\";\n"                                \
	"        level_name[Level2] = \"Shift\";\n"                               \
	"        level_name[Level3] = \"Alt Base\";\n"                            \
	"        level_name[Level4] = \"Shift Alt\";\n"                           \
    "    };\n"                                                                \
    "    type \"FOUR_LEVEL_ALPHABETIC\" {\n"                                  \
	"        modifiers = Shift+Lock+LevelThree;\n"                            \
	"        map[None] = Level1;\n"                                           \
	"        map[Shift] = Level2;\n"                                          \
	"        map[Lock]  = Level2;\n"                                          \
	"        map[LevelThree] = Level3;\n"                                     \
	"        map[Shift+LevelThree] = Level4;\n"                               \
	"        map[Lock+LevelThree] =  Level4;\n"                               \
	"        map[Lock+Shift+LevelThree] =  Level3;\n"                         \
	"        level_name[Level1] = \"Base\";\n"                                \
	"        level_name[Level2] = \"Shift\";\n"                               \
	"        level_name[Level3] = \"Alt Base\";\n"                            \
	"        level_name[Level4] = \"Shift Alt\";\n"                           \
    "    };\n"                                                                \
    "    type \"FOUR_LEVEL_SEMIALPHABETIC\" {\n"                              \
	"        modifiers = Shift+Lock+LevelThree;\n"                            \
	"        map[None] = Level1;\n"                                           \
	"        map[Shift] = Level2;\n"                                          \
	"        map[Lock]  = Level2;\n"                                          \
	"        map[LevelThree] = Level3;\n"                                     \
	"        map[Shift+LevelThree] = Level4;\n"                               \
	"        map[Lock+LevelThree] =  Level3;\n"                               \
	"        map[Lock+Shift+LevelThree] = Level4;\n"                          \
	"        preserve[Lock+LevelThree] = Lock;\n"                             \
	"        preserve[Lock+Shift+LevelThree] = Lock;\n"                       \
	"        level_name[Level1] = \"Base\";\n"                                \
	"        level_name[Level2] = \"Shift\";\n"                               \
	"        level_name[Level3] = \"Alt Base\";\n"                            \
	"        level_name[Level4] = \"Shift Alt\";\n"                           \
    "    };\n"                                                                \
    "  };\n"                                                                  \
    "  xkb_compat { };\n"

/* Macro sorcery: see "src/messages-codes.h" */
#define MATCHdefault             unused,default
#define CHECK_MERGE_MODE(value)  SECOND(JOIN(MATCH, value), other, unused)

#define FORMAT_MERGE_MODE_global_default(mode) "include"
#define FORMAT_MERGE_MODE_global_other(mode)   #mode
#define FORMAT_MERGE_MODE_global(mode) \
        JOIN(FORMAT_MERGE_MODE_global_, CHECK_MERGE_MODE(mode))(mode)

/* Helper to create a keymap string to initialize output files */
#define make_ref_keymap(file, map, suffix)           \
    keymap_common                                    \
    "  xkb_symbols {\n"                              \
    "    include \"" file "(" map suffix ")\"\n"     \
    "  };\n"                                         \
    "};"

/* Helper to create a keymap string to test (global) */
#define make_test_keymap_global(file, map, merge_mode)                        \
    keymap_common                                                             \
    "  xkb_symbols {\n"                                                       \
    /*                                                                        \
     * NOTE: Separate statements so that *all* the merge modes *really* work. \
     *       Using + and | separators downgrades `replace key` to `override/  \
     *       augment key`.                                                    \
     */                                                                       \
    "    include \"" file "(" map "base)\"\n"                                 \
    "    " FORMAT_MERGE_MODE_global(merge_mode) " \"" file "(" map "new)\"\n" \
    "  };\n"                                                                  \
    "};"

/* Helper to create a keymap string to test (local) */
#define make_test_keymap_local(file, map, merge_mode)                         \
    keymap_common                                                             \
    "  xkb_symbols {\n"                                                       \
    /*                                                                        \
     * NOTE: Separate statements so that *all* the merge modes *really* work. \
     *       Using + and | separators downgrades `replace key` to `override/  \
     *       augment key`.                                                    \
     */                                                                       \
    "    include \"" file "(" map "new-" #merge_mode ")\"\n"                  \
    "  };\n"                                                                  \
    "};"

/* Helper to create a test for a single keymap string */
#define make_symbols_test(localness, merge_mode, file, map, map_suffix,                \
                          file_suffix, compile, priv, update) do {                     \
    const char keymap_ref_str[] = make_ref_keymap(file, map, map_suffix);              \
    const char keymap_test_str[] = make_test_keymap_##localness(file, map, merge_mode);\
    assert(test_compile_output(                                                        \
        ctx, compile, priv,                                                            \
        "test_merge_mode: " map ", " #localness " " #merge_mode,                       \
        ((update) == UPDATE_USING_TEST_INPUT) ? keymap_ref_str : keymap_test_str,      \
        ((update) == UPDATE_USING_TEST_INPUT) ? ARRAY_SIZE(keymap_ref_str)             \
                                              : ARRAY_SIZE(keymap_test_str),           \
        /* Local and global merge modes use the same result file */                    \
        GOLDEN_TESTS_OUTPUTS #merge_mode map file_suffix ".xkb",                       \
        !!(update)));                                                                  \
} while (0)

/* Helper to create a test for each merge mode */
#define make_symbols_tests(file, map, suffix, compile, priv, update)                          \
    /* Mode: Default */                                                                       \
    make_symbols_test(local, default, file, map, "override", suffix, compile, priv, update);  \
    make_symbols_test(global, default, file, map, "override", suffix, compile, priv, update); \
    /* Mode: Augment */                                                                       \
    make_symbols_test(local, augment, file, map, "augment", suffix, compile, priv, update);   \
    make_symbols_test(global, augment, file, map, "augment", suffix, compile, priv, update);  \
    /* Mode: Override */                                                                      \
    make_symbols_test(local, override, file, map, "override", suffix, compile, priv, update); \
    make_symbols_test(global, override, file, map, "override", suffix, compile, priv, update);\
    /* Mode: Replace */                                                                       \
    make_symbols_test(local, replace, file, map, "new", suffix, compile, priv, update);       \
    make_symbols_test(global, replace, file, map, "new", suffix, compile, priv, update)
