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

#include "xreatemanager.h"
#include "query/containers.h"
#include "main/Parser.h"
#include "pass/compilepass.h"
#include "llvmlayer.h"

#include "supplemental/docutils.h"
#include "supplemental/basics.h"
#include "gtest/gtest.h"

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

struct Tuple2 {intmax_t a; intmax_t b;};
typedef Tuple2 (*FnTuple2)();

struct Tuple4 {intmax_t a; intmax_t b; intmax_t c; intmax_t d;};
typedef Tuple4 (*FnTuple4)();

TEST(Containers, RecInitByList1){
string code = R"(
  Rec = type {x:: int, y:: int}.
  test = function(a:: int, b::int):: Rec; entry() {
    {x = a + b, y = 2}
  }
)";

  auto man = XreateManager::prepare(move(code));
  man->run();
}

TEST(Containers, RecInitByList2){
  string code = R"(
  Rec = type {x:: int, y:: int}.
  test = function(a:: int, b::int):: Rec; entry() {
    {a + b, y =  2}
  }
)";

  auto man = XreateManager::prepare(move(code));
  man->run();
}

TEST(Containers, RecUpdateByList1){
  string code = R"(
  Rec = type {x:: int, y:: int}.
  test = function(a:: int, b::int):: Rec; entry() {
    r = {0, y = 2}:: Rec.

    r : {a + b}
  }
)";

  auto man = XreateManager::prepare(move(code));
  man->run();
}

TEST(Containers, RecUpdateByListIndex1){
  string code = R"(
  Rec = type {x:: int, y:: int}.
  test = function(a:: int, b::int):: int; entry() {
    r1 = undef:: Rec.
    r2 = r1 : {[1] = b, [0] = a}:: Rec.

    r2["x"]
  }
)";

  auto man = XreateManager::prepare(move(code));
  Fn2Args program = (Fn2Args) man->run();
  ASSERT_EQ(10, program(10, 11));
}

TEST(Containers, RecUpdateInLoop1){
  FILE* code = fopen("scripts/containers/RecUpdateInLoop1.xreate", "r");
  assert(code != nullptr);

  auto man = XreateManager::prepare(code);
  Fn1Args program = (Fn1Args) man->run();
  ASSERT_EQ(11, program(10));
}

TEST(Containers, ArrayInit1){
    XreateManager* man = XreateManager::prepare(
R"Code(
main = function(x:: int):: int; entry() {
    a = {1, 2, 3}:: [int].
    a[x]
}
)Code");

    void* mainPtr = man->run();
    Fn1Args main = (Fn1Args) mainPtr;
    ASSERT_EQ(2, main(1));

    delete man;
}

TEST(Containers, ArrayUpdate1){
  XreateManager* man = XreateManager::prepare(R"(
main = function(x::int):: int; entry()
{
  a = {1, 2, 3}:: [int]; csize(5).
  b = a : {[1] = x}:: [int]; csize(5).
  b[1]
}
)");

  void* mainPtr = man->run();
  Fn1Args main = (Fn1Args) mainPtr;
  ASSERT_EQ(2, main(2));

  delete man;
}

