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


#include "supplemental/docutils.h"
#include "xreatemanager.h"
#include "main/Parser.h"
#include "supplemental/defines.h"
#include "gtest/gtest.h"

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

TEST(AST, Containers1) {
  FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r");
  Scanner scanner(input);
  Parser parser(&scanner);
  parser.Parse();
  assert(!parser.errors->count && "Parser errors");

  fclose(input);
}

TEST(AST, InterfacesDataCFA) {
  XreateManager* man = XreateManager::prepare
    ("interface(cfa){\n"
    "    operator map :: annotation1.\n"
    "}");

  auto answer = man->root->__interfacesData.equal_range(CFA);
  EXPECT_EQ(1, std::distance(answer.first, answer.second));

  Expression&& scheme = move(answer.first->second);

  EXPECT_EQ(Operator::MAP, scheme.op);
  EXPECT_EQ("annotation1", scheme.getOperands().at(0).getValueString());
}

TEST(AST, syntax_recognizeIdentifiers) {
  XreateManager* man = XreateManager::prepare(R"Code(
            test= function(a:: num):: num; entry {
                a = b:: int.
                b = 8:: int.

                a
            }
    )Code");
}

TEST(AST, syntax_operatorIndex) {
  XreateManager* man = XreateManager::prepare(R"Code(
            test= function(a:: num):: num; entry {
                b = a[1].
                b
            }
    )Code");
}

TEST(AST, Variants_switch) {
  XreateManager* man = XreateManager::prepare(R"Code(
    Color = type variant{Blue, White, Green}.

    main = function:: int {
        x = White()::Color.

        switch variant(x)::int
            case (Green) {0}
            case (White) {1}
            case (Blue){2}
    }
)Code");

  Expression e = man->root->findFunction("main")->getEntryScope()->getBody();
  ASSERT_EQ(4, e.getOperands().size());
  ASSERT_EQ(3, e.blocks.size());
}

TEST(AST, DISABLED_InterfacesDataDFA) { }

TEST(AST, DISABLED_InterfacesDataExtern) { }

TEST(AST, Doc_LiteralsAndExpressions) {
  XreateManager* man = XreateManager::prepare(
    R"Code(

    Record1 = type {year:: int, month:: string}.
    isOdd = function(x :: int) :: bool {true}

    test = function:: bool; entry {
        x1 = 5 :: int.
        x2 = "Nimefurahi kukujua":: string.
        x3 = {year = 1934, month = "april"}:: Record1.
        x4 = {16, 8, 3}             :: [int].
        x41 = [1..18]:: [int].
        x5 = 8>=3:: bool.
        x6 = "Blue" <> "Green" :: bool.
        x7 = -true:: bool.
        colors = {"Green", "Blue"} :: [string].
        color = colors[0] :: string.
        date = {year = 1934, month = "april"}:: Record1. year = date["year"] :: int.
        a = 0::int.  b = 0 :: int.
        x7 = a - b:: int.
        result = isOdd(6) :: bool.

        true
    }

    )Code");

  ASSERT_TRUE(true);
}

TEST(AST, Doc_CodeBlocks1) {
  XreateManager* man = XreateManager::prepare(
    getDocumentationExampleById("documentation/Syntax/syntax.xml", "CodeBlocks1"));

  FnNoArgs resultFn = (FnNoArgs) man->run();
  int resultExpected = resultFn();
  ASSERT_EQ(12, resultExpected);
}

TEST(AST, Doc_Functions1) {
  XreateManager* man = XreateManager::prepare(
    getDocumentationExampleById("documentation/Syntax/syntax.xml", "Functions1"));

  ASSERT_TRUE(true);
}

TEST(AST, Doc_FunctionSpecializations1) {
  XreateManager* man = XreateManager::prepare(
    getDocumentationExampleById("documentation/Syntax/syntax.xml", "FunctionSpecialization1"));

  ASSERT_TRUE(true);
}

TEST(AST, Doc_BranchStatements) {
  string code_IfStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "IfStatement1");
  string code_SwitchStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "SwitchStatement1");

  string code =
    R"Code(
test = function:: int; entry
{
  question = "Favorite color?":: string.
  monthNum = 2::                 int.

  %IfStatement1
  %SwitchStatement1

  monthName
}
  )Code";
  replace(code, "%IfStatement1", code_IfStatement1);
  replace(code, "%SwitchStatement1", code_SwitchStatement1);

  XreateManager* man = XreateManager::prepare(move(code));

  ASSERT_TRUE(true);
}

TEST(AST, Doc_LoopStatements) {
  string code_LoopStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement1");
  string code_LoopStatement2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement2");
  string code_FoldStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "FoldStatement1");
  string code_MapStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "MapStatement1");
  
  string code =
    R"Code(
test = function:: int; entry
{
  %LoopStatement1 
  %LoopStatement2
  %FoldStatement1
  %MapStatement1

  min
}
  )Code";
  
  replace(code, "%LoopStatement1", code_LoopStatement1);
  replace(code, "%LoopStatement2", code_LoopStatement2);
  replace(code, "%FoldStatement1", code_FoldStatement1);
  replace(code, "%MapStatement1",  code_MapStatement1);
  XreateManager::prepare(move(code));
  
  ASSERT_TRUE(true);
}

TEST(AST, Doc_Types){
  string code = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Types1");
  XreateManager::prepare(move(code));
  
  ASSERT_TRUE(true);
}

TEST(AST, Doc_Variants1){
  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Variants1");
  XreateManager::prepare(move(code_Variants1));
  
  ASSERT_TRUE(true);
}

TEST(AST, Doc_VariantsSwitch1){
  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "VariantsSwitch1");
  XreateManager::prepare(move(code_Variants1));
  
  ASSERT_TRUE(true);
}

TEST(AST, Doc_RecField1){
  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecField1");
  XreateManager::prepare(move(code_Variants1));
  
  ASSERT_TRUE(true);
}

TEST(AST, Doc_RecUpdate1){
  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecUpdate1");
  XreateManager::prepare(move(code_Variants1));
  
  ASSERT_TRUE(true);
}

TEST(AST, Doc_Versions1){
  string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Versions1_1");
  string code_Variants2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Versions1_2");
  
  string code  = R"Code(
test = function:: int; entry
{
    <BODY>
    y
})Code";
  
  {
    XreateManager* man = XreateManager::prepare(move(code_Variants1));
    man->run();
    delete man;
    ASSERT_TRUE(true);
  }
  
  {
    replace(code, "<BODY>", code_Variants2);
    auto man = details::tier1::XreateManager::prepare(move(code));
    ASSERT_DEATH(man->analyse(), ".*versions graph.*");
  }
}