/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 * Copyright (C) 2004, 2005, 2006, 2007, 2010 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/html/html_map_element.h"

#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/node_lists_node_data.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/html_area_element.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"

namespace blink {

HTMLMapElement::HTMLMapElement(Document& document)
    : HTMLElement(html_names::kMapTag, document) {
  UseCounter::Count(document, WebFeature::kMapElement);
}

HTMLMapElement::~HTMLMapElement() = default;

HTMLAreaElement* HTMLMapElement::AreaForPoint(
    const PhysicalOffset& location,
    const LayoutObject* container_object) {
  HTMLAreaElement* default_area = nullptr;
  for (HTMLAreaElement& area :
       Traversal<HTMLAreaElement>::DescendantsOf(*this)) {
    if (area.IsDefault() && !default_area)
      default_area = &area;
    else if (area.PointInArea(location, container_object))
      return &area;
  }

  return default_area;
}

HTMLImageElement* HTMLMapElement::ImageElement() {
  HTMLCollection* images = GetDocument().images();
  for (unsigned i = 0; Element* curr = images->item(i); ++i) {
    // The HTMLImageElement's useMap() value includes the '#' symbol at the
    // beginning, which has to be stripped off.
    auto& image_element = To<HTMLImageElement>(*curr);
    const AtomicString& use_map_name_with_hash =
        image_element.FastGetAttribute(html_names::kUsemapAttr);
    if (use_map_name_with_hash.length() > 1) {
      StringView use_map_name(use_map_name_with_hash, 1);
      if (use_map_name == name_ || use_map_name == GetIdAttribute()) {
        return &image_element;
      }
    }
  }

  return nullptr;
}

void HTMLMapElement::ParseAttribute(const AttributeModificationParams& params) {
  // To return the first image that matches usemap on name or id attributes, we
  // need to track their values in the TreeScope.
  // https://html.spec.whatwg.org/multipage/#image-map-processing-model
  if (params.name == html_names::kIdAttr ||
      params.name == html_names::kNameAttr) {
    if (isConnected()) {
      // Note that GetIdAttribute() will already return the new value of the
      // `id` attribute, while GetName() won't change until the `name_` member
      // is changed below.
      AtomicString old_name = GetName();
      AtomicString old_id = params.name == html_names::kIdAttr
                                ? params.old_value
                                : GetIdAttribute();
      GetTreeScope().RemoveImageMap(*this, old_name, old_id);
    }

    if (params.name == html_names::kIdAttr) {
      // Call base class so that hasID bit gets set.
      HTMLElement::ParseAttribute(params);
    } else {
      StringView map_name = params.new_value;
      if (map_name.starts_with('#')) {
        map_name.remove_prefix(1);
      }
      if (RuntimeEnabledFeatures::FixMapElementEmptyNameBugEnabled() ||
          !map_name.empty()) {
        name_ = map_name.ToAtomicString();
      }
    }

    if (isConnected()) {
      GetTreeScope().AddImageMap(*this);
    }

    return;
  }

  HTMLElement::ParseAttribute(params);
}

HTMLCollection* HTMLMapElement::areas() {
  return EnsureCachedCollection<HTMLCollection>(kMapAreas);
}

Node::InsertionNotificationRequest HTMLMapElement::InsertedInto(
    ContainerNode& insertion_point) {
  if (insertion_point.isConnected())
    GetTreeScope().AddImageMap(*this);
  return HTMLElement::InsertedInto(insertion_point);
}

void HTMLMapElement::RemovedFrom(ContainerNode& insertion_point) {
  if (insertion_point.isConnected()) {
    GetTreeScope().RemoveImageMap(*this, GetName(), GetIdAttribute());
  }
  HTMLElement::RemovedFrom(insertion_point);
}

}  // namespace blink
