/* 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>
 * 
 * compilepass.h
 */

#ifndef COMPILEPASS_H
#define COMPILEPASS_H

#include "abstractpass.h"
#include "llvm/IR/Function.h"

namespace xreate {
    class TranscendLayer;
    class CompilePass;
    class LLVMLayer;
    
    namespace interpretation{
        class TargetInterpretation;
    }
}

namespace xreate { namespace compilation {

class IBruteScope;
class IBruteFunction;
class TransformationsManager;

/** \brief Holds current position in %AST while traversing*/
struct Context{
    IBruteScope* scope;
    IBruteFunction* function;
    CompilePass* pass;
};

/** \brief Interface for custom function invocation operation compilation
 *  \details Default implementation is xreate::compilation::BruteFnInvocation
 */
class IFnInvocation {
public:
    /** \brief Returns result of custom function invocation for the given arguments*/
    virtual llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl="") = 0;
};

/** \brief Default IFnInvocation implementation */
class BruteFnInvocation: public IFnInvocation{
public:
    BruteFnInvocation(llvm::Function* callee, LLVMLayer* l)
        : __callee(callee), __calleeTy(callee->getFunctionType()), llvm(l) {}
    BruteFnInvocation(llvm::Value* callee, llvm::FunctionType* ty, LLVMLayer* l)
        : __callee(callee), __calleeTy(ty), llvm(l) {}
    
    /** \brief Makes type conversions and returns LLVM call statement with given arguments*/
    llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl="");
        
protected:
    llvm::Value* __callee;        
    llvm::FunctionType* __calleeTy;
    LLVMLayer* llvm;
};

/** \brief %Function invocation operator decorator to handle latex enabled functions with hidden extra arguments */
class HiddenArgsFnInvocation : public compilation::IFnInvocation{
public:
  HiddenArgsFnInvocation(std::vector<llvm::Value *> args, compilation::IFnInvocation *parent)
  : __args(args), __parent(parent){}

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

private:
  std::vector<llvm::Value *> __args;
  compilation::IFnInvocation *__parent;
};

/** \brief Interface to allow modification of CodeScope compilation
 *  \details Default implementation defined in xreate::compilation::DefaultCodeScopeUnit
 */
class IBruteScope{
public:
  CompilePass* const pass;
  IBruteFunction* const function;
  const CodeScope* const scope;
  llvm::BasicBlock* lastBlockRaw;

  IBruteScope(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass);
  virtual ~IBruteScope();

  virtual llvm::Value* compile(const std::string& hintBlockDecl="")=0;
  virtual llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="")=0;
  virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="", const TypeAnnotation& expectedT = TypeAnnotation())=0;

  virtual Symbol bindArg(llvm::Value* value, std::string&& alias)=0;
  virtual void bindArg(llvm::Value* value, const ScopedSymbol& s)=0;
  virtual void reset() = 0;

protected:
  /** \brief For subclasses to implement this method to define a function name resolution*/
  virtual IFnInvocation* findFunction(const Expression& opCall)=0;
};

/** \brief Minimal useful IBruteScope implementation suited for inheritance */
class BasicBruteScope: public IBruteScope{
public:
    BasicBruteScope(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass);

    llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="") override;
    llvm::Value* process(const Expression& expr, const std::string& hintAlias="", const TypeAnnotation& expectedT = TypeAnnotation()) override;
    llvm::Value* compile(const std::string& hintBlockDecl="") override;
    
protected:
    IFnInvocation* findFunction(const Expression& opCall) override;

private:
    std::string getIndexStr(const Expression& index);
};

/** \brief Interface to specify compilation of %Function */
class IBruteFunction{
public:
   IBruteFunction(CodeScope* entry, CompilePass* p): pass(p), __entry(entry){}
   virtual ~IBruteFunction();

    llvm::Function* compile();

    IBruteScope* getEntry();
    virtual ManagedFnPtr getASTFn() const {return ManagedFnPtr();};
    IBruteScope* getBruteScope(const CodeScope * const scope);
    IBruteScope* getScopeUnit(ManagedScpPtr scope);

