#ifndef LLVM_TRANSFORMS_SCALAR_LOOPCHAIN_PRE_NEW_H
#define LLVM_TRANSFORMS_SCALAR_LOOPCHAIN_PRE_NEW_H

#include "llvm/Transforms/Scalar/LoopChainPre.h"
#include "llvm/Transforms/Scalar/LoopChainPreNewUtils.h"
#include "llvm/IR/InstVisitor.h"

//Only for leapfrog hack
#include "llvm/Analysis/ScalarEvolution.h"

#define DEBUG_TYPE "pre-loopchain-new"
namespace llvm{

class CallEntry {
public:
  CallInst *cInst;
  int nestingLevel;
  Loop *L;
  int LCSize = -1;

  CallEntry(CallInst *call, int nestingLevel = -1, Loop *L = nullptr): 
  cInst(call), nestingLevel(nestingLevel), L(L) {}
};

class MyVisitor : public InstVisitor<MyVisitor>{
public:
  SmallVector<CallEntry> listOfFunCalls;
  void visitCallInst(CallInst &CI);
};

class ContainOutlinedVisitor : public InstVisitor<ContainOutlinedVisitor>{
  public:
    bool containOutlinedRoutine = false;
    SmallVector<CallEntry> listOfFunCalls;
    
    void visitCallInst(CallInst &CI);
};

class PreLoopChainPassNew : public PassInfoMixin<PreLoopChainPassNew> {
private:
  PreLoopChainPass PreLC;
  bool linkTime;
  SmallVector<CallEntry> listOfFunCalls;
  SmallVector<Loop*> loopsToOutline;
  PreLoopChainNewUtils util;
  //Threshold for getLoopsToOutline routine to avoid outlining undesired loops
  int minimalLCthreshold = 160;
  const int maximalLCthreshold = 185;

//Only for leapfrog hack
  bool justLeapfrog = false;
  //SmallVector<std::pair<BasicBlock*,BasicBlock*>> BBToOutline; ToDo to be implemented
public:
  PreLoopChainPassNew(bool linkTime = false): PreLC(false), linkTime(linkTime) {}
  PreLoopChainPassNew(PreLoopChainOptions PreLCOpts = {}, bool linkTime = false): 
    PreLC(PreLCOpts), linkTime(linkTime) {}

  
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
  PreservedAnalyses runPreLink(Function &F, FunctionAnalysisManager &AM);
  PreservedAnalyses runLink(Function &F, FunctionAnalysisManager &AM);
  
  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
  bool runOnFunction(Function &F, FunctionAnalysisManager &AM);

  bool detectLCandClone(Function &F, LoopInfo &LI);
  void printPipeline(raw_ostream &OS,
                     function_ref<StringRef(StringRef)> MapClassName2PassName){
                      PreLC.printPipeline(OS,MapClassName2PassName); }

  void getListOfFunCalls(Function &F);
  void updateFunCallInfo(Loop *loop, CallEntry &funCall);
  void checkSubLoops(Loop *loop);
  void locateFunCallsInLoops(Function &F, LoopInfo &LI);
  int computeLCSize();
  bool outline(Function &F, LoopInfo &LI);
  //Function* outlineLoopBody(Loop *L, Function &F);
  bool containOutlinedRoutines(Function &F, SmallSet<CallInst*, 32> &visited);
  bool isCalledByOutlinedRoutine(Function &F);
  void getLoopsToOutline(Function &F, LoopInfo &LI);
  void printListOfFunCalls();
  void dumpCallEntry(CallEntry &);
};
} // namespace llvm

#endif // LLVM_TRANSFORMS_SCALAR_LOOPCHAIN_PRE_H