#ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
#define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/YAMLTraits.h"
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace llvm {
class raw_ostream;
namespace dsymutil {
class DebugMapObject;
class DebugMap {
  Triple BinaryTriple;
  std::string BinaryPath;
  std::vector<uint8_t> BinaryUUID;
  using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>;
  ObjectContainer Objects;
      friend yaml::MappingTraits<std::unique_ptr<DebugMap>>;
  friend yaml::MappingTraits<DebugMap>;
  DebugMap() = default;
  
public:
  DebugMap(const Triple &BinaryTriple, StringRef BinaryPath,
           ArrayRef<uint8_t> BinaryUUID = ArrayRef<uint8_t>())
      : BinaryTriple(BinaryTriple), BinaryPath(std::string(BinaryPath)),
        BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {}
  using const_iterator = ObjectContainer::const_iterator;
  iterator_range<const_iterator> objects() const {
    return make_range(begin(), end());
  }
  const_iterator begin() const { return Objects.begin(); }
  const_iterator end() const { return Objects.end(); }
  unsigned getNumberOfObjects() const { return Objects.size(); }
      DebugMapObject &
  addDebugMapObject(StringRef ObjectFilePath,
                    sys::TimePoint<std::chrono::seconds> Timestamp,
                    uint8_t Type = llvm::MachO::N_OSO);
  const Triple &getTriple() const { return BinaryTriple; }
  ArrayRef<uint8_t> getUUID() const { return ArrayRef<uint8_t>(BinaryUUID); }
  StringRef getBinaryPath() const { return BinaryPath; }
  void print(raw_ostream &OS) const;
#ifndef NDEBUG
  void dump() const;
#endif
    static ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
  parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose);
};
class DebugMapObject {
public:
  struct SymbolMapping {
    Optional<yaml::Hex64> ObjectAddress;
    yaml::Hex64 BinaryAddress;
    yaml::Hex32 Size;
    SymbolMapping(Optional<uint64_t> ObjectAddr, uint64_t BinaryAddress,
                  uint32_t Size)
        : BinaryAddress(BinaryAddress), Size(Size) {
      if (ObjectAddr)
        ObjectAddress = *ObjectAddr;
    }
        SymbolMapping() = default;
  };
  using YAMLSymbolMapping = std::pair<std::string, SymbolMapping>;
  using DebugMapEntry = StringMapEntry<SymbolMapping>;
        bool addSymbol(StringRef SymName, Optional<uint64_t> ObjectAddress,
                 uint64_t LinkedAddress, uint32_t Size);
      const DebugMapEntry *lookupSymbol(StringRef SymbolName) const;
      const DebugMapEntry *lookupObjectAddress(uint64_t Address) const;
  StringRef getObjectFilename() const { return Filename; }
  sys::TimePoint<std::chrono::seconds> getTimestamp() const {
    return Timestamp;
  }
  uint8_t getType() const { return Type; }
  iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const {
    return make_range(Symbols.begin(), Symbols.end());
  }
  bool empty() const { return Symbols.empty(); }
  void addWarning(StringRef Warning) {
    Warnings.push_back(std::string(Warning));
  }
  const std::vector<std::string> &getWarnings() const { return Warnings; }
  void print(raw_ostream &OS) const;
#ifndef NDEBUG
  void dump() const;
#endif
private:
  friend class DebugMap;
    DebugMapObject(StringRef ObjectFilename,
                 sys::TimePoint<std::chrono::seconds> Timestamp, uint8_t Type);
  std::string Filename;
  sys::TimePoint<std::chrono::seconds> Timestamp;
  StringMap<SymbolMapping> Symbols;
  DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
  uint8_t Type;
  std::vector<std::string> Warnings;
      friend yaml::MappingTraits<dsymutil::DebugMapObject>;
  friend yaml::SequenceTraits<std::vector<std::unique_ptr<DebugMapObject>>>;
  DebugMapObject() = default;
public:
  DebugMapObject(DebugMapObject &&) = default;
  DebugMapObject &operator=(DebugMapObject &&) = default;
  };
} } 
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping)
namespace llvm {
namespace yaml {
using namespace llvm::dsymutil;
template <>
struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> {
  static void mapping(IO &io,
                      std::pair<std::string, DebugMapObject::SymbolMapping> &s);
  static const bool flow = true;
};
template <> struct MappingTraits<dsymutil::DebugMapObject> {
  struct YamlDMO;
  static void mapping(IO &io, dsymutil::DebugMapObject &DMO);
};
template <> struct ScalarTraits<Triple> {
  static void output(const Triple &val, void *, raw_ostream &out);
  static StringRef input(StringRef scalar, void *, Triple &value);
  static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
};
template <>
struct SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> {
  static size_t
  size(IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq);
  static dsymutil::DebugMapObject &
  element(IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq,
          size_t index);
};
template <> struct MappingTraits<dsymutil::DebugMap> {
  static void mapping(IO &io, dsymutil::DebugMap &DM);
};
template <> struct MappingTraits<std::unique_ptr<dsymutil::DebugMap>> {
  static void mapping(IO &io, std::unique_ptr<dsymutil::DebugMap> &DM);
};
} } 
#endif