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

class Adhoc_pass_Adhoc1_Test;
#define FRIENDS_ADHOC \
    friend class ::Adhoc_pass_Adhoc1_Test;


#include "ast.h"
#include "xreatemanager.h"

#include "gtest/gtest.h"
#include <stdio.h>
#include <memory>
#include <sstream>
#include <stdlib.h>


#include "pass/adhocpass.h"
#include "pass/compilepass.h"
#include "llvmlayer.h"

using namespace xreate;
using namespace xreate::adhoc;
using namespace std;

TEST(Adhoc, ast_operatorAdhoc1){
    XreateManager* man = XreateManager::prepare (
    "test = function:: int {\n"
    "   ad hoc exception(nonImplemented)\n"
    "}");

    const Expression& subject = man->root->findFunction("test")->getEntryScope()->getBody();
    ASSERT_EQ(Operator::ADHOC, subject.op);

    Expression exception = AdhocExpression(subject).getCommand();
    ASSERT_EQ("exception", exception.getValueString());
}

TEST(Adhoc, ast_schemeAdhoc1){
    XreateManager* man = XreateManager::prepare (
    "interface(adhoc){\n"
    "		pre function expectNoErrors:: bool {\n"
    "			case (Error)  {false}\n"
    "			case (Success) {true}\n"
    "		}\n"
    "	}");

    assert(man->root->__interfacesData.count(ASTInterface::Adhoc));
    Expression adhocData = man->root->__interfacesData.find(ASTInterface::Adhoc)->second;
    ASSERT_EQ(Operator::SWITCH, adhocData.operands[0].op);
}

TEST(Adhoc, pass_Adhoc1){
    details::tier1::XreateManager* man = details::tier1::XreateManager::prepare (
    "interface(adhoc){\n"
    "   pre function expectNoErrors:: bool {\n"
    "       case (Error)  {false}\n"
    "       case (Success) {true}\n"
    "   }\n"
    "}\n"

    "main = function::int; entry {0} \n"
    );

    man->analyse();
    AdhocPass* pass = reinterpret_cast<AdhocPass* >(man->getPassById(PassId::AdhocPass));
    EXPECT_TRUE(pass->__schemes.size() > 0);

    AdhocScheme* scheme =  pass->__schemes.begin()->second;
    EXPECT_EQ("expectNoErrors", scheme->getName());
}

TEST(Adhoc, full_1){
    XreateManager* man = XreateManager::prepare (
    "	import raw (\"core/control-context.lp\").\n"

    "	interface(adhoc){\n"
    "		pre function expectNoErrors:: bool {\n"
    "			case (error)  {false}\n"
    "			case (success) {true}\n"
    "		}\n"
    "	}\n"

    "	test1 = pre function {\n"
    "		context:: expectNoErrors."
    "		ad hoc success\n"
    "	}"

    "main = function::bool;entry {\n"
    "			test1()\n"
    "		}");

    bool (*main)() = (bool (*)()) man->run();
    bool result = main();
    ASSERT_EQ(true, result);
}

TEST(Adhoc, full_2){
    XreateManager* man = XreateManager::prepare (
    "	import raw (\"core/control-context.lp\").\n"

    "	interface(adhoc){\n"
    "		pre function expectNoErrors:: bool {\n"
    "			case (error)  {false}\n"
    "			case (success) {true}\n"
    "		}\n"

    "		pre function expectErrors:: bool {\n"
    "			case (error)  {true}\n"
    "			case (success) {false}\n"
    "		}\n"

    "	}\n"

    "	test1 = pre function {\n"
    "		context:: expectNoErrors."
    "		ad hoc success\n"
    "	}\n"

    "	test2 = pre function {\n"
    "		context:: expectErrors."
    "		ad hoc success\n"
    "	}"

    "main = function::bool;entry {\n"
    "   test1() != test2()\n"
    "}");

    bool (*main)() = (bool (*)()) man->run();
    bool result = main();
    ASSERT_EQ(true, result);
}

//TODO adhoc type. FDecl sets wrong type in prefunc case(invalid type))
TEST(Adhoc, full_contextExpectNoErrrors){
    XreateManager* man = XreateManager::prepare (
    "import raw (\"core/control-context.lp\").\n"

    "interface(extern-c){\n"
    "  xml2 = library:: pkgconfig(\"libxml-2.0\").\n"
    "  \n"
    "  include {\n"
    "    xml2 = [\"stdlib.h\"]\n"
    "  }.\n"
    "}"

    "interface(adhoc){\n"
    "	pre function expectNoErrors:: bool {\n"
    "		case (error)  {false}\n"
    "		case (success) {true}\n"
    "	}\n"
    "}\n"

    "expectErrorCode = pre function(x::int){\n"
    "	if (x==0)::undef {ad hoc success}\n"
    "	else {ad hoc error}\n"
    "}\n"

    "main = function::bool; entry {\n"
    "		context:: expectNoErrors."
    "		expectErrorCode(system(\"ls -la\"))\n"
    "}"	);

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

//DEBT Implement compilation of switch adhoc
TEST(Adhoc, ast_switchAdhoc1){
    XreateManager* man = XreateManager::prepare (
        "test1 = function:: bool {\n"
        "   x = 0. \n"
        "   switch ad hoc (x:: errors)\n"
        "       case (error) {0}\n"
        "	case (success) {1}\n"
        "\n"
        "}"
    );

    const Expression& eSwitch = man->root->findFunction("test1")->getEntryScope()->getBody();
    EXPECT_EQ(Operator::SWITCH_ADHOC, eSwitch.op);
    EXPECT_EQ(3, eSwitch.operands.size());

    EXPECT_EQ(1, eSwitch.tags.size());
    EXPECT_EQ("errors", eSwitch.tags.begin()->first);

    Expression eCondition = eSwitch.getOperands()[0];
    EXPECT_EQ("x", eCondition.getValueString());
}
