#include "compilepass.h"
#include "clasplayer.h"
#include <ast.h>
#include "llvmlayer.h"

#include "query/containers.h"
#include "query/context.h"
#include "compilation/containers.h"
#include "compilation/transformations.h"
#include "compilation/latecontextcompiler2.h"
#include "ExternLayer.h"
#include "pass/adhocpass.h"
#include "compilation/targetinterpretation.h"

#include <boost/optional.hpp>
#include <memory>
#include <iostream>

using namespace std;
using namespace xreate;
using namespace xreate::compilation;
using namespace llvm;

//TODO use Scope<TargetLlvm>

//SECTIONTAG types/convert implementation
//TODO type conversion:
//a)    automatically expand types int -> bigger int; int -> floating
//b)    detect exact type of `num` based on max used numeral / function type
//c)    warning if need to truncate (allow/dissalow based on annotations)

namespace xreate {

llvm::Value*
doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder){
    if (tyTarget->isIntegerTy() && source->getType()->isIntegerTy())
    {
            llvm::IntegerType* tyTargetInt = llvm::dyn_cast<IntegerType>(tyTarget);
            llvm::IntegerType* tySourceInt = llvm::dyn_cast<IntegerType>(source->getType());

            if (tyTargetInt->getBitWidth() < tySourceInt->getBitWidth()){
                    return builder.CreateCast(llvm::Instruction::Trunc, source, tyTarget);
            }

            if (tyTargetInt->getBitWidth() > tySourceInt->getBitWidth()){
                    return builder.CreateCast(llvm::Instruction::SExt, source, tyTarget);
            }
    }

    if (source->getType()->isIntegerTy() && tyTarget->isFloatingPointTy()){
        return builder.CreateCast(llvm::Instruction::SIToFP, source, tyTarget);
    }

    return source;
}


std::string
BasicFunctionDecorator::prepareName(){
    AST* ast = FunctionUnit::pass->man->root;

    string name = ast->getFunctionVariants(FunctionUnit::function->__name).size() > 1?
        FunctionUnit::function->__name  + std::to_string(FunctionUnit::function.id()) :
        FunctionUnit::function->__name;

    return name;
}

std::vector<llvm::Type*>
BasicFunctionDecorator::prepareArguments(){
    LLVMLayer* llvm = FunctionUnit::pass->man->llvm;
    AST* ast = FunctionUnit::pass->man->root;
    CodeScope* entry = FunctionUnit::function->__entry;
    std::vector<llvm::Type*> signature;

    std::transform(entry->__bindings.begin(), entry->__bindings.end(), std::inserter(signature, signature.end()),
            [llvm, ast, entry](const std::string &arg)->llvm::Type* {
                assert(entry->__identifiers.count(arg));

                ScopedSymbol argid{entry->__identifiers.at(arg), VERSION_NONE};
                return llvm->toLLVMType(ast->expandType(entry->__declarations.at(argid).type));
    });

    return signature;
}

llvm::Type*
BasicFunctionDecorator::prepareResult(){
    LLVMLayer* llvm = FunctionUnit::pass->man->llvm;
    AST* ast = FunctionUnit::pass->man->root;
    CodeScope* entry = FunctionUnit::function->__entry;

    return llvm->toLLVMType(ast->expandType(entry->__declarations.at(ScopedSymbol::RetSymbol).type));
}

llvm::Function::arg_iterator
BasicFunctionDecorator::prepareBindings(){
    CodeScope* entry = FunctionUnit::function->__entry;
    CodeScopeUnit* entryCompilation = FunctionUnit::getScopeUnit(entry);
    llvm::Function::arg_iterator fargsI = FunctionUnit::raw->arg_begin();

    for (std::string &arg : entry->__bindings) {
        ScopedSymbol argid = entry->__identifiers[arg];

        entryCompilation->__rawVars[argid] = &*fargsI;
        fargsI->setName(arg);
        ++fargsI;
    }

    return fargsI;
}

//SECTIONTAG late-context FunctionDecorator
template<class Parent>
class LateContextFunctionDecorator: public Parent{
public:
    LateContextFunctionDecorator(ManagedFnPtr f, CompilePass* p)
        : Parent(f, p), contextCompiler(this, p)
    {}

protected:
    std::vector<llvm::Type*>  prepareArguments(){
        std::vector<llvm::Type*>&& arguments = Parent::prepareArguments();

        size_t sizeLateContextDemand = contextCompiler.getFunctionDemandSize();
        if (sizeLateContextDemand) {
            llvm::Type* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext());
            llvm::Type* tyDemand = llvm::ArrayType::get(ty32, sizeLateContextDemand);

            arguments.push_back(tyDemand);
        }

