#include "attachments.h"

using namespace xreate;

#include "passmanager.h"
#include "compilation/targetinterpretation.h"

#include "gtest/gtest.h"
#include "boost/scoped_ptr.hpp"

#define private public
#include "Parser.h"
#include "pass/interpretationpass.h"

using namespace xreate;
using namespace xreate::compilation;

TEST(Interpretation, Analysis_StatementIF_1){
    PassManager* man = PassManager::prepareForCode(

R"Code(
    main = function::int {
        x = "a":: string.

        y = if (x=="b"):: string; interpretation(force) {
            1

        } else {
            0
        }.

        y
    }
)Code"          );

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }
    Symbol symbolY = man->root->findFunction("main")->__entry->findSymbol("y");
    InterpretationData& dataSymbolY = Attachments::get<Symbol, InterpretationData>(symbolY);

    ASSERT_EQ(INTR_ONLY, dataSymbolY.resolution);
}

TEST(Interpretation, Compilation_StatementIF_1){
    PassManager* man = PassManager::prepareForCode(

R"Code(
    main = function::int; entry {
        x = "a":: string.

        y = if (x=="b"):: string; interpretation(force) {
            1

        } else {
            0
        }.

        y
    }
)Code"          );

    man->runWithoutCompilation();

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }


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

    ASSERT_EQ(0, result);
}

TEST(Interpretation, Analysis_StatementIF_InterpretCondition_1){
    PassManager* man = PassManager::prepareForCode(

R"Code(
    main = function::int {
        x = "a":: string; interpretation(force).

        y = if (x=="b"):: string {
            1

        } else {
            0
        }.

        y
    }
)Code"          );

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }

    Symbol symbolY = man->root->findFunction("main")->__entry->findSymbol("y");
    InterpretationData& dataSymbolY = Attachments::get<Symbol, InterpretationData>(symbolY);

    ASSERT_EQ(BOTH, dataSymbolY.resolution);
    ASSERT_EQ(IF_INTERPRET_CONDITION, dataSymbolY.op);
}

TEST(Interpretation, Compilation_StatementIF_InterpretCondition_1){
    PassManager* man = PassManager::prepareForCode(

R"Code(
    main = function::int; entry {
        x = "a":: string; interpretation(force).

        y = if (x=="b"):: string {
            1

        } else {
            0
        }.

        y
    }
)Code"          );

    man->runWithoutCompilation();

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }

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

    ASSERT_EQ(0, result);
}

TEST(Interpretation, Compilation_StatementFOLD_INTERPRET_INPUT_1){
    PassManager* man = PassManager::prepareForCode(

R"Code(
    main = function::int; entry {
        commands = ["inc", "double", "dec"]:: [string]; interpretation(force).

        loop fold(commands->comm::string, 10->operand):: int{
            switch(comm)::int

            case ("inc"){
                operand + 1
            }

            case ("dec"){
                operand - 1
            }

            case ("double"){
                operand * 2
            }
        }
    }
)Code"          );

    man->runWithoutCompilation();

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }

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

    ASSERT_EQ(21, result);
}

TEST(Interpretation, StatementCall_RecursionNo_1){
    PassManager* man = PassManager::prepareForCode(
R"Code(
    unwrap = function(data::undef, keys::undef):: undef; interpretation(force){
        loop fold(keys->key::string, data->a):: undef {
           a[key]
        }
    }

    start = function::num; entry{
        result = unwrap(
            {
                a = {
                        b =
                        {
                            c = "core"
                        }
                    }
            }, ["a", "b", "c"])::undef.

        result == "core"
    }
)Code" );

    man->runWithoutCompilation();

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }

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

    ASSERT_EQ(1, result);
}

TEST(Interpretation, StatementCall_RecursionDirect_1){
    PassManager* man = PassManager::prepareForCode(
R"Code(
    unwrap = function(data:: X):: Y {
        if (data[0] == "a")::Y {0} else {unwrap(data[0])}
    }

    entry = function:: i8; entry {
        unwrap([[[["a"]]]]):: i8; interpretation(force)
    }
)Code" );

    man->runWithoutCompilation();

    InterpretationPass* pass;
    if (man->isPassRegistered(PassId::InterpretationPass)){
        pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass);
    } else {
        pass = new InterpretationPass(man);
        pass->run();
    }

    InterpretationResolution resolutionActual = pass->process(man->root->findFunction("unwrap"));
    ASSERT_EQ(BOTH, resolutionActual);

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

    ASSERT_EQ(0, result);
}

TEST(Interpretation, StatementCall_RecursionIndirect_1){
        PassManager* man = PassManager::prepareForCode(
R"Code(
    funcA = function(data:: X):: Y {
        if (data == "a")::Y {0} else {funcB(data)}
    }

    funcB = function(data:: X):: Y {
        if (data == "b")::Y {1} else {funcA(data)}
    }

    entry = function:: i8; entry {
        funcA(""):: i8; interpretation(force)
    }
)Code" );

    InterpretationPass* pass = new InterpretationPass(man);
    ASSERT_DEATH(pass->run(), "Indirect recursion detected");
}

//TOTEST call indirect recursion(w/o tags)
//TASK implement and test Loop Inf (fix acc types in coco grammar)


