/*
 * Copyright (C) 2005, 2006, 2009 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "third_party/blink/renderer/core/dom/qualified_name.h"

#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/mathml_names.h"
#include "third_party/blink/renderer/core/svg_names.h"
#include "third_party/blink/renderer/core/xlink_names.h"
#include "third_party/blink/renderer/core/xml_names.h"
#include "third_party/blink/renderer/core/xmlns_names.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "third_party/blink/renderer/platform/wtf/static_constructors.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"

namespace blink {

struct SameSizeAsQualifiedNameImpl
    : public RefCounted<SameSizeAsQualifiedNameImpl> {
  unsigned bitfield;
  void* pointers[4];
};

ASSERT_SIZE(QualifiedNameImpl, SameSizeAsQualifiedNameImpl);

using QualifiedNameCache = HashSet<QualifiedNameImpl*>;

static QualifiedNameCache& GetQualifiedNameCache() {
  // This code is lockless and thus assumes it all runs on one thread!
  DCHECK(IsMainThread());
  static QualifiedNameCache* g_name_cache = new QualifiedNameCache;
  return *g_name_cache;
}

struct QNameComponentsTranslator {
  static unsigned GetHash(const QualifiedNameData& data) {
    return HashComponents(data.components_);
  }
  static bool Equal(QualifiedNameImpl* name, const QualifiedNameData& data) {
    return data.components_.prefix_ == name->prefix_.Impl() &&
           data.components_.local_name_ == name->local_name_.Impl() &&
           data.components_.namespace_ == name->namespace_.Impl();
  }
  static void Store(QualifiedNameImpl*& location,
                    const QualifiedNameData& data,
                    unsigned) {
    const QualifiedNameComponents& components = data.components_;
    auto name =
        QualifiedNameImpl::Create(components.prefix_, components.local_name_,
                                  components.namespace_, data.is_static_);
    name->AddRef();
    location = name.get();
  }
};

QualifiedName::QualifiedName(const AtomicString& p,
                             const AtomicString& l,
                             const AtomicString& n) {
  QualifiedNameData data = {
      {p.Impl(), l.Impl(), n.empty() ? g_null_atom.Impl() : n.Impl()}, false};
  QualifiedNameCache::AddResult add_result =
      GetQualifiedNameCache().AddWithTranslator<QNameComponentsTranslator>(
          data);
  impl_ = *add_result.stored_value;
  if (add_result.is_new_entry)
    impl_->Release();
}

QualifiedName::QualifiedName(const AtomicString& local_name)
    : QualifiedName(g_null_atom, local_name, g_null_atom) {}

QualifiedName::QualifiedName(const AtomicString& p,
                             const AtomicString& l,
                             const AtomicString& n,
                             bool is_static) {
  QualifiedNameData data = {{p.Impl(), l.Impl(), n.Impl()}, is_static};
  QualifiedNameCache::AddResult add_result =
      GetQualifiedNameCache().AddWithTranslator<QNameComponentsTranslator>(
          data);
  impl_ = *add_result.stored_value;
  if (add_result.is_new_entry)
    impl_->Release();
}

QualifiedName::~QualifiedName() = default;

QualifiedNameImpl::~QualifiedNameImpl() {
  GetQualifiedNameCache().erase(this);
}

String QualifiedName::ToString() const {
  String local = LocalName();
  if (HasPrefix())
    return StrCat({Prefix().GetString(), ":", local});
  return local;
}

// Global init routines
DEFINE_GLOBAL(, QualifiedName, g_any_name);
DEFINE_GLOBAL(, QualifiedName, g_null_name);

void QualifiedName::InitAndReserveCapacityForSize(wtf_size_t size) {
  DCHECK(g_star_atom.Impl());
  GetQualifiedNameCache().ReserveCapacityForSize(
      size + 2 /*g_star_atom and g_null_atom */);
  new ((void*)&g_any_name)
      QualifiedName(g_null_atom, g_null_atom, g_star_atom, true);
  new ((void*)&g_null_name)
      QualifiedName(g_null_atom, g_null_atom, g_null_atom, true);
}

const AtomicString& QualifiedName::LocalNameUpperSlow() const {
  impl_->local_name_upper_ = impl_->local_name_.ToAsciiUpper();
  return impl_->local_name_upper_;
}

unsigned QualifiedNameImpl::ComputeHash() const {
  QualifiedNameComponents components = {prefix_.Impl(), local_name_.Impl(),
                                        namespace_.Impl()};
  return HashComponents(components);
}

void QualifiedName::CreateStatic(void* target_address,
                                 StringImpl* name,
                                 const AtomicString& name_namespace) {
  new (target_address)
      QualifiedName(g_null_atom, AtomicString(name), name_namespace, true);
}

void QualifiedName::CreateStatic(void* target_address, StringImpl* name) {
  new (target_address)
      QualifiedName(g_null_atom, AtomicString(name), g_null_atom, true);
}

void QualifiedNameWithHash::CreateStatic(void* target_address,
                                         StringImpl* name,
                                         const AtomicString& name_namespace) {
  new (target_address) QualifiedNameWithHash(g_null_atom, AtomicString(name),
                                             name_namespace, true);
}

void QualifiedNameWithHash::CreateStatic(void* target_address,
                                         StringImpl* name) {
  new (target_address)
      QualifiedNameWithHash(g_null_atom, AtomicString(name), g_null_atom, true);
}

std::ostream& operator<<(std::ostream& ostream, const QualifiedName& qname) {
  ostream << "QualifiedName(local=" << qname.LocalName()
          << " ns=" << qname.NamespaceURI() << " prefix=" << qname.Prefix()
          << ")";
  return ostream;
}

QualifiedNameWithHash::QualifiedNameWithHash(const AtomicString& prefix,
                                             const AtomicString& local_name,
                                             const AtomicString& namespace_uri,
                                             bool is_static)
    : QualifiedName(prefix, local_name, namespace_uri, is_static),
      bloom_filter(Element::FilterForAttribute(*this)) {}

}  // namespace blink