        return arguments;
    }

    llvm::Function::arg_iterator prepareBindings(){
        llvm::Function::arg_iterator fargsI = Parent::prepareBindings();

        size_t sizeLateContextDemand = contextCompiler.getFunctionDemandSize();
        if (sizeLateContextDemand){
            fargsI->setName("latecontext");
            contextCompiler.rawContextArgument = &*fargsI;
            ++fargsI;
        }

        return fargsI;
    }

public:
    LateContextCompiler2 contextCompiler;

};

//SECTIONTAG adhoc FunctionDecorator
template<class Parent>
class AdhocFunctionDecorator: public Parent{
public:
    AdhocFunctionDecorator(ManagedFnPtr f, CompilePass* p)
        : Parent(f, p)    {}

protected:
    llvm::Type* prepareResult(){
        PassManager* man = Parent::pass->man;
        CodeScope* entry = Parent::function->__entry;
        LLVMLayer* llvm = Parent::pass->man->llvm;
        AST* ast = Parent::pass->man->root;
        AdhocPass* adhocpass = reinterpret_cast<AdhocPass*>(man->getPassById(PassId::AdhocPass));

        if (! Parent::function->isPrefunction){
            return Parent::prepareResult();
        }

        adhocImplementation = adhocpass->determineForScope(entry);
        return llvm->toLLVMType(ast->expandType(adhocImplementation->getResultType()));
    }

public:
    AdhocScheme* adhocImplementation=nullptr;

};

typedef LateContextFunctionDecorator<
        AdhocFunctionDecorator<
        BasicFunctionDecorator>> DefaultFunctionUnit;


CodeScopeUnit::CodeScopeUnit(CodeScope* codeScope, FunctionUnit* f, CompilePass* compilePass)
    : scope(codeScope), pass(compilePass), function(f)
{}


llvm::Value*
CallStatementRaw::operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl) {
    llvm::Function* calleeInfo = dyn_cast<llvm::Function>(__callee);

    if (calleeInfo){
        auto argsFormal = calleeInfo->args();

        int pos=0;
        //SECTIONTAG types/convert function ret value
        for (auto argFormal = argsFormal.begin(); argFormal!=argsFormal.end(); ++argFormal, ++pos){
            args[pos] = doAutomaticTypeConversion(args[pos], argFormal->getType(), llvm->builder);
        }
    }

    return llvm->builder.CreateCall(__calleeTy, __callee, args, hintDecl);
}

//DEBT implement inlining
class CallStatementInline: public CallStatement{
public:
	CallStatementInline(FunctionUnit* caller, FunctionUnit* callee, LLVMLayer* l)
		: __caller(caller), __callee(callee), llvm(l) {}

	llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl) {
		//TOTEST inlining
//            CodeScopeUnit* entryCompilation = outer->getScopeUnit(function->__entry);
//            for(int i=0, size = args.size(); i<size; ++i) {
//                entryCompilation->bindArg(args.at(i), string(entryCompilation->scope->__bindings.at(i)));
//            }
//
//
//            return entryCompilation->compile();
	}

private:
	FunctionUnit* __caller;
	FunctionUnit* __callee;
        LLVMLayer* llvm;

        bool isInline(){
            // Symbol ret = Symbol{0, function->__entry};
            // bool flagOnTheFly = SymbolAttachments::get<IsImplementationOnTheFly>(ret, false);
            //TODO consider inlining
            return false;
        }
};


}

void
CodeScopeUnit::overrideDeclaration(const Symbol binding, Expression&& declaration){
    function->getScopeUnit(binding.scope)->__declarationsOverriden.emplace(binding.identifier, move(declaration));
}

//SECTIONTAG late-context find callee function
//TOTEST static late context decisions
//TOTEST dynamic late context decisions

