//===-- LVSupport.h ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines support functions.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSUPPORT_H
#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSUPPORT_H

#include "llvm/ADT/Twine.h"
#include "llvm/DebugInfo/LogicalView/Core/LVStringPool.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <bitset>
#include <cctype>
#include <map>
#include <sstream>
#include <type_traits>

namespace llvm {
namespace logicalview {

// Returns the unique string pool instance.
LLVM_ABI LVStringPool &getStringPool();

using LVStringRefs = std::vector<StringRef>;
using LVLexicalComponent = std::tuple<StringRef, StringRef>;
using LVLexicalIndex =
    std::tuple<LVStringRefs::size_type, LVStringRefs::size_type>;

// Used to record specific characteristics about the objects.
template <typename T> class LVProperties {
  static constexpr unsigned N_PROPS = static_cast<unsigned>(T::LastEntry);
  // Use uint32_t as the underlying type if the `T` enum has at most 32
  // enumerators; otherwise, fallback to the generic `std::bitset` case.
  std::conditional_t<(N_PROPS > 32), std::bitset<N_PROPS>, uint32_t> Bits{};

public:
  LVProperties() = default;

  void set(T Idx) {
    if constexpr (std::is_same_v<decltype(Bits), uint32_t>)
      Bits |= 1 << static_cast<unsigned>(Idx);
    else
      Bits.set(static_cast<unsigned>(Idx));
  }
  void reset(T Idx) {
    if constexpr (std::is_same_v<decltype(Bits), uint32_t>)
      Bits &= ~(1 << static_cast<unsigned>(Idx));
    else
      Bits.reset(static_cast<unsigned>(Idx));
  }
  bool get(T Idx) const {
    if constexpr (std::is_same_v<decltype(Bits), uint32_t>)
      return Bits & (1 << static_cast<unsigned>(Idx));
    else
      return Bits[static_cast<unsigned>(Idx)];
  }
};

// Generate get, set and reset 'bool' functions for LVProperties instances.
// FAMILY: instance name.
// ENUM: enumeration instance.
// FIELD: enumerator instance.
// F1, F2, F3: optional 'set' functions to be called.
#define BOOL_BIT(FAMILY, ENUM, FIELD)                                          \
  bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); }                  \
  void set##FIELD() { FAMILY.set(ENUM::FIELD); }                               \
  void reset##FIELD() { FAMILY.reset(ENUM::FIELD); }

#define BOOL_BIT_1(FAMILY, ENUM, FIELD, F1)                                    \
  bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); }                  \
  void set##FIELD() {                                                          \
    FAMILY.set(ENUM::FIELD);                                                   \
    set##F1();                                                                 \
  }                                                                            \
  void reset##FIELD() { FAMILY.reset(ENUM::FIELD); }

#define BOOL_BIT_2(FAMILY, ENUM, FIELD, F1, F2)                                \
  bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); }                  \
  void set##FIELD() {                                                          \
    FAMILY.set(ENUM::FIELD);                                                   \
    set##F1();                                                                 \
    set##F2();                                                                 \
  }                                                                            \
  void reset##FIELD() { FAMILY.reset(ENUM::FIELD); }

#define BOOL_BIT_3(FAMILY, ENUM, FIELD, F1, F2, F3)                            \
  bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); }                  \
  void set##FIELD() {                                                          \
    FAMILY.set(ENUM::FIELD);                                                   \
    set##F1();                                                                 \
    set##F2();                                                                 \
    set##F3();                                                                 \
  }                                                                            \
  void reset##FIELD() { FAMILY.reset(ENUM::FIELD); }

// Generate get, set and reset functions for 'properties'.
#define PROPERTY(ENUM, FIELD) BOOL_BIT(Properties, ENUM, FIELD)
#define PROPERTY_1(ENUM, FIELD, F1) BOOL_BIT_1(Properties, ENUM, FIELD, F1)
#define PROPERTY_2(ENUM, FIELD, F1, F2)                                        \
  BOOL_BIT_2(Properties, ENUM, FIELD, F1, F2)
#define PROPERTY_3(ENUM, FIELD, F1, F2, F3)                                    \
  BOOL_BIT_3(Properties, ENUM, FIELD, F1, F2, F3)

// Generate get, set and reset functions for 'kinds'.
#define KIND(ENUM, FIELD) BOOL_BIT(Kinds, ENUM, FIELD)
#define KIND_1(ENUM, FIELD, F1) BOOL_BIT_1(Kinds, ENUM, FIELD, F1)
#define KIND_2(ENUM, FIELD, F1, F2) BOOL_BIT_2(Kinds, ENUM, FIELD, F1, F2)
#define KIND_3(ENUM, FIELD, F1, F2, F3)                                        \
  BOOL_BIT_3(Kinds, ENUM, FIELD, F1, F2, F3)

const int HEX_WIDTH = 12;
inline FormattedNumber hexValue(uint64_t N, unsigned Width = HEX_WIDTH,
                                bool Upper = false) {
  return format_hex(N, Width, Upper);
}

// Output the hexadecimal representation of 'Value' using '[0x%08x]' format.
inline std::string hexString(uint64_t Value, size_t Width = HEX_WIDTH) {
  std::string String;
  raw_string_ostream Stream(String);
  Stream << hexValue(Value, Width, false);
  return String;
}

// Get a hexadecimal string representation for the given value.
inline std::string hexSquareString(uint64_t Value) {
  return (Twine("[") + Twine(hexString(Value)) + Twine("]")).str();
}

