#include "engine/framework/text/unicode_normalization.h"

#include "test_assert.h"

#include <cstdint>
#include <iostream>
#include <string>
#include <vector>

namespace {

struct Case {
    uint32_t input;
    std::vector<uint32_t> expected;
};

const std::vector<Case> & issue_cases() {
    static const std::vector<Case> cases = {
        {0x00C0, {0x0041, 0x0300}}, {0x00C1, {0x0041, 0x0301}}, {0x00C2, {0x0041, 0x0302}},
        {0x00C3, {0x0041, 0x0303}}, {0x00C4, {0x0041, 0x0308}}, {0x00C5, {0x0041, 0x030A}},
        {0x00C7, {0x0043, 0x0327}}, {0x00C8, {0x0045, 0x0300}}, {0x00C9, {0x0045, 0x0301}},
        {0x00CA, {0x0045, 0x0302}}, {0x00CB, {0x0045, 0x0308}}, {0x00CC, {0x0049, 0x0300}},
        {0x00CD, {0x0049, 0x0301}}, {0x00CE, {0x0049, 0x0302}}, {0x00CF, {0x0049, 0x0308}},
        {0x00D1, {0x004E, 0x0303}}, {0x00D2, {0x004F, 0x0300}}, {0x00D3, {0x004F, 0x0301}},
        {0x00D4, {0x004F, 0x0302}}, {0x00D5, {0x004F, 0x0303}}, {0x00D6, {0x004F, 0x0308}},
        {0x00D9, {0x0055, 0x0300}}, {0x00DA, {0x0055, 0x0301}}, {0x00DB, {0x0055, 0x0302}},
        {0x00DC, {0x0055, 0x0308}}, {0x00DD, {0x0059, 0x0301}}, {0x00E0, {0x0061, 0x0300}},
        {0x00E1, {0x0061, 0x0301}}, {0x00E2, {0x0061, 0x0302}}, {0x00E3, {0x0061, 0x0303}},
        {0x00E4, {0x0061, 0x0308}}, {0x00E5, {0x0061, 0x030A}}, {0x00E7, {0x0063, 0x0327}},
        {0x00E8, {0x0065, 0x0300}}, {0x00E9, {0x0065, 0x0301}}, {0x00EA, {0x0065, 0x0302}},
        {0x00EB, {0x0065, 0x0308}}, {0x00EC, {0x0069, 0x0300}}, {0x00ED, {0x0069, 0x0301}},
        {0x00EE, {0x0069, 0x0302}}, {0x00EF, {0x0069, 0x0308}}, {0x00F1, {0x006E, 0x0303}},
        {0x00F2, {0x006F, 0x0300}}, {0x00F3, {0x006F, 0x0301}}, {0x00F4, {0x006F, 0x0302}},
        {0x00F5, {0x006F, 0x0303}}, {0x00F6, {0x006F, 0x0308}}, {0x00F9, {0x0075, 0x0300}},
        {0x00FA, {0x0075, 0x0301}}, {0x00FB, {0x0075, 0x0302}}, {0x00FC, {0x0075, 0x0308}},
        {0x00FD, {0x0079, 0x0301}}, {0x00FF, {0x0079, 0x0308}}, {0x0100, {0x0041, 0x0304}},
        {0x0101, {0x0061, 0x0304}}, {0x0102, {0x0041, 0x0306}}, {0x0103, {0x0061, 0x0306}},
        {0x0104, {0x0041, 0x0328}}, {0x0105, {0x0061, 0x0328}}, {0x0106, {0x0043, 0x0301}},
        {0x0107, {0x0063, 0x0301}}, {0x010C, {0x0043, 0x030C}}, {0x010D, {0x0063, 0x030C}},
        {0x010E, {0x0044, 0x030C}}, {0x010F, {0x0064, 0x030C}}, {0x0112, {0x0045, 0x0304}},
        {0x0113, {0x0065, 0x0304}}, {0x0116, {0x0045, 0x0307}}, {0x0117, {0x0065, 0x0307}},
        {0x0118, {0x0045, 0x0328}}, {0x0119, {0x0065, 0x0328}}, {0x011A, {0x0045, 0x030C}},
        {0x011B, {0x0065, 0x030C}}, {0x011E, {0x0047, 0x0306}}, {0x011F, {0x0067, 0x0306}},
        {0x0122, {0x0047, 0x0327}}, {0x0123, {0x0067, 0x0327}}, {0x012A, {0x0049, 0x0304}},
        {0x012B, {0x0069, 0x0304}}, {0x012E, {0x0049, 0x0328}}, {0x012F, {0x0069, 0x0328}},
        {0x0130, {0x0049, 0x0307}}, {0x0136, {0x004B, 0x0327}}, {0x0137, {0x006B, 0x0327}},
        {0x0139, {0x004C, 0x0301}}, {0x013A, {0x006C, 0x0301}}, {0x013B, {0x004C, 0x0327}},
        {0x013C, {0x006C, 0x0327}}, {0x013D, {0x004C, 0x030C}}, {0x013E, {0x006C, 0x030C}},
        {0x0143, {0x004E, 0x0301}}, {0x0144, {0x006E, 0x0301}}, {0x0145, {0x004E, 0x0327}},
        {0x0146, {0x006E, 0x0327}}, {0x0147, {0x004E, 0x030C}}, {0x0148, {0x006E, 0x030C}},
        {0x0150, {0x004F, 0x030B}}, {0x0151, {0x006F, 0x030B}}, {0x0154, {0x0052, 0x0301}},
        {0x0155, {0x0072, 0x0301}}, {0x0158, {0x0052, 0x030C}}, {0x0159, {0x0072, 0x030C}},
        {0x015A, {0x0053, 0x0301}}, {0x015B, {0x0073, 0x0301}}, {0x015E, {0x0053, 0x0327}},
        {0x015F, {0x0073, 0x0327}}, {0x0160, {0x0053, 0x030C}}, {0x0161, {0x0073, 0x030C}},
        {0x0164, {0x0054, 0x030C}}, {0x0165, {0x0074, 0x030C}}, {0x016A, {0x0055, 0x0304}},
        {0x016B, {0x0075, 0x0304}}, {0x016E, {0x0055, 0x030A}}, {0x016F, {0x0075, 0x030A}},
        {0x0170, {0x0055, 0x030B}}, {0x0171, {0x0075, 0x030B}}, {0x0172, {0x0055, 0x0328}},
        {0x0173, {0x0075, 0x0328}}, {0x0178, {0x0059, 0x0308}}, {0x0179, {0x005A, 0x0301}},
        {0x017A, {0x007A, 0x0301}}, {0x017B, {0x005A, 0x0307}}, {0x017C, {0x007A, 0x0307}},
        {0x017D, {0x005A, 0x030C}}, {0x017E, {0x007A, 0x030C}}, {0x0218, {0x0053, 0x0326}},
        {0x0219, {0x0073, 0x0326}}, {0x021A, {0x0054, 0x0326}}, {0x021B, {0x0074, 0x0326}},
        {0x0386, {0x0391, 0x0301}}, {0x0388, {0x0395, 0x0301}}, {0x0389, {0x0397, 0x0301}},
        {0x038A, {0x0399, 0x0301}}, {0x038C, {0x039F, 0x0301}}, {0x038E, {0x03A5, 0x0301}},
        {0x038F, {0x03A9, 0x0301}}, {0x0390, {0x03B9, 0x0308, 0x0301}},
        {0x03AA, {0x0399, 0x0308}}, {0x03AB, {0x03A5, 0x0308}}, {0x03AC, {0x03B1, 0x0301}},
        {0x03AD, {0x03B5, 0x0301}}, {0x03AE, {0x03B7, 0x0301}}, {0x03AF, {0x03B9, 0x0301}},
        {0x03B0, {0x03C5, 0x0308, 0x0301}}, {0x03CA, {0x03B9, 0x0308}}, {0x03CB, {0x03C5, 0x0308}},
        {0x03CC, {0x03BF, 0x0301}}, {0x03CD, {0x03C5, 0x0301}}, {0x03CE, {0x03C9, 0x0301}},
        {0x0401, {0x0415, 0x0308}}, {0x0407, {0x0406, 0x0308}}, {0x040E, {0x0423, 0x0306}},
        {0x0419, {0x0418, 0x0306}}, {0x0439, {0x0438, 0x0306}}, {0x0451, {0x0435, 0x0308}},
        {0x0457, {0x0456, 0x0308}}, {0x045E, {0x0443, 0x0306}},
    };
    return cases;
}

void require_eq_u32_vector(
    const std::vector<uint32_t> & actual,
    const std::vector<uint32_t> & expected,
    const std::string & label) {
    engine::test::require_eq(actual.size(), expected.size(), label + " size");
    for (size_t i = 0; i < expected.size(); ++i) {
        engine::test::require_eq(actual[i], expected[i], label + " value " + std::to_string(i));
    }
}

void test_issue_character_decompositions() {
    for (const auto & test_case : issue_cases()) {
        std::vector<uint32_t> out;
        const bool decomposed = engine::text::append_known_unicode_decomposition(test_case.input, out);
        engine::test::require(decomposed, "expected decomposition");
        require_eq_u32_vector(out, test_case.expected, "direct decomposition");
        require_eq_u32_vector(
            engine::text::decompose_known_unicode_codepoints({test_case.input}),
            test_case.expected,
            "vector decomposition");
    }
}

void test_mixed_sequence_and_passthrough() {
    const std::vector<uint32_t> input{0x0078, 0x0439, 0x0020, 0x03B0, 0x4E2D, 0x00E9};
    const std::vector<uint32_t> expected{
        0x0078,
        0x0438, 0x0306,
        0x0020,
        0x03C5, 0x0308, 0x0301,
        0x4E2D,
        0x0065, 0x0301,
    };
    require_eq_u32_vector(
        engine::text::decompose_known_unicode_codepoints(input),
        expected,
        "mixed sequence");

    std::vector<uint32_t> out;
    engine::test::require(!engine::text::append_known_unicode_decomposition(0x2603, out), "snowman passthrough");
    engine::test::require(out.empty(), "snowman append output");
    require_eq_u32_vector(
        engine::text::decompose_known_unicode_codepoints({0x2603}),
        {0x2603},
        "snowman vector passthrough");
}

}  // namespace

int main() {
    try {
        test_issue_character_decompositions();
        test_mixed_sequence_and_passthrough();
        std::cout << "unicode_normalization_test passed\n";
        return 0;
    } catch (const std::exception & ex) {
        std::cerr << "unicode_normalization_test failed: " << ex.what() << "\n";
        return 1;
    }
}