CallStatement*
CodeScopeUnit::findFunction(const std::string& calleeName){
    LLVMLayer* llvm = pass->man->llvm;
    ClaspLayer* clasp = pass->man->clasp;
    DefaultFunctionUnit* function = dynamic_cast<DefaultFunctionUnit*>(this->function);
    ContextQuery* queryContext = pass->queryContext;

    const std::list<ManagedFnPtr>& specializations = pass->man->root->getFunctionVariants(calleeName);

    //if no specializations registered - check external function
    if (specializations.size()==0){
        llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName);

        return new CallStatementRaw(external, llvm);
    }

    //no decisions required
    if (specializations.size()==1){
        if (!specializations.front()->guardContext.isValid()) {
            return new CallStatementRaw( pass->getFunctionUnit(specializations.front())->compile(), llvm);
        }
    }

    //TODO move dictSpecialization over to a separate function in order to perform cache, etc.
    //prepare specializations dictionary
    std::map<Expression, ManagedFnPtr> dictSpecializations;

    boost::optional<ManagedFnPtr> variantDefault;
    boost::optional<ManagedFnPtr> variant;

    for(const ManagedFnPtr& f: specializations){
        const Expression& guard = f->guardContext;

        //default case:
        if (!guard.isValid()){
                variantDefault = f;
                continue;
        }

        assert(dictSpecializations.emplace(guard, f).second && "Found several identical specializations");
    }

    //check static context
    ScopePacked scopeCaller = clasp->pack(this->scope);
    const string atomSpecialization = "specialization";
    const Expression topicSpecialization(Operator::CALL, {(Atom<Identifier_t>(string(atomSpecialization))), (Atom<Identifier_t>(string(calleeName))), (Atom<Number_t>(scopeCaller))});

    const Decisions& decisions = queryContext->getFinalDecisions(scopeCaller);
    if (decisions.count(topicSpecialization)){
        variant =  dictSpecializations.at(decisions.at(topicSpecialization));
    }

    //TODO check only demand for this particular topic.
    size_t sizeDemand = function->contextCompiler.getFunctionDemandSize();

    //decision made if static context found or no late context exists(and there is default variant)
    bool flagHasStaticDecision =  variant || (variantDefault && !sizeDemand);

    //if no late context exists
    if (flagHasStaticDecision) {
        FunctionUnit* calleeUnit = pass->getFunctionUnit(variant? *variant: *variantDefault);

            //inlining possible based on static decision only
//        if (calleeUnit->isInline()) {
//            return new CallStatementInline(function, calleeUnit);
//        }

        return new CallStatementRaw(calleeUnit->compile(), llvm);
    }

    //require default variant if no static decision made
    assert(variantDefault);

    llvm::Function* functionVariantDefault = this->pass->getFunctionUnit(*variantDefault)->compile();
    llvm::Value* resultFn = function->contextCompiler.findFunction(calleeName, functionVariantDefault, scopeCaller);
    llvm::PointerType *resultPTy = cast<llvm::PointerType>(resultFn->getType());
    llvm::FunctionType *resultFTy = cast<llvm::FunctionType>(resultPTy->getElementType());
    return new CallStatementRaw(resultFn, resultFTy, llvm);
}

void
CodeScopeUnit::bindArg(llvm::Value* value, std::string&& alias)
{
    //reset cached compiled value if any
    raw = nullptr;

    //ensure existing of an alias
    assert(scope->__identifiers.count(alias));

    //memorize new value for an alias
    ScopedSymbol id = scope->__identifiers.at(alias);
    __rawVars[id] = value;
}

llvm::Value*
CodeScopeUnit::process(const Expression& expr, const std::string& hintVarDecl){

    Context ctx{this, this->function, this->pass};

    if (pass->targetInterpretation->isAcceptable(expr)){
        return pass->targetInterpretation->compile(expr, ctx);
    }

    llvm::Value* result = processLowlevel(expr, hintVarDecl);

    if (pass->transformations->isAcceptable(expr)){
        return pass->transformations->transform(expr, result, ctx);
    }

    return result;
}

