#ifndef THIRD_PARTY_UPB_WASM_ABI_HANDLE_CONSTANTS_H_
#define THIRD_PARTY_UPB_WASM_ABI_HANDLE_CONSTANTS_H_

#include <array>
#include <cstdint>
#include <optional>

#include "third_party/absl/log/absl_check.h"
#include "third_party/absl/strings/string_view.h"
#include "upb/base/string_view.h"
#include "upb/mem/arena.h"
#include "upb/message/array.h"
#include "upb/message/message.h"
#include "upb/mini_table/enum.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/message.h"

namespace upb::wasm {

struct EmbeddedMessage {
  upb_Message* msg;
  // Mini-table of the message, required if the message is not null.
  const upb_MiniTable* mt;
};

struct EmbeddedArray {
  upb_Array* arr;
  // Mini-table for array entries. This is required if entries are messages and
  // nullptr otherwise.
  const upb_MiniTable* entry_mt;
};

struct EmbeddedMap {
  upb_Map* map;
  // Mini-table of the map entry, required if the map is not null.
  const upb_MiniTable* entry_mt;
};

// Defines ID assignment for handle representing type `T`.
template <typename T>
constexpr uint32_t IdForHandle();
template <>
constexpr uint32_t IdForHandle<upb_Arena*>() {
  return 1;
}
template <>
constexpr uint32_t IdForHandle<EmbeddedArray>() {
  return 2;
}
template <>
constexpr uint32_t IdForHandle<upb_ExtensionRegistry*>() {
  return 3;
}
template <>
constexpr uint32_t IdForHandle<EmbeddedMap>() {
  return 4;
}
template <>
constexpr uint32_t IdForHandle<EmbeddedMessage>() {
  return 5;
}
template <>
constexpr uint32_t IdForHandle<upb_MiniTable*>() {
  return 6;
}
template <>
constexpr uint32_t IdForHandle<upb_MiniTableField*>() {
  return 7;
}
template <>
constexpr uint32_t IdForHandle<upb_MiniTableEnum*>() {
  return 8;
}
template <>
constexpr uint32_t IdForHandle<upb_StringView>() {
  return 9;
}

// Debug strings for handle representing type indexed by `IdForHandle<T> - 1`.
constexpr std::array<absl::string_view, 10> kDebugStrings = {
    "upb_Arena*",
    "ArrayAndMt",
    "upb_ExtensionRegistry*",
    "MapAndMt",
    "MessageAndMt",
    "upb_MiniTable*",
    "upb_MiniTableField*",
    "upb_MiniTableEnum*",
    "upb_StringView",
    "InvalidHandle"};

// Returns debug string for handle type `T`.
template <typename T>
constexpr absl::string_view DebugStringForHandle() {
  return kDebugStrings[IdForHandle<T>() - 1];
}

// Returns debug string for handle type with matching `id`.
constexpr absl::string_view DebugStringForId(uint32_t id) {
  if (id >= 1u && id <= 9u) {
    return kDebugStrings[id - 1];
  }
  return kDebugStrings[9];
}

// Helper methods for handling different ways of representing null.
template <typename T>
constexpr T NullValue() {
  if constexpr (std::is_same_v<T, upb_StringView>) {
    return upb_StringView{.data = nullptr, .size = 0};
  } else if constexpr (std::is_same_v<T, EmbeddedMessage>) {
    return {.msg = nullptr, .mt = nullptr};
  } else if constexpr (std::is_same_v<T, EmbeddedArray>) {
    return {.arr = nullptr, .entry_mt = nullptr};
  } else if constexpr (std::is_same_v<T, EmbeddedMap>) {
    return {.map = nullptr, .entry_mt = nullptr};
  } else {
    return nullptr;
  }
}

template <typename T>
constexpr bool IsNullValue(const T& value) {
  if constexpr (std::is_same_v<T, upb_StringView>) {
    return value.data == nullptr && value.size == 0;
  } else if constexpr (std::is_same_v<T, EmbeddedMessage>) {
    return value.msg == nullptr;
  } else if constexpr (std::is_same_v<T, EmbeddedArray>) {
    return value.arr == nullptr;
  } else if constexpr (std::is_same_v<T, EmbeddedMap>) {
    return value.map == nullptr;
  } else {
    return value == nullptr;
  }
}

}  // namespace upb::wasm

#endif  // THIRD_PARTY_UPB_WASM_ABI_HANDLE_CONSTANTS_H_
