/*
 * 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/.
 * 
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on October 7, 2017
 */

/**
 * \file  src/compilation/polymorph.h
 * \brief Polymorphism-aware compilation routines 
 */

#ifndef POLYMORPHCOMPILER_H
#define POLYMORPHCOMPILER_H

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

namespace xreate{
namespace polymorph{

typedef Expression Selector;

/** \brief An instance of \ref compilation::IFnInvocation  to manage polymorphic functions invocation*/
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;
} ;

/** 
 * \brief Polymorphism aware \ref xreate::compilation::ICodeScopeUnit decorator
 * \implements xreate::compilation::ICodeScopeUnit
 */
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 */