llvm::Value*
CodeScopeUnit::processLowlevel(const Expression& expr, const std::string& hintVarDecl){
#define DEFAULT(x) (hintVarDecl.empty()? x: hintVarDecl)
    llvm::Value *left; llvm::Value *right;
    LLVMLayer& l = *pass->man->llvm;
    xreate::compilation::Advanced instructions = xreate::compilation::Advanced({this, function, pass});

    switch (expr.op) {
        case Operator::ADD: case Operator::SUB: case Operator::MUL:
        case Operator::DIV: case Operator::EQU: case Operator::LSS:
        case Operator::GTR: case Operator::NE:  case Operator::LSE:
        case Operator::GTE:

            assert(expr.__state == Expression::COMPOUND);
            assert(expr.operands.size() == 2);

            left = process(expr.operands[0]);
            right = process(expr.operands[1]);

            //SECTIONTAG types/convert binary operation
           	right =	doAutomaticTypeConversion(right, left->getType(), l.builder);
            break;

        default:;
    }

    switch (expr.op) {
        case Operator::ADD:
            return l.builder.CreateAdd(left, right, DEFAULT("tmp_add"));
            break;

        case Operator::SUB:
            return l.builder.CreateSub(left, right, DEFAULT("tmp_sub"));
            break;

        case Operator::MUL:
            return l.builder.CreateMul(left, right, DEFAULT("tmp_mul"));
            break;

        case Operator::DIV:
            return l.builder.CreateSDiv(left, right, DEFAULT("tmp_div"));
            break;

        case Operator::EQU:
            if (left->getType()->isIntegerTy()) return l.builder.CreateICmpEQ(left, right, DEFAULT("tmp_equ"));
            if (left->getType()->isFloatingPointTy()) return l.builder.CreateFCmpOEQ(left, right, DEFAULT("tmp_equ"));
            break;

        case Operator::NE:
            return l.builder.CreateICmpNE(left, right, DEFAULT("tmp_ne"));
            break;

        case Operator::LSS:
            return l.builder.CreateICmpSLT(left, right, DEFAULT("tmp_lss"));
            break;

        case Operator::LSE:
            return l.builder.CreateICmpSLE(left, right, DEFAULT("tmp_lse"));
            break;

        case Operator::GTR:
            return l.builder.CreateICmpSGT(left, right, DEFAULT("tmp_gtr"));
            break;

        case Operator::GTE:
            return l.builder.CreateICmpSGE(left, right, DEFAULT("tmp_gte"));
            break;

        case Operator::NEG:
            left = process(expr.operands[0]);
            return l.builder.CreateNeg(left, DEFAULT("tmp_neg"));
            break;

        case Operator::CALL: {
            assert(expr.__state == Expression::COMPOUND);

            std::string nameCallee = expr.getValueString();
            shared_ptr<CallStatement> callee(findFunction(nameCallee));

            //prepare arguments
            std::vector<llvm::Value *> args;
            args.reserve(expr.operands.size());

            std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()),
				[this](const Expression &operand) {
					return process(operand);
				}
            );

            ScopePacked outerScopeId = pass->man->clasp->pack(this->scope);

            //TASK a) refactor CALL/ADHOC/find function
            //SECTIONTAG late-context propagation arg
            size_t calleeDemandSize = pass->queryContext->getFunctionDemand(nameCallee).size();
            if (calleeDemandSize){
                DefaultFunctionUnit* function = dynamic_cast<DefaultFunctionUnit*>(this->function);
            	llvm::Value* argLateContext = function->contextCompiler.compileContextArgument(nameCallee, outerScopeId);
            	args.push_back(argLateContext);
            }

            return (*callee)(move(args), DEFAULT("res_"+nameCallee));
        }

        case Operator::IF:
        {
        	return instructions.compileIf(expr, DEFAULT("tmp_if"));
        }

        case Operator::SWITCH:
        {
        	return instructions.compileSwitch(expr, DEFAULT("tmp_switch"));
        }

        case Operator::LOOP_CONTEXT:
        {
        	return instructions.compileLoopContext(expr, DEFAULT("tmp_loop"));
        }

        case Operator::LOGIC_AND: {
        	assert(expr.operands.size() == 1);
        	return process (expr.operands[0]);
        }

        case Operator::LIST:
        {
           return instructions.compileListAsSolidArray(expr, DEFAULT("tmp_list"));
        };

        case Operator::LIST_RANGE:
        {
            assert(false); //no compilation phase for a range list
          //  return InstructionList(this).compileConstantArray(expr, l, hintRetVar);
        };

        case Operator::LIST_NAMED:
        {
            typedef Expanded<TypeAnnotation> ExpandedType;

            ExpandedType tyRaw = l.ast->expandType(expr.type);

            const std::vector<string> fields = (tyRaw.get().__operator == TypeOperator::CUSTOM)?
                l.layerExtern->getStructFields(l.layerExtern->lookupType(tyRaw.get().__valueCustom))
                : tyRaw.get().fields;

            std::map<std::string, size_t> indexFields;
            for(size_t i=0, size = fields.size(); i<size; ++i){
                indexFields.emplace(fields[i], i);
            }

            llvm::StructType* tyRecord = llvm::cast<llvm::StructType>(l.toLLVMType(tyRaw));
            llvm::Value* record = llvm::UndefValue::get(tyRecord);

            for (size_t i=0; i<expr.operands.size(); ++i){
                const Expression& operand = expr.operands.at(i);
                unsigned int fieldId = indexFields.at(expr.bindings.at(i));

                llvm::Value* result = 0;

//TODO Null ad hoc Llvm implementation
//                if (operand.isNone()){
//                    llvm::Type* tyNullField = tyRecord->getElementType(fieldId);
//                    result = llvm::UndefValue::get(tyNullField);
//
//                } else {
                    result = process(operand);
//                }

                assert (result);
                record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef<unsigned>({fieldId}));
            }

            return record;
        };



        case Operator::MAP:
        {
            assert(expr.blocks.size());
            return instructions.compileMapSolidOutput(expr, DEFAULT("map"));
        };

        case Operator::FOLD:
        {
            return instructions.compileFold(expr, DEFAULT("fold"));
        };

        case Operator::FOLD_INF:
        {
            return instructions.compileFoldInf(expr, DEFAULT("fold"));
        };

        case Operator::INDEX:
        {
                //TODO allow multiindex
            assert(expr.operands.size()==2);
            assert(expr.operands[0].__state == Expression::IDENT);

            const std::string& hintIdent= expr.operands[0].getValueString();
            Symbol s = Attachments::get<Symbol>(expr.operands[0]);
            const ExpandedType& t2 = pass->man->root->expandType(CodeScope::findDeclaration(s).type);

            llvm::Value* aggr  = compileSymbol(s, hintIdent);

            switch (t2.get().__operator)
            {
                case TypeOperator::STRUCT: case TypeOperator::CUSTOM:
                {
                    const Expression& idx = expr.operands.at(1);
                    assert(idx.__state == Expression::STRING);
                    std::string idxField = idx.getValueString();

                    return instructions.compileStructIndex(aggr, t2, idxField);
                };

                case TypeOperator::ARRAY: {
                    std::vector<llvm::Value*> indexes;
                    std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()),
                        [this] (const Expression& op){
                            return process(op);
                        }
                    );

                    return instructions.compileArrayIndex(aggr, indexes, DEFAULT(string("el_") + hintIdent));
                };

                default:
                    assert(false);
            }
        };

        	//SECTIONTAG adhoc actual compilation
                //TODO a) make sure that it's correct: function->adhocImplementation built for Entry scope and used in another scope
        case Operator::ADHOC: {
            DefaultFunctionUnit* function = dynamic_cast<DefaultFunctionUnit*>(this->function);
            assert(function->adhocImplementation && "Adhoc implementation not found");
            string comm = expr.operands[0].getValueString();

            CodeScope* scope = function->adhocImplementation->getImplementationForCommand(comm);
            CodeScopeUnit* unitScope = function->getScopeUnit(scope);

            //SECTIONTAG types/convert ADHOC ret convertation
            llvm::Type* resultTy = l.toLLVMType( pass->man->root->expandType(function->adhocImplementation->getResultType()));
            return doAutomaticTypeConversion(unitScope->compile(), resultTy, l.builder);
        };

        case Operator::NONE:
            assert(expr.__state != Expression::COMPOUND);

            switch (expr.__state) {
                case Expression::IDENT: {
                    Symbol s = Attachments::get<Symbol>(expr);
                    return compileSymbol(s, expr.getValueString());
                }

                case Expression::NUMBER: {
                    int literal = expr.getValueDouble();
                    return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), literal);
                }

                case Expression::STRING: {
                    return instructions.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str"));
                };

                case Expression::VARIANT: {
                	const ExpandedType& typVariant = pass->man->root->expandType(expr.type);
                	llvm::Type* typRaw = l.toLLVMType(typVariant);
                	int value = expr.getValueDouble();
                    return llvm::ConstantInt::get(typRaw, value);
                }

                default: {
                    break;
                }
            };

            break;

        default: break;

    }

    assert(false);
    return 0;
}

