#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;

class TargetInterpretation;


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

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

    void bindArg(llvm::Value* value, std::string&& alias);
    void overrideDeclaration(const Symbol binding, Expression&& declaration);
    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="");
    llvm::Value* processLowlevel(const Expression& expr, const std::string& hintVarDecl="");
    
    CodeScope* scope;

private:
    CompilePass* pass;
    llvm::Value* raw = nullptr;
    FunctionUnit* function;
    std::unordered_map<VID, Expression> __declarationsOverriden;
    

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

class IFunctionDecorator {
protected:
    virtual std::string prepareName() = 0;
    virtual std::vector<llvm::Type*>  prepareArguments() = 0;
    virtual llvm::Type* prepareResult() = 0;
    virtual void prepareBindings() = 0;
    virtual ~IFunctionDecorator(){}
};

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

    llvm::Function* compile();

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

    ManagedFnPtr function;
    llvm::Function* raw = nullptr;
    
protected:
    std::string prepareName();
    std::vector<llvm::Type*>  prepareArguments();
    llvm::Type* prepareResult();
    
private:
    CompilePass* pass;
    std::map<CodeScope*, std::unique_ptr<CodeScopeUnit>> scopes;
};

    class Transformations;
} // 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:
    compilation::Transformations* transformations;
    
    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, compilation::FunctionUnit> functions;
    llvm::Function* entry = 0;

    ContextQuery* queryContext;
    compilation::TargetInterpretation* targetInterpretation;
    
};

}

#endif // COMPILEPASS_H
