/* 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 "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 Tuple3 {intmax_t a; intmax_t b; intmax_t c; };
typedef Tuple3 (*FnTuple3)();

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);
}

intmax_t fn_BUG_Triple(FnTuple3 callee){
  Tuple3 result = callee();
  return result.a+ result.b + result.c;
}
TEST(Containers, BUG_Triple){
  std::unique_ptr<XreateManager> man(XreateManager::prepare(R"(
Tuple2 = type {int, int}.
Tuple3 = type {int, int, int}.
Tuple4 = type {int, int, int, int}.

main = function:: Tuple3; entry()
{
  {1, 2, 3}
}
)"));
  FnTuple3 mainFn = (FnTuple3) man->run();
  intmax_t result = fn_BUG_Triple(mainFn);

  ASSERT_EQ(6, result);
//  ASSERT_EQ(2, result.b);
//  ASSERT_EQ(3, result.c);
}

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

  std::unique_ptr<XreateManager> man (XreateManager::prepare(code));
  FnNoArgs mainFn = (FnNoArgs) man->run();
  ASSERT_EQ(0, mainFn());
}


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