llvm::Value*
CodeScopeUnit::compile(const std::string& hintBlockDecl){
    if (raw != nullptr) return raw;


    if (!hintBlockDecl.empty()) {
        llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm::getGlobalContext(), hintBlockDecl, function->raw);
        pass->man->llvm->builder.SetInsertPoint(block);
    }

    raw = process(scope->getBody());
    return raw;
}

llvm::Value*
CodeScopeUnit::compileSymbol(const Symbol& s, std::string hintRetVar)
{
    CodeScope* scope = s.scope;
    CodeScopeUnit* self = function->getScopeUnit(scope);

    if (self->__rawVars.count(s.identifier))     {
        return self->__rawVars[s.identifier];
    }

    //compilation transformations could override symbol declarations.
    Expression declaration = CodeScope::findDeclaration(s);
    if (!declaration.isDefined()){
        if (self->__declarationsOverriden.count(s.identifier)){
            declaration = self->__declarationsOverriden[s.identifier];

        } else {
            assert(false); //in case of bindings there should be raws already.
        }
    }

    return self->__rawVars[s.identifier] = self->process(declaration, hintRetVar);
}



llvm::Function*
FunctionUnit::compile(){
    if (raw != nullptr) return raw;

    LLVMLayer* llvm = pass->man->llvm;
    llvm::IRBuilder<>& builder = llvm->builder;

    string&& functionName = prepareName();
    std::vector<llvm::Type*>&& types = prepareArguments();
    llvm::Type* expectedResultType = prepareResult();

    llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false);
    raw = llvm::cast<llvm::Function>(llvm->module->getOrInsertFunction(functionName, ft));
    prepareBindings();


    const std::string&blockName =  "entry";
    llvm::BasicBlock* blockCurrent = builder.GetInsertBlock();

    llvm::Value* result =getScopeUnit(function->__entry)->compile(blockName);
    assert(result);

    //SECTIONTAG types/convert function ret value
    builder.CreateRet(doAutomaticTypeConversion(result, expectedResultType, llvm->builder));

    if (blockCurrent){
    	builder.SetInsertPoint(blockCurrent);
    }

    llvm->moveToGarbage(ft);
    return raw;
}

