//===-- include/flang/Common/enum-class.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
//
//===----------------------------------------------------------------------===//

// The macro
//   ENUM_CLASS(className, enum1, enum2, ..., enumN)
// defines
//   enum class className { enum1, enum2, ... , enumN };
// as well as the introspective utilities
//   static constexpr std::size_t className_enumSize{N};
//   static inline std::string_view EnumToString(className);

#ifndef FORTRAN_COMMON_ENUM_CLASS_H_
#define FORTRAN_COMMON_ENUM_CLASS_H_

#include <array>
#include <functional>
#include <string_view>
namespace Fortran::common {

constexpr std::size_t CountEnumNames(const char *p) {
  std::size_t n{0};
  std::size_t any{0};
  for (; *p; ++p) {
    if (*p == ',') {
      n += any;
      any = 0;
    } else if (*p != ' ') {
      any = 1;
    }
  }
  return n + any;
}

template <std::size_t ITEMS>
constexpr std::array<std::string_view, ITEMS> EnumNames(const char *p) {
  std::array<std::string_view, ITEMS> result{""};
  std::size_t at{0};
  const char *start{nullptr};
  for (; *p; ++p) {
    if (*p == ',' || *p == ' ') {
      if (start) {
        result[at++] =
            std::string_view{start, static_cast<std::size_t>(p - start)};
        start = nullptr;
      }
    } else if (!start) {
      start = p;
    }
  }
  if (start) {
    result[at] = std::string_view{start, static_cast<std::size_t>(p - start)};
  }
  return result;
}

#define ENUM_CLASS(NAME, ...) \
  enum class NAME { __VA_ARGS__ }; \
  [[maybe_unused]] static constexpr std::size_t NAME##_enumSize{ \
      ::Fortran::common::CountEnumNames(#__VA_ARGS__)}; \
  [[maybe_unused]] static inline std::size_t EnumToInt(NAME e) { \
    return static_cast<std::size_t>(e); \
  } \
  [[maybe_unused]] static inline std::string_view EnumToString(NAME e) { \
    static const constexpr auto names{ \
        ::Fortran::common::EnumNames<NAME##_enumSize>(#__VA_ARGS__)}; \
    return names[static_cast<std::size_t>(e)]; \
  } \
  [[maybe_unused]] inline void ForEach##NAME(std::function<void(NAME)> f) { \
    for (std::size_t i{0}; i < NAME##_enumSize; ++i) { \
      f(static_cast<NAME>(i)); \
    } \
  }
} // namespace Fortran::common
#endif // FORTRAN_COMMON_ENUM_CLASS_H_
