/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 *
 * latereasoning.cpp
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on April 21, 2018, 5:10 PM
 */

#include "xreatemanager.h"
#include "transcendlayer.h"
#include "pass/latereasoningpass.h"
#include "aux/latereasoning.h"
#include "supplemental/docutils.h"
#include "gtest/gtest.h"

using namespace std;
using namespace xreate;
using namespace xreate::latereasoning;

TEST(LateReasoning, Syntax1) {
  XreateManager* man = XreateManager::prepare(R"Code(
test = function:: int {
            x = 0::int.
            y1= switch late (x)::int{0}.
            y2= switch late(x+y1->a::int)::int{1}.
            y1+y2
}
)Code");

  CodeScope* scope = man->root->findFunction("test")->getEntryScope();
  Expression y1 = scope->getDefinition(scope->getSymbol("y1"));
  Expression y2 = scope->getDefinition(scope->getSymbol("y2"));

  ASSERT_EQ(1, y1.bindings.size());
  ASSERT_STRCASEEQ("x", y1.bindings.at(0).c_str());

  ASSERT_EQ(1, y2.bindings.size());
  ASSERT_STRCASEEQ("a", y2.bindings.at(0).c_str());
}

TEST(LateReasoning, Pass_DFAPassDec_1) {
  typedef LateReasoningTranscendDecorator<TranscendLayer> LRTranscend;

  auto man = details::tier2::XreateManager::prepare(R"Code(
Dom = type slave dom.

test = function::   int; entry
{
    LateIdent = 0:: Dom.
    0::             int; ann1(LateIdent)
}
)Code");

  CodeScope* scopeEntry = man->root->findFunction("test")->getEntryScope();
  ScopedSymbol keyS = scopeEntry->getSymbol("LateIdent");
  SymbolPacked keySP = man->transcend->pack(Symbol{keyS, scopeEntry});

  std::shared_ptr<LateReasoningScope> scopeLateEntry(new LateReasoningScope(nullptr));
  scopeLateEntry->addIdentifier("LateIdent", keySP);

  typedef LateReasoningDFAPassDecorator<dfa::DFAPass> LRDFAPass;
  LRDFAPass* dfaPass = new LRDFAPass(man);
  dfaPass->registerLateScope(scopeEntry, scopeLateEntry.get());

  man->transcend->addRawScript("dom(guard1; guard2).\n");

  man->registerPass(dfaPass, PassId::DFAPass, nullptr);
  man->executePasses();

  testing::internal::CaptureStdout();
  man->analyse();
  std::string outputActual = testing::internal::GetCapturedStdout();
  cout << outputActual << endl;

  string outputExpected = "late(s(0,-2,0), (s(1,-2,0)), (LateIdent), ann1(LateIdent)):- dom(LateIdent).";
  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));
}

TEST(LateReasoning, Doc_SwitchLateOperation) {
  string example = R"Code(
mul = function(x::float, y::float):: float
{
    x * y
}
<BODY>
)Code";

  string codeTranscend =
    R"Code(
arithmetic(fast; accurate).
)Code";

  string example_LateAnn_1 = getDocumentationExampleById("documentation/Transcend/latetranscend.xml", "LateAnn_1");
  string example_SwitchLate_1 = getDocumentationExampleById("documentation/Transcend/latetranscend.xml", "SwitchLate_1");
  string outputExpected = getDocumentationExampleById("documentation/Transcend/latetranscend.xml", "Output_LateAnn_1");
  replace(example, "<BODY>", example_SwitchLate_1);

  ASSERT_NE(std::string::npos, example_SwitchLate_1.find(example_LateAnn_1));

  typedef LateReasoningTranscendDecorator<TranscendLayer> LateTranscend;
  typedef LateReasoningDFAPassDecorator<dfa::DFAPass> LTDFAPass;

  auto man = details::tier2::XreateManager::prepare(move(example));
  std::unique_ptr<LateTranscend> transcend(new LateTranscend());
  man->transcend = transcend.get();
  man->transcend->addRawScript(move(codeTranscend));
  LTDFAPass* pass = new LTDFAPass(man);

  man->registerPass(pass, PassId::DFAPass, nullptr);
  man->executePasses();

  testing::internal::CaptureStdout();
  man->analyse();
  std::string outputActual = testing::internal::GetCapturedStdout();
  cout << outputActual << endl;
  ASSERT_NE(std::string::npos, outputActual.find(outputExpected));

  auto result = transcend->queryLate("arithmetic");
  ASSERT_EQ(1, result.annotations.size());
  ASSERT_EQ(2, result.annotations.begin()->second.guardedContent.size());
}
