/* 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 "gtest/gtest.h"

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

typedef int (*IntFn)();

TEST(Containers, ListAsArray){
    XreateManager* man = XreateManager::prepare(

R"Code(
    main = function(x:: int):: int;entry {
            a = {1, 2, 3}:: [int].
            a[x]
    }
)Code"          );

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

    delete man;
}

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();
    int (*main)() = (int (*)())mainPtr;
    ASSERT_EQ(12, main());

    delete man;
}

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;

	int 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));
  IntFn program = (IntFn) xreate->run();
  
  int 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));
  IntFn program = (IntFn) xreate->run();
  
  int 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));
  IntFn program = (IntFn) xreate->run();
  
  int 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));
}
