#ifndef LLVM_TRANSFORMS_SCALAR_LOOPCHAIN_PRE_NEW_UTILS_H
#define LLVM_TRANSFORMS_SCALAR_LOOPCHAIN_PRE_NEW_UTILS_H

#include "llvm/IR/PassManager.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/ADT/SmallSet.h"

#define PRE_LOOPCHAIN_PASS_PREFIX "loopchain_"

namespace llvm {

enum ModesForVisitor {Storage, Replacing, Remove};
class PreLoopChainNewSimpleUtils{
  public:
    //Replace the function called in the instruction with the cloned Function
    static bool replaceWithClonedFunctionCall(Instruction *OrigCallInst, Function *clonedFunction, CallInst* &NewCI);
    static bool replaceWithClonedFunctionCall(Instruction *OrigCallInst, Function *clonedFunction);
    static Function *getCalledFunction(CallInst *CI);

};

class myNewVisitor : public InstVisitor<myNewVisitor>{

    ModesForVisitor mode;
  public:
    Module *M;
    SmallVector<CallInst*> listOfInstFunCalls;
    
    myNewVisitor(Module *m, ModesForVisitor mode = Replacing) : M(m), mode(mode) {};
    //bool containOutlinedRoutine = false;
   
    
    void visitCallInst(CallInst &CI);

    void visit_replace(CallInst &CI);
    void visit_store(CallInst &CI);
    void visit_remove(CallInst &CI);
};
class PreLoopChainNewUtils{
  private:
    DominatorTree *DT = nullptr;
    Function *F = nullptr;
    LoopInfo *LI = nullptr;

  public :
    void init(Function *F_, LoopInfo *LI_, DominatorTree *DT_){ DT = DT_; F = F_; LI = LI_; }
    
    //Only for leapfrog hack
    ScalarEvolution *SE = nullptr;
    bool justLeapfrog = false;
    
    //Create a candidate for LC from loop body that is
    //cloned, outline guarded and all called functions are pointing to cloned ones
    Function* createCandidateForLCFromLoopBody(Loop *L);

    //Move conditinal branch in the loopheader to the loop body.
    //Required for outlineLoopBody
    BasicBlock* SplitLoopHeaderIfReq(Loop *L);
    //Outline loop body of L to new function
    Function* outlineLoopBody(Loop * L);
    //Creates and returns new BB that branches to BB_orig and BB_cloned bases on aocc_lcrand intrinsic 
    BasicBlock* createGuardingBranchWithIntrinsic(BasicBlock *BB_orig, BasicBlock *BB_cloned, const Twine &BBName = "BB_new_branch");
    
    //Only for leapfrog hack
    BasicBlock* createGuardingBranchWithTS(BasicBlock *BB_orig, BasicBlock *BB_cloned, Instruction *I, const Twine &BBName = "BB_new_branch");
    
    //Helpers for debuging

    //Check whether i-th element of candidates dominates all j-th entries for j > i
    bool dominates(const int &i, SmallVector<BasicBlock*> candidates);

    void replaceAllFunctionCalls(Function *Func, SmallSet<CallInst*, 32> &visited);
    //Return the number of the BB or Loop if getName().empty()
    StringRef getSimpleName(const BasicBlock *BB);
    StringRef getSimpleName(const Loop *L);
    StringRef getSimpleName(const Instruction *I);
};

} // namespace llvm


#endif // LLVM_TRANSFORMS_SCALAR_LOOPCHAIN_PRE_NEW_UTILS_H