{% from "templates/macros.tmpl" import license, source_files_for_generated_file %}
{{ license() }}

{{source_files_for_generated_file(template_file, input_files)}}

#include "{{this_include_path}}"

#include "base/containers/span.h"
#include "third_party/blink/renderer/platform/wtf/static_constructors.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hasher.h"

namespace blink {
namespace {{cpp_namespace}} {

DEFINE_GLOBAL(, AtomicString, {{namespace_prefix}}NamespaceURI);

{% if tags %}
// Tags

void* tag_storage[kTagsCount * ((sizeof({{namespace}}QualifiedName) + sizeof(void *) - 1) / sizeof(void *))];
{% for tag in tags|sort(attribute='name', case_sensitive=True) %}
const {{namespace}}QualifiedName& {{tag|symbol}}Tag = reinterpret_cast<{{namespace}}QualifiedName*>(&tag_storage)[{{loop.index0}}];
{% endfor %}


base::HeapArray<const QualifiedName*> GetTags() {
  auto tags = base::HeapArray<const QualifiedName*>::Uninit(kTagsCount);
  for (size_t i = 0; i < kTagsCount; ++i) {
    tags[i] = reinterpret_cast<QualifiedNameWithHash*>(&tag_storage) + i;
  }
  return tags;
}

{% endif %}
// Attributes

void* attr_storage[kAttrsCount * ((sizeof(QualifiedNameWithHash) + sizeof(void *) - 1) / sizeof(void *))];

{% for attr in attrs|sort(attribute='name', case_sensitive=True) %}
const QualifiedNameWithHash& {{attr|symbol}}Attr = reinterpret_cast<QualifiedNameWithHash*>(&attr_storage)[{{loop.index0}}];
{% endfor %}

base::HeapArray<const QualifiedName*> GetAttrs() {
  auto attrs = base::HeapArray<const QualifiedName*>::Uninit(kAttrsCount);
  for (size_t i = 0; i < kAttrsCount; ++i) {
    attrs[i] = reinterpret_cast<QualifiedNameWithHash*>(&attr_storage) + i;
  }
  return attrs;
}


void Init() {
  struct NameEntry {
    const char* name;
    unsigned char length;
    unsigned char is_tag;
    unsigned char is_attr;
  };

  // Namespace
  // Use placement new to initialize the globals.
  new ((void*)&{{namespace_prefix}}NamespaceURI) AtomicString("{{namespace_uri}}");

  {% set tagnames = tags|map(attribute='name')|list() %}
  {% set attrnames = attrs|map(attribute='name')|list() %}
  static constexpr NameEntry kNames[] = {
  {% for name, tag_list in (tags + attrs)|groupby('name')|sort(attribute=0, case_sensitive=True) %}
    { "{{name}}", {{name.original|length}}, {{ (name in tagnames)|int }}, {{ (name in attrnames)|int }} },
  {% endfor %}
  };

  {% if tags %}
  size_t tag_i = 0;
  {% endif %}
  size_t attr_i = 0;
  for (const NameEntry& name : kNames) {
    StringImpl* impl = StringImpl::CreateStatic(base::span(name.name, name.length));

    // We need a static version of the uppercase string, since the QualifiedNameWithHash
    // constructor will call LocalNameUpper(), and we cannot insert non-static strings
    // before we are done inserting static strings.
    char upper_buf[256];
    for (unsigned i = 0; i < name.length; ++i) {
      CHECK_LT(i, sizeof(upper_buf));
      upper_buf[i] = ToAsciiUpper(name.name[i]);
    }
    AtomicString upper(StringImpl::CreateStatic(base::span(upper_buf, name.length)));

    {% if tags %}
    if (name.is_tag) {
      void* address = reinterpret_cast<{{namespace}}QualifiedName*>(&tag_storage) + tag_i;
      QualifiedNameWithHash::CreateStatic(address, impl, {{namespace_prefix}}NamespaceURI);
      ++tag_i;
    }

    if (!name.is_attr)
      continue;
    {% endif %}
    void* address = reinterpret_cast<QualifiedNameWithHash*>(&attr_storage) + attr_i;
    {% if use_namespace_for_attrs %}
    QualifiedNameWithHash::CreateStatic(address, impl, {{namespace_prefix}}NamespaceURI);
    {% else %}
    QualifiedNameWithHash::CreateStatic(address, impl);
    {% endif %}
    ++attr_i;
  }
  {% if tags %}
  DCHECK_EQ(tag_i, kTagsCount);
  {% endif %}
  DCHECK_EQ(attr_i, kAttrsCount);
}

{% if generate_tag_enum %}
const blink::{{namespace}}QualifiedName& TagToQualifiedName(
    {{namespace}}Tag tag) {
  switch (tag) {
{% for tag in tags|sort(attribute='name', case_sensitive=True) %}
    case {{namespace}}Tag::{{tag|tag_symbol}}:
      return {{tag|symbol}}Tag;
{% endfor %}
    case {{namespace}}Tag::kUnknown:
      return static_cast<const blink::{{namespace}}QualifiedName&>(g_null_name);
  }
}
{% endif %}

}  // namespace {{cpp_namespace}}
}  // namespace blink