// Return a string with the First and Others separated by spaces.
template <typename... Args>
std::string formatAttributes(const StringRef First, Args... Others) {
  const auto List = {First, Others...};
  std::stringstream Stream;
  size_t Size = 0;
  for (const StringRef &Item : List) {
    Stream << (Size ? " " : "") << Item.str();
    Size = Item.size();
  }
  Stream << (Size ? " " : "");
  return Stream.str();
}

// Add an item to a map with second being a small vector.
template <typename MapType, typename KeyType, typename ValueType>
void addItem(MapType *Map, KeyType Key, ValueType Value) {
  (*Map)[Key].push_back(Value);
}

// Double map data structure.
template <typename FirstKeyType, typename SecondKeyType, typename ValueType>
class LVDoubleMap {
  static_assert(std::is_pointer<ValueType>::value,
                "ValueType must be a pointer.");
  using LVSecondMapType = std::map<SecondKeyType, ValueType>;
  using LVFirstMapType =
      std::map<FirstKeyType, std::unique_ptr<LVSecondMapType>>;
  using LVAuxMapType = std::map<SecondKeyType, FirstKeyType>;
  using LVValueTypes = std::vector<ValueType>;
  LVFirstMapType FirstMap;
  LVAuxMapType AuxMap;

public:
  void add(FirstKeyType FirstKey, SecondKeyType SecondKey, ValueType Value) {
    typename LVFirstMapType::iterator FirstIter = FirstMap.find(FirstKey);
    if (FirstIter == FirstMap.end()) {
      auto SecondMapSP = std::make_unique<LVSecondMapType>();
      SecondMapSP->emplace(SecondKey, Value);
      FirstMap.emplace(FirstKey, std::move(SecondMapSP));
    } else {
      LVSecondMapType *SecondMap = FirstIter->second.get();
      if (SecondMap->find(SecondKey) == SecondMap->end())
        SecondMap->emplace(SecondKey, Value);
    }

    typename LVAuxMapType::iterator AuxIter = AuxMap.find(SecondKey);
    if (AuxIter == AuxMap.end()) {
      AuxMap.emplace(SecondKey, FirstKey);
    }
  }

  LVSecondMapType *findMap(FirstKeyType FirstKey) const {
    typename LVFirstMapType::const_iterator FirstIter = FirstMap.find(FirstKey);
    if (FirstIter == FirstMap.end())
      return nullptr;

    return FirstIter->second.get();
  }

  ValueType find(FirstKeyType FirstKey, SecondKeyType SecondKey) const {
    LVSecondMapType *SecondMap = findMap(FirstKey);
    if (!SecondMap)
      return nullptr;

    typename LVSecondMapType::const_iterator SecondIter =
        SecondMap->find(SecondKey);
    return (SecondIter != SecondMap->end()) ? SecondIter->second : nullptr;
  }

  ValueType find(SecondKeyType SecondKey) const {
    typename LVAuxMapType::const_iterator AuxIter = AuxMap.find(SecondKey);
    if (AuxIter == AuxMap.end())
      return nullptr;
    return find(AuxIter->second, SecondKey);
  }

  // Return a vector with all the 'ValueType' values.
  LVValueTypes find() const {
    LVValueTypes Values;
    if (FirstMap.empty())
      return Values;
    for (typename LVFirstMapType::const_reference FirstEntry : FirstMap) {
      LVSecondMapType &SecondMap = *FirstEntry.second;
      for (typename LVSecondMapType::const_reference SecondEntry : SecondMap)
        Values.push_back(SecondEntry.second);
    }
    return Values;
  }
};

// Unified and flattened pathnames.
LLVM_ABI std::string transformPath(StringRef Path);
LLVM_ABI std::string flattenedFilePath(StringRef Path);

inline std::string formattedKind(StringRef Kind) {
  return (Twine("{") + Twine(Kind) + Twine("}")).str();
}

inline std::string formattedName(StringRef Name) {
  return (Twine("'") + Twine(Name) + Twine("'")).str();
}

inline std::string formattedNames(StringRef Name1, StringRef Name2) {
  return (Twine("'") + Twine(Name1) + Twine(Name2) + Twine("'")).str();
}

// The given string represents a symbol or type name with optional enclosing
// scopes, such as: name, name<..>, scope::name, scope::..::name, etc.
// The string can have multiple references to template instantiations.
// It returns the inner most component.
LLVM_ABI LVLexicalComponent getInnerComponent(StringRef Name);
LLVM_ABI LVStringRefs getAllLexicalComponents(StringRef Name);
LLVM_ABI std::string getScopedName(const LVStringRefs &Components,
                                   StringRef BaseName = {});

// These are the values assigned to the debug location record IDs.
// See DebugInfo/CodeView/CodeViewSymbols.def.
// S_DEFRANGE                               0x113f
// S_DEFRANGE_SUBFIELD                      0x1140
// S_DEFRANGE_REGISTER                      0x1141
// S_DEFRANGE_FRAMEPOINTER_REL              0x1142
// S_DEFRANGE_SUBFIELD_REGISTER             0x1143
// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE   0x1144
// S_DEFRANGE_REGISTER_REL                  0x1145
// When recording CodeView debug location, the above values are truncated
// to a uint8_t value in order to fit the 'OpCode' used for the logical
// debug location operations.
// Return the original CodeView enum value.
inline uint16_t getCodeViewOperationCode(uint8_t Code) { return 0x1100 | Code; }

} // end namespace logicalview
} // end namespace llvm

#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSUPPORT_H
