/*
 * 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 June 23, 2018, 2:51 PM
 *
 */

#ifndef LATEX_H
#define LATEX_H

#include "query/demand.h"
#include "analysis/utils.h"
#include "pass/compilepass.h"

namespace xreate{namespace demand{

template<class Parent>
class DemandBruteFnDecorator : public Parent{
public:
  DemandBruteFnDecorator(ManagedFnPtr f, CompilePass *p): Parent(f, p){}

protected:
  std::vector<llvm::Type *>
  prepareSignature() override{
    std::vector<llvm::Type *> &&signature = Parent::prepareSignature();

    DemandQuery* query = reinterpret_cast<DemandQuery *> (Parent::pass->man->transcend->getQuery(QueryId::DemandQuery));
    const Demand& demand = query->getFnDemand(Parent::__function->getName());
    signature.reserve(signature.size() + demand.size());

    unsigned int argOffset = signature.size();
    for(const auto &rec: demand){
      TypeAnnotation argTCust(TypeOperator::ALIAS, {});
      argTCust.__valueCustom = rec.second;
      const ExpandedType &argT = Parent::pass->man->root->expandType(argTCust);
      Expression bindingE;
      bindingE.type = argT.get();
      llvm::Type *argTRaw = Parent::pass->man->llvm->toLLVMType(argT);

      Parent::__function->addBinding(
        Atom<Identifier_t>(std::string(rec.first)),
        std::move(bindingE),
        argOffset++
      );
      signature.push_back(argTRaw);
    }

    return signature;
  }
};

/**
 * \brief Latex aware \ref xreate::compilation::IBruteScope decorator
 * \implements xreate::compilation::IBruteScope
 */
template<class Parent>
class DemandBruteScopeDecorator : public Parent{
public:
  DemandBruteScopeDecorator(const CodeScope *const codeScope, compilation::IBruteFunction *f, CompilePass *compilePass)
          : Parent(codeScope, f, compilePass){}

  compilation::IFnInvocation *
  findFunction(const Expression& opCall){
    DemandQuery* query = reinterpret_cast<DemandQuery *> (Parent::pass->man->transcend->getQuery(QueryId::DemandQuery));
    const std::string &calleeName = opCall.getValueString();
    compilation::IFnInvocation *opInvocBase = Parent::findFunction(opCall);

    const Demand &demand = query->getFnDemand(calleeName);
    if(!demand.size()) return opInvocBase;

    //prepare additional arguments
    std::vector<llvm::Value*> argsDemand;
    argsDemand.reserve(demand.size());

    const Supply& argsActual = query->getFnSupply(ASTSite{opCall.id});
    for(const auto& arg: demand){
      TypeAnnotation argTCust(TypeOperator::ALIAS, {});
      argTCust.__valueCustom = arg.second;
      const ExpandedType &argScheme = Parent::pass->man->root->expandType(argTCust);
      //llvm::Type* argSchemeRaw = Parent::pass->man->llvm->toLLVMType(argScheme);
      const Gringo::Symbol argTrAtom = argsActual.at(arg.first);
      Expression argE = analysis::representTransExpression(argTrAtom, argScheme, Parent::pass->man->transcend);
      argE.type = argScheme.get();
      llvm::Value* argValueRaw = Parent::process(argE);

      argsDemand.push_back(argValueRaw);
    }

    return new compilation::HiddenArgsFnInvocation(std::move(argsDemand), opInvocBase);
  }
};

}} //end of namespace xreate::demand

#endif /* LATEX_H */

