#include <luna/CString.h>
#include <luna/Utf8.h>
#include <test.h>

TestResult test_basic_utf8_ascii_decoding()
{
    wchar_t buf[20];

    Utf8StringDecoder decoder("hello, world!");
    validate(!decoder.decode(buf, sizeof(buf)).has_error());

    validate(!wcscmp(buf, L"hello, world!"));

    test_success;
}

TestResult test_basic_utf8_ascii_encoding()
{
    char buf[20];

    Utf8StringEncoder encoder(L"hello, world!");
    validate(!encoder.encode(buf, sizeof(buf)).has_error());

    validate(!strcmp(buf, "hello, world!"));

    test_success;
}

TestResult test_utf8_decoding_with_non_ascii_chars()
{
    wchar_t buf[20];

    Utf8StringDecoder decoder("¿ñ?");
    validate(!decoder.decode(buf, sizeof(buf)).has_error());

    validate(!wcscmp(buf, L"¿ñ?"));

    test_success;
}

TestResult test_utf8_encoding_with_non_ascii_chars()
{
    char buf[20];

    Utf8StringEncoder encoder(L"¿ñ?");
    validate(!encoder.encode(buf, sizeof(buf)).has_error());

    validate(!strcmp(buf, "¿ñ?"));

    test_success;
}

TestResult test_utf8_decoding_invalid_continuation_byte_at_start()
{
    Utf8StringDecoder decoder("\x83");
    validate(decoder.code_points().has_error());

    test_success;
}

TestResult test_utf8_decoding_invalid_start_byte_at_continuation()
{
    Utf8StringDecoder decoder("\xc2\x7f");
    validate(decoder.code_points().has_error());

    test_success;
}

TestResult test_utf8_decoding_unfinished_sequence()
{
    Utf8StringDecoder decoder("\xe0\x83");
    validate(decoder.code_points().has_error());

    test_success;
}

TestResult test_utf8_decoding_invalid_bytes()
{
    Utf8StringDecoder decoder("\xf5\xf7");
    validate(decoder.code_points().has_error());

    test_success;
}

TestResult test_utf8_decoding_overlong_encoding()
{
    Utf8StringDecoder decoder("\xe0\x80\x80");
    validate(decoder.code_points().has_error());

    test_success;
}

TestResult test_utf8_decoder_calculate_code_points()
{
    Utf8StringDecoder decoder("¿ñ? ✨");
    auto rc = decoder.code_points();
    validate(!rc.has_error());
    validate(rc.value() == 5);

    test_success;
}

TestResult test_utf8_encoder_calculate_byte_length()
{
    Utf8StringEncoder encoder(L"¿ñ? ✨");
    auto rc = encoder.byte_length();
    validate(!rc.has_error());
    validate(rc.value() == 9);

    test_success;
}

TestResult test_utf8_encoder_code_points_outside_unicode()
{
    const wchar_t buf[] = { 0x120000, 0x000000 };

    Utf8StringEncoder encoder(buf);
    validate(encoder.byte_length().has_error());

    test_success;
}

Result<void> test_main()
{
    test_prelude;

    run_test(test_basic_utf8_ascii_decoding);
    run_test(test_basic_utf8_ascii_encoding);
    run_test(test_utf8_decoding_with_non_ascii_chars);
    run_test(test_utf8_encoding_with_non_ascii_chars);
    run_test(test_utf8_decoding_invalid_continuation_byte_at_start);
    run_test(test_utf8_decoding_invalid_start_byte_at_continuation);
    run_test(test_utf8_decoding_unfinished_sequence);
    run_test(test_utf8_decoding_invalid_bytes);
    run_test(test_utf8_decoding_overlong_encoding);
    run_test(test_utf8_decoder_calculate_code_points);
    run_test(test_utf8_encoder_calculate_byte_length);
    run_test(test_utf8_encoder_code_points_outside_unicode);

    return {};
}