#ifndef LLVM_ANALYSIS_TYPERECONSTRUCTION
#define LLVM_ANALYSIS_TYPERECONSTRUCTION

#include "llvm/Pass.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include <functional>

namespace llvm {

struct RT {
  typedef enum { BASE, FUN, ST, PTR, ARR, ANY } kind;
  kind k;
  Type *bt = nullptr;
  RT *rt = nullptr;
  uint64_t sz;
  std::vector<RT *> st;
  std::string str() const;
  std::string str(std::map<const RT *, int>& m, int& mi) const;
  RT(kind k) :k(k) { }
  RT(Type *bt) :k(BASE), bt(bt) { }
  RT() :k(ANY) { }
  bool isptr() const { return (k == PTR); }
  bool isfun() const { return (k == FUN); }
  bool isany() const { return (k == ANY); }
  bool isst() const { return (k == ST); }
  bool isarr() const { return (k == ARR); }
  bool isbase() const { return (k == BASE); }
  uint64_t arsz() const { return (isarr() ? sz : -1); }
  const RT *ptref() const { return rt; }
  RT *ptref() { return rt; }
  const RT *stfld(int f) const { return (isst() ? st.at(f) : nullptr); }
  const RT *fnret() const { return (isfun() ? st.at(0) : nullptr); }
  const RT *fnarg(int i) const { return (isfun() ? st.at(i + 1) : nullptr); }
  const RT *sub(int i) const { return st.at(i); }
  const Type *bty() const { return (isbase() ? bt : nullptr); }
  int nsub() const { return st.size(); }
  int narg() const { return (isfun() ? (st.size() - 1) : -1); }
  int nfld() const { return (isst() ? st.size() : -1); }
  bool eq(const RT *) const;
  ~RT();
  void detach(const RT *);
};
class RTD;
class CSFunction;
class TypeReconstructionAnalysis
  :public AnalysisInfoMixin<TypeReconstructionAnalysis> {
public:
  TypeReconstructionAnalysis() :OS(dbgs()) { }
  TypeReconstructionAnalysis(raw_ostream& OS) :OS(OS) { }
  ~TypeReconstructionAnalysis();
  static AnalysisKey Key;
  struct Result {
    RTD *A = nullptr;
    RT *ty(Value *);
  };
  Result run(Module& M, ModuleAnalysisManager& AM);
  Result run(Function& F, FunctionAnalysisManager& FAM);
  bool invalidate(Module &, const PreservedAnalyses &PA, ModuleAnalysisManager::Invalidator &);
  bool invalidate(Function &, const PreservedAnalyses &PA, FunctionAnalysisManager::Invalidator &);
private:
  raw_ostream& OS;
  Result R;
  void run(Function& M, CSFunction& );
};

class TypeReconstructionPrinterPass
  :public PassInfoMixin<TypeReconstructionPrinterPass> {
  raw_ostream& OS;
public:
  explicit TypeReconstructionPrinterPass(raw_ostream& OS) :OS(OS) { }
  explicit TypeReconstructionPrinterPass() :OS(dbgs()) { }
  PreservedAnalyses run(Module& M, ModuleAnalysisManager& AM);
};


typedef std::function<TypeReconstructionAnalysis::Result* (void)> TRF;
extern cl::opt<int> DoTypeReconstructionD;
}

#endif
