#include "../../extern/doctest.hpp"
#include "../../src/Parser/Lexer/Lexer.hpp"

using namespace MalachScript::Parser;

#define KEYWORD_TEST(script, symbol)                                                                                   \
    TEST_CASE("Lex " script) {                                                                                         \
        MalachScript::Diagnostics::Logger diag;                                                                        \
        auto lexer = Lexer(u8##script, u8##script, &diag);                                                             \
        const auto* token = lexer.Lex();                                                                               \
        CHECK(diag.GetMessages().empty());                                                                             \
        CHECK(token->GetKind() == LexTokenKind::symbol);                                                               \
        CHECK(token->GetNext()->GetKind() == LexTokenKind::EndOfFile);                                                 \
    }

KEYWORD_TEST("and", AndKeyword);
KEYWORD_TEST("abstract", AbstractKeyword);
KEYWORD_TEST("auto", AutoKeyword);
KEYWORD_TEST("bool", BoolKeyword);
KEYWORD_TEST("break", BreakKeyword);
KEYWORD_TEST("case", CaseKeyword);
KEYWORD_TEST("cast", CastKeyword);
KEYWORD_TEST("catch", CatchKeyword);
KEYWORD_TEST("class", ClassKeyword);
KEYWORD_TEST("const", ConstKeyword);
KEYWORD_TEST("continue", ContinueKeyword);
KEYWORD_TEST("default", DefaultKeyword);
KEYWORD_TEST("do", DoKeyword);
KEYWORD_TEST("double", DoubleKeyword);
KEYWORD_TEST("else", ElseKeyword);
KEYWORD_TEST("enum", EnumKeyword);
KEYWORD_TEST("explicit", ExplicitKeyword);
KEYWORD_TEST("external", ExternalKeyword);
KEYWORD_TEST("false", FalseKeyword);
KEYWORD_TEST("final", FinalKeyword);
KEYWORD_TEST("float", FloatKeyword);
KEYWORD_TEST("for", ForKeyword);
KEYWORD_TEST("from", FromKeyword);
KEYWORD_TEST("funcdef", FuncdefKeyword);
KEYWORD_TEST("function", FunctionKeyword);
KEYWORD_TEST("get", GetKeyword);
KEYWORD_TEST("if", IfKeyword);
KEYWORD_TEST("import", ImportKeyword);
KEYWORD_TEST("in", InKeyword);
KEYWORD_TEST("inout", InoutKeyword);
KEYWORD_TEST("int", IntKeyword);
KEYWORD_TEST("interface", InterfaceKeyword);
KEYWORD_TEST("int8", Int8Keyword);
KEYWORD_TEST("int16", Int16Keyword);
KEYWORD_TEST("int32", Int32Keyword);
KEYWORD_TEST("int64", Int64Keyword);
KEYWORD_TEST("is", IsKeyword);
KEYWORD_TEST("mixin", MixinKeyword);
KEYWORD_TEST("namespace", NamespaceKeyword);
KEYWORD_TEST("not", NotKeyword);
KEYWORD_TEST("null", NullKeyword);
KEYWORD_TEST("or", OrKeyword);
KEYWORD_TEST("out", OutKeyword);
KEYWORD_TEST("override", OverrideKeyword);
KEYWORD_TEST("private", PrivateKeyword);
KEYWORD_TEST("property", PropertyKeyword);
KEYWORD_TEST("protected", ProtectedKeyword);
KEYWORD_TEST("return", ReturnKeyword);
KEYWORD_TEST("set", SetKeyword);
KEYWORD_TEST("shared", SharedKeyword);
KEYWORD_TEST("super", SuperKeyword);
KEYWORD_TEST("switch", SwitchKeyword);
KEYWORD_TEST("this", ThisKeyword);
KEYWORD_TEST("true", TrueKeyword);
KEYWORD_TEST("try", TryKeyword);
KEYWORD_TEST("typedef", TypedefKeyword);
KEYWORD_TEST("uint", UintKeyword);
KEYWORD_TEST("uint8", Uint8Keyword);
KEYWORD_TEST("uint16", Uint16Keyword);
KEYWORD_TEST("uint32", Uint32Keyword);
KEYWORD_TEST("uint64", Uint64Keyword);
KEYWORD_TEST("void", VoidKeyword);
KEYWORD_TEST("while", WhileKeyword);
KEYWORD_TEST("xor", XorKeyword);

namespace doctest {
    template <> struct StringMaker<std::u8string> {
        static String convert(const std::u8string& value) {
            return String(reinterpret_cast<const char*>(value.data()));
        }
    };
    template <> struct StringMaker<LexTokenKind> {
        static String convert(LexTokenKind value) { return String(std::to_string((uint32_t)value).c_str()); }
    };

}

#define IDENTIFIER_TEST(identifier)                                                                                    \
    TEST_CASE("Lex identifier " identifier) {                                                                          \
        MalachScript::Diagnostics::Logger diag;                                                                        \
        auto lexer = Lexer(u8##identifier, u8##identifier, &diag);                                                     \
        const auto* token = lexer.Lex();                                                                               \
        CHECK(diag.GetMessages().empty());                                                                             \
        REQUIRE(token->GetKind() == LexTokenKind::Identifier);                                                         \
        auto value = ((IdentifierToken*)token)->GetValue().GetString();                                                \
        CHECK(value == std::u8string(reinterpret_cast<const char8_t*>(identifier)));                                   \
        CHECK(token->GetNext()->GetKind() == LexTokenKind::EndOfFile);                                                 \
    }

IDENTIFIER_TEST("foobar");
IDENTIFIER_TEST("_foobar");
IDENTIFIER_TEST("_foo8bar");

#undef KEYWORD_TEST
#undef IDENTIFIER_TEST