#include "gtest/gtest.h"
#include "passmanager.h"
#include "Scanner.h"
#include "Parser.h"
#include <iostream>
#include <llvm/Support/DynamicLibrary.h>

using namespace std;

TEST(InterfaceExternC, testAST) {
    std::string code = "											\
			interface(extern-c){									\
			  xml2 = library:: pkgconfig(\"libxml-2.0\").				\
			  	  	  	  	  	  	  	  	  	  	  	  	  	  	\
			  include {												\
			    xml2 = [\"libxml/tree.h\"]							\
			  }.													\
			}   													\
			";

    Scanner scanner(reinterpret_cast<const unsigned char*> (code.c_str()), code.size());
    Parser parser(&scanner);
    parser.Parse();

    ASSERT_EQ(1, parser.root.__externdata.size());

    for (const ExternEntry& lib : parser.root.__externdata) {
        ASSERT_EQ("libxml-2.0", lib.package);
        ASSERT_EQ(1, lib.headers.size());
        ASSERT_EQ("libxml/tree.h", lib.headers.at(0));
    }
}

TEST(InterfaceExternC, testfetchPackageHeaders) {
    ExternEntry entry{"libxml-2.0",
        {}};

    vector<string> args = ExternLayer::fetchPackageFlags(entry);
    ASSERT_EQ(1, args.size());
    ASSERT_EQ("-I/usr/include/libxml2", args.at(0));
}

TEST(InterfaceExternC, testfetchPackageLibs) {
    ExternEntry entry{"libxml-2.0",
        {}};

    vector<string> args = ExternLayer::fetchPackageLibs(entry);
    ASSERT_EQ(1, args.size());
    ASSERT_EQ("xml2", args.at(0));

}

TEST(InterfaceExternC, testLoadLib) {
    std::string msgErr;
    if (!llvm::sys::DynamicLibrary::LoadLibraryPermanently("-lpcre -lxml2", &msgErr)) {
        cout << msgErr;
        ASSERT_EQ("", msgErr);
    }

    ASSERT_TRUE(true);
}

TEST(InterfaceExternC, testBSD1) {
    std::string code = "                            \n\
        interface(extern-c){                        \n\
          libbsd = library:: pkgconfig(\"libbsd\"). \n\
                                                    \n\
          include {                                 \n\
            libbsd = [\"bsd/stdlib.h\"]             \n\
          }.                                        \n\
        }                                           \n"

        "main= function:: int; entry{arc4random_uniform(24) }";

    std::unique_ptr<PassManager> program(PassManager::prepareForCode(move(code)));

    void* entryPtr = program->run();
    int (*entry)() = (int (*)())(intptr_t) entryPtr;

    int answer = 24;
    answer = entry();
    cout << answer;
    ASSERT_LT(answer, 24);
}

TEST(InterfaceExternC, testStructFields1) {
    FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r");
    assert(input != nullptr);

    Scanner scanner(input);
    Parser parser(&scanner);
    parser.Parse();

    AST& ast = parser.root;
    CodeScope* body = ast.findFunction("test")->getEntryScope();

    const ExpandedType& t2Tree = ast.getType(body->getDeclaration(body->getSymbol("tree")));
    LLVMLayer llvm(&ast);
    TypeUtils utils(&llvm);

    std::vector<std::string>fields = utils.getStructFields(t2Tree);

    auto field = std::find(fields.begin(), fields.end(), "children");
    ASSERT_TRUE(field != fields.end());
}
