/* 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 <boost/format.hpp>
#include "gtest/gtest.h"

using namespace xreate;

TEST(LateReasoning, test2) {
    FILE* input = fopen("scripts/latereasoning/test2.xreate", "r");
    assert(input != nullptr);

    std::unique_ptr<XreateManager> man(XreateManager::prepare(input));

    int (*main)() = (int (*)())man->run();
    int result = main();

    ASSERT_EQ(3, result);
}

/**
 *   Test plan:
 *     - add late annotation(several variants)
 *     - define late bindings
 *     - get late variant wrt defined bindings
 **/

TEST(LateReasoning, PutAndGetLateAnnotation1) {
    #define FORMATSYMBOL(s) (formatSymb % s.identifier % s.version  % s.scope).str()

    Attachments::init<LateBinding>();
    Attachments::init<versions::VariableVersion>();
    std::unique_ptr<TranscendLayer> transcend(new TranscendLayer());

    std::unique_ptr<CodeScope> scope(new CodeScope(nullptr));
    Symbol symbA = scope->addDefinition(Atom<Identifier_t>("a"), Expression());
    Symbol symbB = scope->addDefinition(Atom<Identifier_t>("b"), Expression());
    Symbol symbC = scope->addDefinition(Atom<Identifier_t>("c"), Expression());
    Symbol symbTarget = scope->addDefinition(Atom<Identifier_t>("target"), Expression());

    SymbolPacked symbpA = transcend->pack(symbA, "a");
    SymbolPacked symbpB = transcend->pack(symbB, "b");
    SymbolPacked symbpC = transcend->pack(symbC, "c");
    SymbolPacked symbpTarget = transcend->pack(symbTarget, "target");

    boost::format formatSymb("s(%1%,%2%,%3%)");
    boost::format formatLateAnnotation("late(%1%, (%2%, %3%, %4%), (%5%, %6%, %7%), %8%).");

    //Add `variant1` variant
    transcend->addRawScript((formatLateAnnotation
        % FORMATSYMBOL(symbpTarget)
        % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) % FORMATSYMBOL(symbpC)
        % "guard1" % "guard1" % "guard1"
        % "result(variant1)"
    ). str());

    //Add `result2` variant
    transcend->addRawScript((formatLateAnnotation
        % FORMATSYMBOL(symbpTarget)
        % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) % FORMATSYMBOL(symbpC)
        % "guard2" % "guard2" % "guard2"
        % "result(variant2)"
    ). str());

    transcend->run();

    //Define keys
    Attachments::put<LateBinding>(symbA, Expression(Operator::CALL, {Atom<Identifier_t>("guard2")}));
    Attachments::put<LateBinding>(symbB, Expression(Operator::CALL, {Atom<Identifier_t>("guard2")}));
    Attachments::put<LateBinding>(symbC, Expression(Operator::CALL, {Atom<Identifier_t>("guard2")}));

    //Fetch late annotation
    ReasoningModel model = transcend->queryCompiled();
    StaticModel answer = model.queryLate("result", symbpTarget);
    ASSERT_EQ(1, answer.size());

    std::tuple<std::string> answerParsed = transcend->parse<std::string>(answer.begin()->second);

    ASSERT_STREQ("variant2", std::get<0>(answerParsed).c_str());
}

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