#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() }";

	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_NE(24, answer);
}

TEST(InterfaceExternC, testStructFields1){
	FILE* input = fopen("scripts/testspass/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();

	Symbol symbTree = body->findSymbol("tree");

    const TypeAnnotation& tTree = CodeScope::findDeclaration(symbTree).type;
    const ExpandedType& t2Tree = ast.expandType(tTree);
    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());
}
