/*
 * SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef NVIDIA_COMMON_TYPE_NAME_HPP_
#define NVIDIA_COMMON_TYPE_NAME_HPP_

#include <cstdint>

#if defined(__clang__)
inline char* TypenameAsStringImpl(const char* begin, char* output, int32_t max_length) {
  // Return nullptr since this is not implemented
  return nullptr;
}
#elif defined(__GNUC__)
#include "type_name_gnuc.hpp"
#elif defined(_MSC_VER)
  #include<string_view>
  #define __PRETTY_FUNCTION__ __FUNCSIG__
#endif

namespace nvidia {
namespace helper {
/**
 * Returns the pretty function name. It includes the complete function name along with the namespace
 * for template parameters.
 * Compiler using gcc-13 or later strips the namespace of the template parameter if
 * PRETTY_FUNCTION is called in the same namespace as that of the namespace of template parameter.
 * Compiler does not strip the namespace if the function that calls the PRETTY_FUNCTION macro is
 * called in a different namespace.
 *
 * @return the pretty function name as a const char*
 */
  template <typename>
  const char* PrettyFunctionName() {
    return __PRETTY_FUNCTION__;
  }

/**
 * Returns the sizeof the pretty function name.
 */
  template <typename>
  constexpr int32_t PrettyFunctionSize() {
    return sizeof(__PRETTY_FUNCTION__);
  }
}  // namespace helper
}  // namespace nvidia

namespace nvidia {
// Gives a string representation of the name of a C++ type.
//
// The function will compute the typename during the first invocation and store it in heap memory.
// Computation of the typename is linear in the length of the typename and does not involve dynamic
// memory allocations.
//
// For example:
//   namespace foo {
//     struct Bar {};
//   }  // namespace
//   TyenameAsString<foo::Bar>();  // returns "foo::Bar"
//
// Note: Only "simple" class types in global namespace or a "normal" namespaces are guaranteed to
// work. Templates, lambdas and anonymous namespaces are not guaranteed to work as expected.
#ifdef _MSC_VER
constexpr bool starts_with(std::string_view str, std::string_view prefix) {
  return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix;
}
#endif


template <typename T>
const char* TypenameAsString() {
#ifdef _MSC_VER
  constexpr std::string_view func_sig = __FUNCSIG__;  // MSVC-specific
  constexpr std::string_view prefix = "const char *__cdecl nvidia::TypenameAsString<";
  constexpr std::string_view suffix = ">(void)";

  constexpr size_t start = func_sig.find(prefix) + prefix.size();
  constexpr size_t end = func_sig.rfind(suffix);
  constexpr size_t raw_len = end - start;
  constexpr std::string_view raw_type = func_sig.substr(start, raw_len);

  constexpr std::string_view class_prefix = "class ";
  constexpr std::string_view struct_prefix = "struct ";
  constexpr std::string_view typename_prefix = "typename ";

  constexpr std::string_view cleaned_type =
      starts_with(raw_type, class_prefix)      ? raw_type.substr(class_prefix.size())
      : starts_with(raw_type, struct_prefix)   ? raw_type.substr(struct_prefix.size())
      : starts_with(raw_type, typename_prefix) ? raw_type.substr(typename_prefix.size())
                                               : raw_type;

  static char type_name[cleaned_type.size() + 1] = {};
  for (size_t i = 0; i < cleaned_type.size(); ++i) { type_name[i] = cleaned_type[i]; }
  type_name[cleaned_type.size()] = '\0';

  return type_name;
#else
  // Ideally the typename string would be computed at compile time, however this does not seem to
  // be possible. Thus the typename string is computed the first time this function is evaluated
  // and cached for future function evaluations.
  const char* template_name = nvidia::helper::PrettyFunctionName<T>();
  // Use upper bound to be safe.
  constexpr int32_t kMaxNameLength = nvidia::helper::PrettyFunctionSize<T>();
  static char s_name[kMaxNameLength] = {0};  // Initialize with 0 to get a null-terminated string.
  static char* result = s_name;
  if (s_name[0] == 0 && result != nullptr) {  // Check for first invocation of this function.
    result = TypenameAsStringImpl(template_name, s_name, kMaxNameLength);
  }
  return result;
#endif
}

}  // namespace nvidia

#endif  // NVIDIA_COMMON_TYPE_NAME_HPP_
