/*
 * types.cpp
 *
 *  Created on: Jun 4, 2015
 *      Author: pgess
 */

#include "gtest/gtest.h"
#include "passmanager.h"
#include "llvmlayer.h"
#include "Parser.h"

using namespace std;
using namespace xreate;

TEST(Types, DependantTypes1) {
	string&& code = "XmlNode = type alias {\n"
			"        tag:: string,\n"
			"       /* attrs:: [string],*/\n"
			"        content:: string\n"
			"}.\n";

	std::unique_ptr<PassManager> program(PassManager::prepareForCode(move(code)));
	ExpandedType typeXmlNode = program->root->findType("XmlNode");

	ASSERT_EQ(TypeOperator::STRUCT, typeXmlNode->__operator);
	ASSERT_EQ(2, typeXmlNode->__operands.size());
	ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(0).__value);
	ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(1).__value);
}

TEST(Types, DependantTypes2) {
	string&& code = "XmlNode = type alias {\n"
			"        tag:: string,\n"
			"       /* attrs:: [string],*/\n"
			"        content:: string\n"
			"}.\n"
			""
			"Template = type Template(Leaf) [Leaf, [Leaf[content]]]."
			"Concrete = type alias Template(XmlNode).";

	std::unique_ptr<PassManager> program(PassManager::prepareForCode(move(code)));
	ExpandedType typeConcrete = program->root->findType("Concrete");


	ASSERT_EQ(TypeOperator::TUPLE, typeConcrete->__operator);
	ASSERT_EQ(2, typeConcrete->__operands.size());
	ASSERT_EQ(TypeOperator::STRUCT, typeConcrete->__operands.at(0).__operator);

	ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator);
	ASSERT_EQ(TypePrimitive::String, typeConcrete->__operands.at(1).__operands.at(0).__value);
}

TEST(Types, TreeType1) {
	string&& code = "XmlNode = type alias {\n"
			"        tag:: string,\n"
			"       /* attrs:: [string],*/\n"
			"        content:: string\n"
			"}.\n"
			""
			"Tree = type Tree(Leaf) [Leaf, [Tree(Leaf)]]."
			"Concrete = type alias Tree(XmlNode).";

	std::unique_ptr<PassManager> program(PassManager::prepareForCode(move(code)));
	ExpandedType typeConcrete = program->root->findType("Concrete");

	ASSERT_EQ(TypeOperator::TUPLE, typeConcrete->__operator);
	ASSERT_EQ(2, typeConcrete->__operands.size());
	ASSERT_EQ(TypeOperator::STRUCT, typeConcrete->__operands.at(0).__operator);

	ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator);

	auto typeLink = typeConcrete->__operands.at(1).__operands.at(0);
	ASSERT_EQ(TypeOperator::LINK, typeLink.__operator);
	ASSERT_EQ(typeConcrete->conjuctionId,typeLink.conjuctionId);
}

TEST(Types, TreeType1LLvm){
	string&& code = "XmlNode = type alias {\n"
			"        tag:: string,\n"
			"       /* attrs:: [string],*/\n"
			"        content:: string\n"
			"}.\n"
			""
			"Tree = type Tree(Leaf) [Leaf, [Tree(Leaf)]]."
			"Concrete = type alias Tree(XmlNode).";

	std::unique_ptr<PassManager> program(PassManager::prepareForCode(move(code)));
	ExpandedType typeConcrete = program->root->findType("Concrete");

	llvm::Type* raw = program->llvm->toLLVMType(typeConcrete);
}

TEST(Types, ArrayOfExternal1){
	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 symb = body->findSymbol("childrenRaw");

    const TypeAnnotation& t = CodeScope::findDefinition(symb);
    const ExpandedType& t2 = ast.expandType(t);
    EXPECT_EQ(t2->__operator, TypeOperator::ARRAY);
}

TEST(Types, ExternType1){
	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& t = CodeScope::findDefinition(symbTree);
    const ExpandedType& t2 = ast.expandType(t);
    EXPECT_EQ(t2->__operator, TypeOperator::CUSTOM);
}

TEST(Types, ast_VariantType1){
	string&& code =
			"	colors = type variant (RED, BLUE, GREEN).\n"
			"	test = function:: colors; entry {GREEN}";

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

	ExpandedType typ = program->root->findType("colors");
	EXPECT_EQ(TypeOperator::VARIANT, typ->__operator);

	Expression eRed = program->root->findFunction("test")->getEntryScope()->__body;
	EXPECT_EQ(Expression::VARIANT, eRed.__state);

	const ExpandedType& typ2 = program->root->expandType(eRed.type);
	EXPECT_EQ(TypeOperator::VARIANT, typ2->__operator);

	program->run();
}

TEST(Types, full_VariantType_Switch1){
	string&& code =
			"	colors = type variant (RED, BLUE, GREEN).\n"
			"	test = function:: colors; entry {GREEN}"

			"main = function:: int; entry {\n"
			"	switch(test():: color)\n"
			"	case GREEN {0}\n"
			"	case default {1}\n"
			"}";

	PassManager* man = PassManager::prepareForCode(move(code));
	int (*main)() = (int (*)()) man->run();

	EXPECT_EQ(0, main());
}


//TEST string type
