/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 *
 * compilation.cpp
 *
 *  Created on: -
 *      Author: pgess <v.melnychenko@xreate.org>
 */

#include "xreatemanager.h"
#include "gtest/gtest.h"
using namespace xreate;
//DEBT implement no pkgconfig ways to link libs
//TOTEST FunctionUnit::compileInline

TEST(Compilation, functionEntry1){
    std::unique_ptr<XreateManager> program(XreateManager::prepare(
        "func1 = function(a:: int):: int {a+8} \
         func2 = function::int; entry {12 + func1(4)}      \
        "));

    void* entryPtr = program->run();
    int (*entry)() = (int (*)())(intptr_t)entryPtr;

    int answer = entry();
    ASSERT_EQ(24, answer);
}

TEST(Compilation, full_IFStatementWithVariantType){
    XreateManager* man = XreateManager::prepare(
        "Color = type variant {RED, BLUE, GREEN}.\n"
        "\n"
        "   main = function(x::int):: bool; entry {\n"
        "       color = if (x == 0 )::Color {RED()} else {BLUE()}.\n"
        "       if (color == BLUE())::bool {true} else {false}\n"
        "   }"
    );

    bool (*main)(int) = (bool (*)(int)) man->run();
    ASSERT_FALSE(main(0));
    ASSERT_TRUE(main(1));
}

TEST(Compilation, full_Variant1){
    XreateManager* man = XreateManager::prepare(R"Code(
        OneArgument = type{x::int}.
        TwoArguments = type{x::int, y::int}.

        Command= type variant{
            Add::TwoArguments,
            Dec::OneArgument
        }.

        main = function::Command; entry {
            Dec({x=2})::Command
        }
    )Code");

    void (*main)() = (void (*)()) man->run();
}

TEST(Compilation, full_SwitchVariant1){
    XreateManager* man = XreateManager::prepare(R"Code(
        OneArgument = type{x::int}.
        TwoArguments = type{x::int, y::int}.

        Command= type variant{
            Add::TwoArguments,
            Dec::OneArgument
        }.

        main = function::int; entry {
            //command = Dec({x = 8}):: Command.
            command = Add({x= 3, y= 5}):: Command.

            switch variant(command)::int
            case(Add){command["x"] + command["y"]}
            case(Dec){command["x"]}
        }
    )Code");

    int (*mainFn)() = (int (*)()) man->run();
    int result = mainFn();
    ASSERT_EQ(8, result);
}

TEST(Compilation, full_SwitchVariantNoArguments2){
    XreateManager* man = XreateManager::prepare(R"Code(
        Command= type variant{Add, Dec}.

        main = function::int; entry {
            command = Dec():: Command.

            switch variant(command)::int
            case(Add){0}
            case(Dec){1}
        }
    )Code");

    int (*mainFn)() = (int (*)()) man->run();
    int result = mainFn();
    ASSERT_EQ(1, result);
}

TEST(Compilation, full_SwitchVariantMixedArguments3){
    XreateManager* man = XreateManager::prepare(R"Code(
        TwoArguments = type{x::int, y::int}.
        Command= type variant{
            Add::TwoArguments,
            Dec
        }.

        main = function(arg::int)::     int; entry {
            command = if (arg > 0)::Command {Dec()} else {Add({x=1,y=2})}.

            switch variant(command)::int
            case(Add){0}
            case(Dec){1}
        }
    )Code");

    int (*mainFn)(int) = (int (*)(int)) man->run();
    int result = mainFn(5);
    ASSERT_EQ(1, result);
}

TEST(Compilation, full_StructUpdate){
    XreateManager* man = XreateManager::prepare(
    R"Code(

    Rec = type {
        a :: int,
        b:: int
    }.

    test= function:: int; entry {
        a = {a = 18, b = 20}:: Rec.

        b = a + {a = 11}:: Rec.
        b["a"]
    }
    )Code");

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

    ASSERT_EQ(11, result);
}

TEST(Compilation, AnonymousStruct_init_index){
    std::string code =
R"Code(

    main = function:: int; entry {
        x = {10, 15} :: {int, int}.
        x[1]
    }

)Code";

    std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
    int (*main)() = (int (*)()) man->run();

    EXPECT_EQ(15, main());
}

TEST(Compilation, AnonymousStruct_init_update){
    std::string code =
R"Code(

    main = function:: int; entry {
        x = {10, 15} :: {int, int}.
        y = x + {6}:: {int, int}.
        y[0]
    }

)Code";

    std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
    int (*main)() = (int (*)()) man->run();

    EXPECT_EQ(6, main());
}

TEST(Compilation, BugIncorrectScopes1){
    std::string code =
R"Code(

    init = function:: int {10}
    main = function(cmd:: int):: int; entry {
        x = init():: int.
        if(cmd > 0):: int { x + 1 } else { x }
    }
)Code";

    std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
    int (*mainFn)(int) = (int (*)(int)) man->run();

    EXPECT_EQ(11, mainFn(1));
}

TEST(Compilation, Sequence1){
        std::string code =
R"Code(
    interface(extern-c){
      libbsd = library:: pkgconfig("libbsd").

      include {
        libbsd = {"bsd/stdlib.h", "string.h"}
      }.
    }

    start = function:: i32; entry {
        seq {
            nameNew = "TestingSequence":: string.
            setprogname(nameNew)

        } {strlen(getprogname())}::i32
    }
)Code";

    std::unique_ptr<XreateManager> man(XreateManager::prepare(move(code)));
    int (*startFn)() = (int (*)()) man->run();

    int nameNewLen = startFn();
    ASSERT_EQ(15, nameNewLen);
}

