/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 *
 * polymorph.cpp
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on October 11, 2017, 8:37 PM
 */

#include "xreatemanager.h"
#include "ast.h"
#include <list>
#include "gtest/gtest.h"
#include "clasplayer.h"

using namespace std;
using namespace xreate;
using namespace std;
TEST(Polymorphs, ast1) {
    xreate::XreateManager* man = xreate::XreateManager::prepare(
R"CODE(
    guard:: a {
        test = function:: int {0}
    }

    guard:: b {
        test = function:: int {1}
    }

    main = function:: int; entry { test() }

)CODE");

    const std::list<ManagedFnPtr>& specs = man->root->getFunctionSpecializations("test");
    ASSERT_EQ(2, specs.size());

    auto itSpecs = specs.begin();
    ASSERT_EQ("a", (*itSpecs)->guard.getValueString());
    itSpecs++;
    ASSERT_EQ("b", (*itSpecs)->guard.getValueString());
}

TEST(Polymorphs, StaticCall1) {
    xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(
R"CODE(
    import raw("scripts/dfa/polymorphism.lp").

    guard:: a {
        test = function:: int {0}
    }

    guard:: b {
        test = function:: int {1}
    }

    main = function:: int; entry { test()::int; callguard(b);dfa_polym(ret)}

)CODE");

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

    ASSERT_EQ(1, main());
}

TEST(Polymorphs, LateCall1){
    Attachments::init<LateBinding>();

    xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(
R"CODE(
    guard:: a {
        test = function:: int {0}
    }

    guard:: b {
        test = function:: int {1}
    }

    main = function:: int; entry{
        key = 0:: int; _guard.
        if (key == 0)::int {
            test()::int; dfa_polym(late)
        } else {0}
    }
)CODE");

    man->clasp->addRawScript(
R"RULE(
    late(SymbRet, list(SymbGuard), list(a), dfa_callguard(a)):-
        bind(SymbRet, dfa_polym(late));
        bind(SymbGuard, _guard).

    late(SymbRet, list(SymbGuard), list(b), dfa_callguard(b)):-
        bind(SymbRet, dfa_polym(late));
        bind(SymbGuard, _guard).
)RULE");
    man->analyse();

    CodeScope* scopeMainBody = man->root->findFunction("main")->getEntryScope();
    Symbol symbKey = Symbol{scopeMainBody->getSymbol("key"), scopeMainBody};

    Attachments::put<LateBinding>(symbKey, Expression(Operator::CALL, {Atom<Identifier_t>("b")}));
    int (*main)() = (int (*)()) man->run();

    ASSERT_EQ(1, main());
}