/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * 
 * File:   polymorphcompiler.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on October 7, 2017
 */

#ifndef POLYMORPHCOMPILER_H
#define POLYMORPHCOMPILER_H

#include "pass/compilepass.h"
#include "query/polymorph.h"
#include "compilation/latereasoning.h"
#include "compilation/targetinterpretation.h"

namespace xreate{
namespace polymorph{

typedef Expression Selector;

class PolymorphFnInvocation: public compilation::IFnInvocation{
public:
    PolymorphFnInvocation(const latereasoning::LateAnnotation& selector,
                          std::list<ManagedFnPtr> calleeSpecializations,
                          CompilePass* pass,
                          PolymorphQuery* query,
                          LLVMLayer* llvm,
                          latereasoning::LateReasoningCompiler* compiler);

    llvm::Value* operator()(std::vector<llvm::Value *>&& args, const std::string& hintDecl = "");

private:
    latereasoning::LateAnnotation __selector;
    std::list<ManagedFnPtr> __calleeSpecializations;

    CompilePass* __pass;
    PolymorphQuery* __query;
    LLVMLayer* __llvm;
    latereasoning::LateReasoningCompiler* __compiler;
};

template <class Parent>
class PolymorphCodeScopeUnit: public Parent{
public:

    PolymorphCodeScopeUnit(const CodeScope * const codeScope,
                           compilation::IFunctionUnit* f,
                           CompilePass* compilePass)
    : Parent(codeScope, f, compilePass){ }

protected:

    compilation::IFnInvocation*
    findFunction(const Expression& opCall) override{
//        //Check does invocation require guards satisfaction
        const std::string& nameCallee = opCall.getValueString();
        const std::list<ManagedFnPtr>& specializations =
            Parent::pass->man->root->getFunctionSpecializations(nameCallee);

        //Extern function 
        if(specializations.size() == 0){
            return Parent::findFunction(opCall);
        }

        //No other specializations. Check if it has no guard
        if(specializations.size() == 1){
            if(!specializations.front()->guard.isValid()){
                return Parent::findFunction(opCall);
            }
        }

        //Several specializations
        PolymorphQuery* query = dynamic_cast<PolymorphQuery*> (
            Parent::pass->man->transcend->getQuery(QueryId::PolymorphQuery));
        const latereasoning::LateAnnotation& selector = query->get(opCall);

        compilation::Context ctx{this, Parent::function, Parent::pass};
        interpretation::InterpretationScope* scopeIntrpr =
            Parent::pass->targetInterpretation->transformContext(ctx);
        latereasoning::LateReasoningCompiler* compiler
            = new latereasoning::LateReasoningCompiler(dynamic_cast<interpretation::InterpretationFunction*>(scopeIntrpr->function), ctx);

        return new PolymorphFnInvocation(selector, specializations, Parent::pass,
            query, Parent::pass->man->llvm, compiler);
    }
};

}
} //end of xreate::polymorph

#endif /* POLYMORPHCOMPILER_H */

