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

#include "xreatemanager.h"
#include "analysis/transcendtarget.h"
#include "gtest/gtest.h"

using namespace std;
using namespace xreate;

TEST(Dimensions, AST_AnnotationTypeEmpty){
  std::string code = R"(
    my-rec-t = type predicate{}
  )";

  ASSERT_DEATH(XreateManager::prepare(move(code)), "-- line 2 col 31: Predicate type can't be empty.");
}

TEST(Dimensions, AST_RecognizeAnnIdents){
  std::string code = R"(
    my-func1 = function:: int; UnknownAnn() {0}
    my-func2 = function:: int; UnknownIdent {0}
  )";

  ASSERT_DEATH(XreateManager::prepare(move(code)),
    "-- line 2 col 32: Undefined predicate\n-- line 3 col 32: Undefined scope");
}

TEST(Dimensions, CompileFnAnn1){
  std::string code = R"(
    FnAnns = type predicate{
      FnMyAnn
    }.
    myf = function:: int; FnMyAnn() {
      0
    }
  )";

  auto xreate = details::tier2::XreateManager::prepare(move(code));
  xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass);
  xreate->executePasses();
  testing::internal::CaptureStdout();
  xreate->analyse();
  std::string outputActual = testing::internal::GetCapturedStdout();
  cout << outputActual;

  string outputExpected = "bind_func(\"myf\",fnMyAnn)";
  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}

TEST(Dimensions, CompileScopeAnn1){
  std::string code = R"(
    ScopeAnns = type predicate{
      MyScopeAnn
    }.

    myf = function:: int {
      context:: MyScopeAnn().
      0
    }
  )";

  auto xreate = details::tier2::XreateManager::prepare(move(code));
  xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass);
  xreate->executePasses();
  testing::internal::CaptureStdout();
  xreate->analyse();
  std::string outputActual = testing::internal::GetCapturedStdout();
  cout << outputActual;
  string outputExpected = "bind_scope(0, myScopeAnn)";
  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}

TEST(Dimensions, CompileScopeVar1){
  std::string code = R"(
    ScopeAnns = type predicate{
      MyScopeAnn(int)
    }.

    myf = function:: int {
      x = MyScopeAnn(8):: ScopeAnns.
      if (0>2):: int
      {
        context:: x.
        0
      } else {0}
    }
  )";

  auto xreate = details::tier2::XreateManager::prepare(move(code));
  xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass);
  xreate->executePasses();
  testing::internal::CaptureStdout();
  xreate->analyse();
  std::string outputActual = testing::internal::GetCapturedStdout();
  cout << outputActual;
  string outputExpected = "bind_scope(1,myScopeAnn(8))";
  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}

TEST(Dimensions, RepresentDeepAnn1){
  std::string code = R"(
figure = type predicate {
  circle(int)
}

fn-ann = type predicate {
  entry, demand(figure)
}

dem-fn = function:: int {0}

main-fn = function(x::int, y::int):: int; entry() {
  fig = circle(x+y):: figure.
  dem-fn():: int; demand(fig)
}
  )";

  auto xreate = details::tier2::XreateManager::prepare(move(code));
  xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass);
  xreate->executePasses();
  testing::internal::CaptureStdout();
  xreate->analyse();
  std::string outputActual = testing::internal::GetCapturedStdout();
  string outputExpected = "bind(s(0,-2,1),demand(circle(a(41))))";
  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}

TEST(Dimensions, CompileDeepAnn1){
  std::string code = R"(
figure = type predicate {
  oval, circle(int), square
}

fn-ann = type predicate {
  entry, demand(figure)
}

fn-dem = function:: int {0}

main-fn = function(x::int, y::int):: int; entry() {
  fig = circle(x+y):: figure.
  fn-dem():: int; demand(fig)
}
  )";

  auto xreate = details::tier2::XreateManager::prepare(move(code));
  xreate->transcend->addRawScript(R"(
    func_demand("fn-dem", "figf", "figure").
    func_supply(Site, "figf", Value):- bind(Site, demand(Value)).
  )");

  xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass);
  xreate->executePasses();
  //testing::internal::CaptureStdout();
  xreate->analyse();
  xreate->run();


  //std::string outputActual = testing::internal::GetCapturedStdout();
  //string outputExpected = "bind(s(0,-2,1),demand(circle(a(41))))";
  //ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}

TEST(Dimensions, DISABLED_Test1){
  string s =
R"(
  my-ann = type annotation {
    circle,
    square
  }

  my-func = function:: int; entry
  {
    x = circle():: my-ann.

    context:: x
    {
      0
    }
  }
)";
  cout << s;
}