#ifndef COMPILEPASS_H
#define COMPILEPASS_H

#include <compilation/latecontextcompiler2.h>
#include "abstractpass.h"
#include "llvm/IR/Function.h"

namespace xreate {
	class AdhocScheme;
	class ClaspLayer;
	class ContextQuery;
	class CallStatement;
}

namespace xreate {

class CompilePass;

namespace compilation {

class CodeScopeUnit;
class FunctionUnit;


struct Context{
    FunctionUnit* function;
    CodeScopeUnit* scope;
    CompilePass* pass;
};

class CodeScopeUnit {
public:
    CodeScopeUnit(CodeScope* codeScope, FunctionUnit* f, CompilePass* compilePass);

    void bindArg(llvm::Value* var, std::string&& name);
    std::map<VID,llvm::Value*> __rawVars;

    void reset(){raw = nullptr;}
    llvm::Value* compile(const std::string& hintBlockDecl="");
    llvm::Value* compileSymbol(const Symbol& s, std::string hintRetVar="");
    llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="");
    CodeScope* scope;

private:
    CompilePass* pass;
    llvm::Value* raw = nullptr;
    FunctionUnit* function;

    CallStatement* findFunction(const std::string& callee);
};

class FunctionUnit{
public:
   FunctionUnit(ManagedFnPtr f, CompilePass* p)
    : function(f), pass(p), contextCompiler(this, p) {}

   llvm::Value* compileInline(std::vector<llvm::Value*>&& args, FunctionUnit* outer);
   bool isInline();
   llvm::Function* compile();

   CodeScopeUnit* getEntry();
   CodeScopeUnit* getScopeUnit(CodeScope* scope);
   CodeScopeUnit* getScopeUnit(ManagedScpPtr scope);

   ManagedFnPtr function;
   llvm::Function* raw = nullptr;
   AdhocScheme* adhocImplementation=nullptr;	//SECTIONTAG adhoc prefunc scheme declaration

private:
   CompilePass* pass;
   std::map<CodeScope*, std::unique_ptr<CodeScopeUnit>> scopes;

public:
   LateContextCompiler2 contextCompiler;
};

} // end of namespace `xreate::compilation`

class CompilePass : public AbstractPass<void> {
	friend class LateContextCompiler;
	friend class LateContextCompiler2;
	friend class compilation::CodeScopeUnit;
	friend class compilation::FunctionUnit;

public:
    CompilePass(PassManager* manager): AbstractPass<void>(manager) {}
    compilation::FunctionUnit* getFunctionUnit(const ManagedFnPtr& function);
    void run() override;
    llvm::Function* getEntryFunction();
    static void prepareQueries(ClaspLayer* clasp);

private:
    std::map<unsigned int, std::unique_ptr<compilation::FunctionUnit>> functions;
    llvm::Function* entry = 0;

    ContextQuery* queryContext;
};

}

#endif // COMPILEPASS_H
