#ifndef COMPILEPASS_H
#define COMPILEPASS_H

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

namespace xreate {


class AdhocScheme;
class ClaspLayer;
class ContextQuery;

class CompilePass : public AbstractPass<void> {
	friend class LateContext;

public:
        class CodeScopeUnit;
        class FunctionUnit{
        public:
           FunctionUnit(ManagedFnPtr f, CompilePass* p)
            : function(f), lateContext(f->getName(), p), pass(p)  {}

           llvm::Value* compileInline(std::vector<llvm::Value*>&& args, CompilePass::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
           LateContext lateContext;

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

        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;

            FunctionUnit* findFunctionUnit(const std::string f);
        };

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

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

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

    ContextQuery* queryContext;
};

}

#endif // COMPILEPASS_H
