# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Codegen common to .h files."""

import common
import java_types


def class_accessors(sb,
                    java_classes,
                    *,
                    is_muxing=False,
                    use_weak_called_by_natives=False):
  for java_class in java_classes:
    escaped_name = java_class.class_without_prefix.to_cpp()
    # #ifdef needed when multiple .h files include shared common classes.
    sb(f"""\
#ifndef {escaped_name}_clazz_defined
#define {escaped_name}_clazz_defined
""")
    if is_muxing and not use_weak_called_by_natives:
      sb(f"""\
namespace jni_zero::internal {{
extern const uint16_t kClassIdx_{escaped_name};
}}  // namespace jni_zero::internal
JNI_ZERO_ALWAYS_INLINE inline jclass {escaped_name}_clazz(JNIEnv* env) {{
  return jni_zero::internal::LazyGetClassMuxed(
      env, ::jni_zero::internal::kClassIdx_{escaped_name});
}}
#endif

""")
    else:
      weak_attr = ('[[gnu::weak]] ' if use_weak_called_by_natives else '')
      sb(f"""\
{weak_attr}inline jclass {escaped_name}_clazz(JNIEnv* env) {{
  static const char kClassName[] = "{java_class.full_name}";
  static std::atomic<jclass> cached_class;
  return jni_zero::internal::LazyGetClass(env, kClassName, &cached_class);
}}
#endif

""")


def class_accessor_expression(java_class):
  # Keep in sync with java_types.JCLASS_GLOBALS_CLASSES.
  if java_class == java_types.CLASS_LOADER_CLASS:
    return 'jni_zero::g_class_loader_class'
  if java_class == java_types.OBJECT_CLASS:
    return 'jni_zero::g_object_class'
  if java_class == java_types.STRING_CLASS:
    return 'jni_zero::g_string_class'

  return f'{java_class.class_without_prefix.to_cpp()}_clazz(env)'


def header_preamble(script_name,
                    java_class=None,
                    system_includes=None,
                    user_includes=None,
                    header_guard=None,
                    is_shared_header=False):
  if header_guard is None and java_class is not None:
    if is_shared_header:
      header_guard = f'{java_class.to_cpp()}_SHARED_JNI'
    else:
      header_guard = f'{java_class.to_cpp()}_JNI'
  sb = []
  sb.append(f"""\
// This file was generated by
//     {script_name}
""")
  if java_class is not None:
    sb.append(f"""\
// For
//     {java_class.full_name_with_dots}

""")
  if header_guard:
    sb.append(f"""\
#ifndef {header_guard}
#define {header_guard}

""")
  if system_includes:
    sb.extend(f'#include <{x}>\n' for x in system_includes)
    sb.append('\n')
  if user_includes:
    sb.extend(f'#include "{x}"\n' for x in user_includes)
    sb.append('\n')

  if not is_shared_header:
    sb.append("""\
// Field, class, and method accessors use inline functions with static
// variables for caching.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunique-object-duplication"
""")
  preamble = ''.join(sb)

  sb = []
  if not is_shared_header:
    sb.append('#pragma clang diagnostic pop\n')
  if header_guard:
    sb.append(f'#endif  // {header_guard}\n')
  epilogue = ''.join(sb)

  return preamble, epilogue