TEST(Containers, FlyMap1){
  std::unique_ptr<XreateManager> man(XreateManager::prepare(R"(
main = function:: int; entry()
{
  x = {1, 2, 3, 4}:: [int].
  y = loop map(x->el::int)::[int]; fly(csize(4))
    {2 * el:: int }.
  loop fold((y::[int]; fly(csize(4)))->el:: int, 0->sum):: int {sum + el}-20
}
)"));

  FnNoArgs mainFn = (FnNoArgs) man->run();
  intmax_t valueMain = mainFn();
  ASSERT_EQ(0, valueMain);
}

TEST(Containers, ArrayArg1){
  FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
  assert(code != nullptr);

  std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
  man->options.requireEntryFn = false;
  man->run();
  FnNoArgs fnTested = (FnNoArgs) man->getExteriorFn("fn-ArrayArg1");
  ASSERT_EQ(1, fnTested());
}

TEST(Containers, FlyArg1){
  FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
  assert(code != nullptr);

  std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
  man->options.requireEntryFn = false;
  man->run();
  FnNoArgs fnTested = (FnNoArgs) man->getExteriorFn("fn-FlyArg1");
  ASSERT_EQ(8, fnTested());
}

TEST(Containers, Range1){
  FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
  assert(code != nullptr);

  std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
  man->options.requireEntryFn = false;
  man->run();
  {
    FnNoArgs fnRange1 = (FnNoArgs) man->getExteriorFn("fn-Range1");
    ASSERT_EQ(10, fnRange1());
  }

  {
    FnNoArgs fnRange2 = (FnNoArgs) man->getExteriorFn("fn-Range2");
    ASSERT_EQ(20, fnRange2());
  }
}

TEST(Containers, RetRange1){
  FILE* code = fopen("scripts/containers/containers-tests.xreate", "r");
  assert(code != nullptr);

  std::unique_ptr<XreateManager> man(XreateManager::prepare(code));
  man->options.requireEntryFn = false;
  man->run();
  {
    FnNoArgs fnRange1 = (FnNoArgs) man->getExteriorFn("fn-RetRange1");
    ASSERT_EQ(10, fnRange1());
  }
}

//TEST(Containers, ListAsArray2){
//    XreateManager* man = XreateManager::prepare(
//
//R"Code(
//                        // CONTAINERS
//    import raw("scripts/dfa/ast-attachments.lp").
//    import raw("scripts/containers/containers.lp").
//
//    main = function:: int;entry {
//            a= {1, 2, 3}:: [int].
//            b= loop map(a->el:: int):: [int]{
//                2 * el
//            }.
//
//            sum = loop fold(b->el:: int, 0->acc):: int {
//                acc + el
//            }.
//
//            sum
//    }
//)Code");
//
//    void* mainPtr = man->run();
//    FnNoArgs main = (FnNoArgs) mainPtr;
//    ASSERT_EQ(12, main());
//
//    delete man;
//}
//
//TEST(Containers, Doc_RecField1){
//  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecField1");
//  XreateManager::prepare(move(code_Variants1));
//
//  ASSERT_TRUE(true);
//}
//
//TEST(Containers, Doc_RecUpdate1){
//  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecUpdate1");
//  XreateManager::prepare(move(code_Variants1));
//
//  ASSERT_TRUE(true);
//}
//
//TEST(Containers, ContanierLinkedList1){
//	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 Symbol symb_chilrenRaw{body->findSymbolByAlias("childrenRaw"), body};
//
//	containers::ImplementationLinkedList iLL(symb_chilrenRaw);
//
//	ASSERT_EQ(true, static_cast<bool>(iLL));
//	ASSERT_EQ("next", iLL.fieldPointer);
//
//	Implementation impl = Implementation::create(symb_chilrenRaw);
//	ASSERT_NO_FATAL_FAILURE(impl.extract<ON_THE_FLY>());
//
//	ImplementationRec<ON_THE_FLY> recOnthefly = impl.extract<ON_THE_FLY>();
//	ASSERT_EQ(symb_chilrenRaw, recOnthefly.source);
//}
//
//TEST(Containers, Implementation_LinkedListFull){
//	FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r");
//	assert(input != nullptr);
//
//    std::unique_ptr<XreateManager> program(XreateManager::prepare(input));
//	void* mainPtr = program->run();
//	int (*main)() = (int (*)())(intptr_t)mainPtr;
//
//  intmax_t answer = main();
//	ASSERT_EQ(17, answer);
//
//	fclose(input);
//}
//
//TEST(Containers, Doc_Intr_1){
//  string example = R"Code(
//    import raw("scripts/containers/containers.lp").
//
//    test = function:: int; entry
//    {
//      <BODY>
//      x
//    }
//  )Code";
//  string body = getDocumentationExampleById("documentation/Concepts/containers.xml", "Intr_1");
//  replace(example, "<BODY>", body);
//
//  XreateManager* xreate = XreateManager::prepare(move(example));
//  FnNoArgs program = (FnNoArgs) xreate->run();
//
//  intmax_t result = program();
//  ASSERT_EQ(1, result);
//}
//
//TEST(Containers, Doc_OpAccessSeq_1){
//  string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessSeq_1");
//  XreateManager* xreate = XreateManager::prepare(move(example));
//  FnNoArgs program = (FnNoArgs) xreate->run();
//
//  intmax_t result = program();
//  ASSERT_EQ(15, result);
//}
//
//TEST(Containers, Doc_OpAccessRand_1){
//  string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessRand_1");
//  XreateManager* xreate = XreateManager::prepare(move(example));
//  FnNoArgs program = (FnNoArgs) xreate->run();
//
//  intmax_t result = program();
//  ASSERT_EQ(2, result);
//}
//
//TEST(Containers, Doc_ASTAttach_1){
//  string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "ASTAttach_1");
//  string outputExpected = "containers_impl(s(1,-2,0),onthefly)";
//  XreateManager* xreate = XreateManager::prepare(move(example));
//
//  testing::internal::CaptureStdout();
//  xreate->run();
//  std::string outputActual = testing::internal::GetCapturedStdout();
//
//  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
//}
//
//TEST(Containers, IntrinsicArrInit1){
//  XreateManager* man = XreateManager::prepare(
//
//R"Code(
//FnAnns = type predicate {
//  entry
//}
//
//main = function(x:: int):: int; entry() {
//  a{0} = intrinsic array_init(16):: [int].
//  a{1} = a{0} + {15: 12}
//}
//)Code");
//}
