/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 * 
 * types.cpp
 *
 *  Created on: Jun 4, 2015
 *      Author: pgess  <v.melnychenko@xreate.org>
 */

#include "gtest/gtest.h"
#include "xreatemanager.h"
#include "llvmlayer.h"
#include "main/Parser.h"

using namespace std;
using namespace xreate;
using namespace xreate::grammar::main;

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

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

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

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

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

	ASSERT_EQ(TypeOperator::STRUCT, 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 {\n"
			"        tag:: string,\n"
			"        attrs:: [string],\n"
			"        content:: string\n"
			"}.\n"
			""
			"Tree = type(Leaf) {Leaf, [Tree(Leaf)]}."
			"Concrete = type Tree(XmlNode).";

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

	ASSERT_EQ(TypeOperator::STRUCT, 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 {\n"
			"        tag:: string,\n"
			"       /* attrs:: [string],*/\n"
			"        content:: string\n"
			"}.\n"
			""
			"Tree = type(Leaf) {Leaf, [Tree(Leaf)]}."
			"Concrete = type Tree(XmlNode).";

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

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

TEST(Types, ArrayOfExternal1){
    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->finalize();

    CodeScope* body = ast->findFunction("test")->getEntryScope();

    const ExpandedType& t2 = ast->getType(body->getDeclaration(body->getSymbol("childrenRaw")));
    EXPECT_EQ(t2->__operator, TypeOperator::ARRAY);
}

TEST(Types, ExternType1){
	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->finalize();
	CodeScope* body = ast->findFunction("test")->getEntryScope();

    const ExpandedType& t2 = ast->getType(body->getDeclaration(body->getSymbol("tree")));
    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<XreateManager> program(XreateManager::prepare(move(code)));

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

	Expression eRed = program->root->findFunction("test")->getEntryScope()->getBody();
	EXPECT_EQ(Operator::VARIANT, eRed.op);

	const ExpandedType& typ2 = program->root->getType(eRed);
	EXPECT_EQ(TypeOperator::VARIANT, typ2->__operator);
}

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

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

	XreateManager* man = XreateManager::prepare(move(code));
	int (*main)() = (int (*)()) man->run();

	EXPECT_EQ(0, main());
}

TEST(Types, ast_VariantType2){
    std::string script=
R"Code(
    Expression = type
        variant {
            Num:: int,
            String:: string,
            Func:: {name::string, arguments::[Expression]}
        }.
)Code";

    std::unique_ptr<XreateManager> program(XreateManager::prepare(move(script)));
    ExpandedType typ = program->root->findType("Expression");
    ASSERT_EQ(3, typ.get().fields.size());
}

//TEST(Types, A)


//TOTEST string type