    llvm::Function* raw = nullptr;
    
protected:
    CompilePass* pass=nullptr;
    CodeScope* __entry;
    
    virtual std::string prepareName() = 0;
    virtual std::vector<llvm::Type*>  prepareSignature() = 0;
    virtual llvm::Function::arg_iterator prepareBindings() = 0;
    virtual llvm::Type* prepareResult() = 0;
    virtual void applyAttributes() = 0;

private:
    std::map<const CodeScope * const, std::weak_ptr<IBruteScope>> __scopes;
    std::list<std::shared_ptr<IBruteScope>> __orphanedScopes;

protected:
  std::vector<llvm::Type*> getScopeSignature(CodeScope* scope);
};

/** \brief Minimal useful IBruteFunction implementation suited for inheritance */
class BasicBruteFunction: public IBruteFunction{
public:
    BasicBruteFunction(ManagedFnPtr f, CompilePass* p)
    : IBruteFunction(f->getEntryScope(), p), __function(f) {}

protected:
    std::string prepareName() override;
    virtual std::vector<llvm::Type*>  prepareSignature() override;
    virtual llvm::Type* prepareResult() override;
    virtual llvm::Function::arg_iterator prepareBindings() override;
    virtual void applyAttributes() override;
    virtual ManagedFnPtr getASTFn() const {return __function;};

protected:
  ManagedFnPtr __function;
};
} // end of namespace compilation

class CompilePass : public AbstractPass<void> {
    friend class compilation::BasicBruteScope;
    friend class compilation::IBruteFunction;

public:
    compilation::TransformationsManager* managerTransformations;
    interpretation::TargetInterpretation* targetInterpretation;
    
    CompilePass(PassManager* manager): AbstractPass<void>(manager) {}
    /** \brief Executes compilation process */
    void run() override;
    
    /**\brief Returns compiled specified %Function
     * \details Executes function compilation or read cache if it's already done
     */
    compilation::IBruteFunction* getBruteFn(const ManagedFnPtr& function);
    
    /**\brief Returns compiled main(entry) %Function in program */
    llvm::Function* getEntryFunction();
    
    /** \brief Initializes queries required by compiler. See xreate::IQuery, xreate::TranscendLayer */
    static void prepareQueries(TranscendLayer* transcend);
    void prepare();

protected:
    virtual compilation::IBruteFunction* buildFunctionUnit(const ManagedFnPtr& function)=0;
    virtual compilation::IBruteScope* buildCodeScopeUnit(const CodeScope* const scope, compilation::IBruteFunction* function)=0;
    
private:
    //TODO free `functions` in destructor
    std::map<unsigned int, compilation::IBruteFunction*> functions;
    llvm::Function* __fnEntryRaw = 0;
};

namespace compilation{

/** \brief Constructs compiler with desired %Function and %Code Scope decorators. See adaptability in xreate::CompilePass*/    
template<class FUNCTION_DECORATOR=void, class SCOPE_DECORATOR=void>
class CompilePassCustomDecorators: public ::xreate::CompilePass{
public:
    CompilePassCustomDecorators(PassManager* manager): ::xreate::CompilePass(manager) {}
    
    virtual compilation::IBruteFunction* buildFunctionUnit(const ManagedFnPtr& function) override{
        return new FUNCTION_DECORATOR(function, this);
    }
    
    virtual compilation::IBruteScope* buildCodeScopeUnit(const CodeScope* const scope, IBruteFunction* function) override{
        return new SCOPE_DECORATOR(scope, function, this);
    }        
};

template<>
compilation::IBruteFunction*
CompilePassCustomDecorators<void, void>::buildFunctionUnit(const ManagedFnPtr& function);

template<>
compilation::IBruteScope*
CompilePassCustomDecorators<void, void>::buildCodeScopeUnit(const CodeScope* const scope, IBruteFunction* function);

}} //end of namespace xreate::compilation

#endif // COMPILEPASS_H
