/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on June 25, 2018, 5:42 PM
 *
 * \file    latex.cpp
 * \brief   Testing of latex
 */

#include "xreatemanager.h"
#include "pass/compilepass.h"
#include "transcendlayer.h"
#include "compilation/latex.h"
#include "aux/xreatemanager-decorators.h"
#include "compilation/scopedecorators.h"
#include "llvmlayer.h"

#include <boost/format.hpp>
#include <gtest/gtest.h>

using namespace xreate::latex;
using namespace xreate::compilation;
using namespace xreate;
using namespace std;

TEST(Latex, Analysis1) {
    std::string program =
        R"CODE(
        import raw("scripts/cfa/context.lp").

        a = function:: int
        {
            context:: forC(a).
            c()
        }

        b = function:: int
        {
            context:: forC(b).
            c()
        }

        c = function:: int {0}

        main = function:: int; entry
        {
            a() + b()
        }
    )CODE";

    std::unique_ptr<details::tier1::XreateManager> man(details::tier1::XreateManager::prepare(move(program)));

    CodeScope* blockC = man->root->findFunction("c")->getEntryScope();
    auto blockCP = man->transcend->pack(blockC);
    boost::format formatAlias("alias(%1%, %2%).");
    man->transcend->addRawScript((formatAlias % blockCP % "blockC").str());
    man->transcend->addRawScript(
        R"SCRIPT(
        latex_scope_demand(BlockC, forC):- alias(BlockC, blockC).
        latex_registered_subjects(forC, Variant):- bind_scope(_, Variant, strong); Variant = forC(_).
        )SCRIPT");
    man->analyse();

    ASSERT_EQ(1, man->transcend->query("latex_fn_demand").size());
    ASSERT_EQ(2, man->transcend->query("latex_decision").size());
}

TEST(Latex, Compilation1) {
    std::string program =
        R"CODE(

        a = function:: int
        {0}

        main = function:: int; entry
        {
            a()
        }
        )CODE";

    string script =
        R"SCRIPT(
            latex_fn_demand(%1%, subject1).
            latex_decision(%2%, subject1, 5).
            latex_registered_subjects(subject1, 1).
            latex_registered_subjects(subject1, 5).
        )SCRIPT";

    typedef LatexBruteFunctionDecorator<compilation::BasicFunctionUnit> FnImpl;
    typedef LatexBruteScopeDecorator<compilation::CachedScopeDecorator<compilation::BasicCodeScopeUnit>> ScopeImpl;

    std::unique_ptr<details::tier1::XreateManager> man(details::tier1::XreateManager::prepare(move(program)));
    ScopePacked scopeMainP = man->transcend->pack(man->root->findFunction("main")->getEntryScope());
    boost::format format(script);
    man->transcend->addRawScript((format % "a" % scopeMainP).str());
    man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery);
    man->analyse();

    std::unique_ptr<CompilePass> compiler(new compilation::CompilePassCustomDecorators<FnImpl, ScopeImpl>(man.get()));
    compiler->run();
    man->llvm->print();
}

TEST(Latex, Full1) {
    std::string program =
        R"CODE(
            import raw("scripts/cfa/context.lp").

            a = function:: int
            {
                context:: forC(a).
                c()
            }

            b = function:: int
            {
                context:: forC(b).
                c()
            }

            c = function:: int {0}

            main = function:: int; entry
            {
                a() + b()
            }
        )CODE";

    string script =
        R"SCRIPT(
            alias(%1%, scopeC).
            latex_scope_demand(ScopeC, forC) :- alias(ScopeC, scopeC).
            latex_registered_subjects(forC, forC(a)).
            latex_registered_subjects(forC, forC(b)).
        )SCRIPT";

    typedef LatexBruteFunctionDecorator<compilation::BasicFunctionUnit> FnImpl;
    typedef LatexBruteScopeDecorator<compilation::CachedScopeDecorator<compilation::BasicCodeScopeUnit>> ScopeImpl;

    std::unique_ptr<details::tier1::XreateManager> man(details::tier1::XreateManager::prepare(move(program)));
    ScopePacked scopeMainP = man->transcend->pack(man->root->findFunction("main")->getEntryScope());
    auto scopeCP = man->transcend->pack(man->root->findFunction("c")->getEntryScope());
    boost::format format(script);
    man->transcend->addRawScript((format %scopeCP).str());
    man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery);
    man->analyse();

    std::unique_ptr<CompilePass> compiler(new compilation::CompilePassCustomDecorators<FnImpl, ScopeImpl>(man.get()));
    compiler->run();
    man->llvm->print();
}

TEST(Latex, TransitFn1){
    std::string program =
        R"CODE(
            import raw("scripts/cfa/context.lp").

            branchA = function:: int
            {
                context:: sink(a).
                fnTransit()
            }

            branchB = function:: int
            {
                context:: sink(b).
                fnTransit()
            }

            fnSink = function:: int {0}
            fnTransit = function:: int {fnSink()}

            main = function:: int; entry
            {
                branchA() + branchB()
            }
        )CODE";

    string script =
        R"SCRIPT(
            alias(scopeSink, %1%).
            latex_scope_demand(ScopeSink, sink):- alias(scopeSink, ScopeSink).
            latex_registered_subjects(sink, sink(a)).
            latex_registered_subjects(sink, sink(b)).
        )SCRIPT";

    typedef LatexBruteFunctionDecorator<compilation::BasicFunctionUnit> FnImpl;
    typedef LatexBruteScopeDecorator<compilation::CachedScopeDecorator<compilation::BasicCodeScopeUnit>> ScopeImpl;

    std::unique_ptr<details::tier1::XreateManager> man(details::tier1::XreateManager::prepare(move(program)));
    auto scopeSinkP = man->transcend->pack(man->root->findFunction("fnSink")->getEntryScope());
    boost::format format(script);
    man->transcend->addRawScript((format %scopeSinkP).str());
    man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery);
    man->analyse();

    std::unique_ptr<CompilePass> compiler(new compilation::CompilePassCustomDecorators<FnImpl, ScopeImpl>(man.get()));
    compiler->run();
    man->llvm->print();
}