CodeScopeUnit*
FunctionUnit::getScopeUnit(CodeScope* scope){
    if (!scopes.count(scope)){
    	CodeScopeUnit* unit = new CodeScopeUnit(scope, this, pass);
        scopes.emplace(scope, std::unique_ptr<CodeScopeUnit>(unit));
    }

    return scopes.at(scope).get();
}

CodeScopeUnit*
FunctionUnit::getEntry(){
	return getScopeUnit(function->getEntryScope());
}

CodeScopeUnit*
FunctionUnit::getScopeUnit(ManagedScpPtr scope){
    return getScopeUnit(&*scope);
}

FunctionUnit*
CompilePass::getFunctionUnit(const ManagedFnPtr& function){
	unsigned int id = function.id();

    if (!functions.count(id)){
    	FunctionUnit* unit = new DefaultFunctionUnit(function, this);
        functions.emplace(id, unit);
        return unit;
    }

    return functions.at(id);
}

void
CompilePass::run(){
    transformations = new Transformations(this);
    transformations->registerTransformer(new TransformerSaturation(transformations));

    targetInterpretation = new TargetInterpretation(this->man->root, this);

    queryContext = reinterpret_cast<ContextQuery*> (man->clasp->getQuery(QueryId::ContextQuery));

    //Find out main function;
    ClaspLayer::ModelFragment model = man->clasp->query(Config::get("function-entry"));
    assert(model && "Error: No entry function found");
    assert(model->first != model->second && "Error: Ambiguous entry function");

    string nameMain = std::get<0>(ClaspLayer::parse<std::string>(model->first->second));
    FunctionUnit* unitMain = getFunctionUnit(man->root->findFunction(nameMain));
    entry = unitMain->compile();
}

llvm::Function*
CompilePass::getEntryFunction(){
	assert(entry);
	return entry;
}

void
CompilePass::prepareQueries(ClaspLayer* clasp){
	clasp->registerQuery(new containers::Query(), QueryId::ContainersQuery);
	clasp->registerQuery(new ContextQuery(), QueryId::ContextQuery);
}