/*
 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
 *           (C) 2000 Antti Koivisto (koivisto@kde.org)
 *           (C) 2000 Dirk Mueller (mueller@kde.org)
 * Copyright (C) 2003, 2005, 2006, 2007, 2008 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/style/style_fetched_image.h"

#include "third_party/blink/renderer/core/css/css_image_value.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
#include "third_party/blink/renderer/core/paint/timing/paint_timing.h"
#include "third_party/blink/renderer/core/paint/timing/paint_timing_detector.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"

namespace blink {

StyleFetchedImage::StyleFetchedImage(ImageResourceContent* image,
                                     const CSSUrlData& url_data,
                                     Document& document,
                                     const KURL& url,
                                     const float override_image_resolution)
    : url_data_(url_data),
      document_(document),
      url_(url),
      override_image_resolution_(override_image_resolution) {
  is_image_resource_ = true;

  image_ = image;
  image_->AddObserver(this);
}

StyleFetchedImage::~StyleFetchedImage() = default;

void StyleFetchedImage::Prefinalize() {
  image_->DidRemoveObserver();
  image_ = nullptr;
}

bool StyleFetchedImage::IsEqual(const StyleImage& other) const {
  if (other.IsPendingImage()) {
    // Ignore pending status when comparing; as long as the values are
    // equal, the same, the images should be considered equal, too.
    return base::ValuesEquivalent(CssValue(), other.CssValue());
  }
  if (!other.IsImageResource()) {
    return false;
  }
  const auto& other_image = To<StyleFetchedImage>(other);
  return image_ == other_image.image_ && *url_data_ == *other_image.url_data_ &&
         EqualResolutions(override_image_resolution_,
                          other_image.override_image_resolution_);
}

WrappedImagePtr StyleFetchedImage::Data() const {
  return image_.Get();
}

float StyleFetchedImage::ImageScaleFactor() const {
  if (override_image_resolution_ > 0.0f) {
    return override_image_resolution_;
  }

  if (image_->HasDevicePixelRatioHeaderValue()) {
    return image_->DevicePixelRatioHeaderValue();
  }

  return 1.0f;
}

ImageResourceContent* StyleFetchedImage::CachedImage() const {
  return image_.Get();
}

CSSValue* StyleFetchedImage::CssValue() const {
  return MakeGarbageCollected<CSSImageValue>(
      *url_data_->MakeComputed(), const_cast<StyleFetchedImage*>(this));
}

CSSValue* StyleFetchedImage::ComputedCSSValue(const ComputedStyle&,
                                              bool allow_visited_style,
                                              CSSValuePhase value_phase) const {
  return CssValue();
}

bool StyleFetchedImage::CanRender() const {
  return image_->HasImage() && !image_->ErrorOccurred();
}

bool StyleFetchedImage::IsLoaded() const {
  return image_->IsLoaded();
}

bool StyleFetchedImage::IsLoading() const {
  return image_->IsLoading();
}

bool StyleFetchedImage::ErrorOccurred() const {
  return image_->ErrorOccurred();
}

bool StyleFetchedImage::IsCorsSameOrigin() const {
  if (!image_->IsLoaded() && image_->GetImage() &&
      image_->GetImage()->IsSVGImage()) {
    return false;
  }
  return image_->IsCorsSameOrigin();
}

float StyleFetchedImage::ApplyImageResolution(float multiplier) const {
  const Image& image = *image_->GetImage();
  if (image.IsBitmapImage() && override_image_resolution_ > 0.0f) {
    multiplier /= override_image_resolution_;
  } else if (image_->HasDevicePixelRatioHeaderValue()) {
    multiplier /= image_->DevicePixelRatioHeaderValue();
  }
  return multiplier;
}

gfx::SizeF StyleFetchedImage::ImageSize(
    float multiplier,
    const gfx::SizeF& default_object_size,
    RespectImageOrientationEnum respect_orientation) const {
  multiplier = ApplyImageResolution(multiplier);

  Image& image = *image_->GetImage();
  gfx::SizeF size;
  if (auto* svg_image = DynamicTo<SVGImage>(image)) {
    const SVGImageViewInfo* view_info =
        SVGImageForContainer::CreateViewInfo(*svg_image, url_);
    const gfx::SizeF unzoomed_default_object_size =
        gfx::ScaleSize(default_object_size, 1 / multiplier);
    size = SVGImageForContainer::ConcreteObjectSize(
        *svg_image, view_info, unzoomed_default_object_size);
  } else {
    size = gfx::SizeF(
        image.Size(ForceOrientationIfNecessary(respect_orientation)));
  }
  return ApplyZoom(size, multiplier);
}

NaturalSizingInfo StyleFetchedImage::GetNaturalSizingInfo(
    float multiplier,
    RespectImageOrientationEnum respect_orientation) const {
  Image& image = *image_->GetImage();
  NaturalSizingInfo sizing_info;
  if (auto* svg_image = DynamicTo<SVGImage>(image)) {
    const SVGImageViewInfo* view_info =
        SVGImageForContainer::CreateViewInfo(*svg_image, url_);
    sizing_info =
        SVGImageForContainer::GetNaturalDimensions(*svg_image, view_info)
            .value_or(NaturalSizingInfo::None());
  } else {
    gfx::SizeF size(
        image.Size(ForceOrientationIfNecessary(respect_orientation)));
    sizing_info = NaturalSizingInfo::MakeFixed(size);
  }

  multiplier = ApplyImageResolution(multiplier);
  sizing_info.size = ApplyZoom(sizing_info.size, multiplier);
  return sizing_info;
}

bool StyleFetchedImage::HasIntrinsicSize() const {
  Image& image = *image_->GetImage();
  if (auto* svg_image = DynamicTo<SVGImage>(image)) {
    const SVGImageViewInfo* view_info =
        SVGImageForContainer::CreateViewInfo(*svg_image, url_);
    std::optional<NaturalSizingInfo> natural_sizing_info =
        SVGImageForContainer::GetNaturalDimensions(*svg_image, view_info);
    return natural_sizing_info && !natural_sizing_info->IsNone();
  }
  return image.HasIntrinsicSize();
}

void StyleFetchedImage::AddClient(ImageResourceObserver* observer) {
  image_->AddObserver(observer);
}

void StyleFetchedImage::RemoveClient(ImageResourceObserver* observer) {
  image_->RemoveObserver(observer);
}

void StyleFetchedImage::ImageNotifyFinished(ImageResourceContent*) {
  if (!document_) {
    return;
  }

  if (image_ && image_->HasImage()) {
    Image& image = *image_->GetImage();

    if (auto* svg_image = DynamicTo<SVGImage>(image)) {
      // Check that the SVGImage has completed loading (i.e the 'load' event
      // has been dispatched in the SVG document).
      svg_image->CheckLoaded();
      svg_image->UpdateUseCountersAfterLoad(*document_);
    }
    image_->RecordDecodedImageType(document_->GetExecutionContext());
  }

  if (document_->domWindow()) {
    PaintTimingDetector::From(*document_).NotifyBackgroundImageFinished(this);
  }

  // Oilpan: do not prolong the Document's lifetime.
  document_.Clear();
}

scoped_refptr<Image> StyleFetchedImage::GetImage(
    const ImageResourceObserver&,
    const Node& node,
    const ComputedStyle& style,
    const gfx::SizeF& target_size) const {
  Image* image = image_->GetImage();
  auto* svg_image = DynamicTo<SVGImage>(image);
  if (!svg_image) {
    return image;
  }
  const SVGImageViewInfo* view_info =
      SVGImageForContainer::CreateViewInfo(*svg_image, url_);
  return SVGImageForContainer::Create(
      *svg_image, target_size, style.EffectiveZoom(), view_info,
      node.GetDocument().GetStyleEngine().ResolveColorSchemeForEmbedding(
          &style));
}

bool StyleFetchedImage::KnownToBeOpaque(const Document&,
                                        const ComputedStyle&) const {
  return image_->GetImage()->IsOpaque();
}

bool StyleFetchedImage::GetImageAnimationPolicy(
    mojom::blink::ImageAnimationPolicy& policy) {
  if (!document_ || !document_->GetSettings()) {
    return false;
  }
  policy = document_->GetSettings()->GetImageAnimationPolicy();
  return true;
}

bool StyleFetchedImage::CanBeSpeculativelyDecoded() const {
  return false;
}

void StyleFetchedImage::Trace(Visitor* visitor) const {
  visitor->Trace(image_);
  visitor->Trace(url_data_);
  visitor->Trace(document_);
  StyleImage::Trace(visitor);
  ImageResourceObserver::Trace(visitor);
}

}  // namespace blink
