// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "pdf/test/test_helpers.h"

#include <string_view>

#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/test/task_environment.h"
#include "base/test/test_switches.h"
#include "build/build_config.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/pixel_test_utils.h"
#include "pdf/pdfium/pdfium_engine_exports.h"
#include "printing/units.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/web/web_print_params.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/skia_conversions.h"

#if BUILDFLAG(IS_WIN)
#include "base/strings/utf_string_conversions.h"
#endif

namespace chrome_pdf {

namespace {

base::test::TaskEnvironment* g_task_environment = nullptr;

testing::AssertionResult MatchesPngFileImpl(
    const SkImage& actual_image,
    const base::FilePath& expected_png_file,
    const cc::PixelComparator& comparitor) {
  SkBitmap actual_bitmap;
  if (!actual_image.asLegacyBitmap(&actual_bitmap)) {
    return testing::AssertionFailure() << "Reference: " << expected_png_file;
  }

  const base::FilePath expected_path = GetTestDataFilePath(expected_png_file);
  if (cc::MatchesPNGFile(actual_bitmap, expected_path, comparitor)) {
    return testing::AssertionSuccess();
  }

  auto* cmd = base::CommandLine::ForCurrentProcess();
  if (!cmd->HasSwitch(switches::kRebaselinePixelTests)) {
    return testing::AssertionFailure() << "Reference: " << expected_png_file;
  }

  if (cc::WritePNGFile(actual_bitmap, expected_path, true)) {
    return testing::AssertionSuccess();
  }

  return testing::AssertionFailure() << "Rebase: " << expected_png_file;
}

SkBitmap RenderPdfToSkBitmap(base::span<const uint8_t> pdf_data,
                             int page_index,
                             const gfx::Size& size_in_points) {
  const gfx::Rect page_rect(size_in_points);
  SkBitmap page_bitmap;
  page_bitmap.allocPixels(
      SkImageInfo::Make(gfx::SizeToSkISize(page_rect.size()),
                        kBGRA_8888_SkColorType, kPremul_SkAlphaType));

  PDFiumEngineExports::RenderingSettings settings(
      gfx::Size(printing::kPointsPerInch, printing::kPointsPerInch), page_rect,
      /*fit_to_bounds=*/false,
      /*stretch_to_bounds=*/false,
      /*keep_aspect_ratio=*/true,
      /*center_in_bounds=*/false,
      /*autorotate=*/false, /*use_color=*/true, /*render_for_printing=*/false);

  PDFiumEngineExports exports;
  if (!exports.RenderPDFPageToBitmap(pdf_data, page_index, settings,
                                     page_bitmap.getPixels())) {
    ADD_FAILURE();
  }
  return page_bitmap;
}

}  // namespace

base::FilePath GetTestDataFilePath(const base::FilePath& path) {
  return base::PathService::CheckedGet(base::DIR_SRC_TEST_DATA_ROOT)
      .Append(FILE_PATH_LITERAL("pdf"))
      .Append(FILE_PATH_LITERAL("test"))
      .Append(FILE_PATH_LITERAL("data"))
      .Append(path);
}

base::FilePath::StringType GetTestDataPathWithPlatformSuffix(
    std::string_view filename) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN)
  base::FilePath path(base::UTF8ToWide(filename));
  static constexpr std::wstring_view kSuffix = L"_win";
#else
  base::FilePath path(filename);
  static constexpr std::string_view kSuffix = "_mac";
#endif  // BUILDFLAG(IS_WIN)
  return path.InsertBeforeExtension(kSuffix).value();
#else
  return base::FilePath(filename).value();
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
}

base::FilePath GetReferenceFilePath(
    base::FilePath::StringViewType sub_directory,
    std::string_view test_filename,
    bool use_platform_suffix) {
  if (use_platform_suffix) {
    return base::FilePath(sub_directory)
        .Append(GetTestDataPathWithPlatformSuffix(test_filename));
  }

  return base::FilePath(sub_directory).AppendASCII(test_filename);
}

testing::AssertionResult MatchesPngFile(
    const SkImage& actual_image,
    const base::FilePath& expected_png_file) {
  return MatchesPngFileImpl(actual_image, expected_png_file,
                            cc::ExactPixelComparator());
}

testing::AssertionResult FuzzyMatchesPngFile(
    const SkImage& actual_image,
    const base::FilePath& expected_png_file) {
  // Effectively a "FuzzyPixelOffByTwoComparator".
  cc::FuzzyPixelComparator comparator;
  comparator.SetErrorPixelsPercentageLimit(100.0f);
  comparator.SetAbsErrorLimit(2);
  return MatchesPngFileImpl(actual_image, expected_png_file, comparator);
}

bool IsBitmapBlank(const SkBitmap& bitmap) {
  for (int i = 0; i < bitmap.width(); ++i) {
    for (int j = 0; j < bitmap.height(); ++j) {
      if (bitmap.getColor(i, j) != SK_ColorWHITE) {
        return false;
      }
    }
  }
  return true;
}

bool IsImageBlank(const SkImage& image) {
  SkBitmap bitmap;
  if (!image.asLegacyBitmap(&bitmap)) {
    ADD_FAILURE();
    return false;
  }
  return IsBitmapBlank(bitmap);
}

void CheckPdfRendering(base::span<const uint8_t> pdf_data,
                       int page_index,
                       const gfx::Size& size_in_points,
                       const base::FilePath& expected_png_file) {
  SkBitmap page_bitmap =
      RenderPdfToSkBitmap(pdf_data, page_index, size_in_points);
  EXPECT_TRUE(MatchesPngFile(*page_bitmap.asImage(), expected_png_file));
}

void CheckFuzzyPdfRendering(base::span<const uint8_t> pdf_data,
                            int page_index,
                            const gfx::Size& size_in_points,
                            const base::FilePath& expected_png_file) {
  SkBitmap page_bitmap =
      RenderPdfToSkBitmap(pdf_data, page_index, size_in_points);
  EXPECT_TRUE(FuzzyMatchesPngFile(*page_bitmap.asImage(), expected_png_file));
}

sk_sp<SkSurface> CreateSkiaSurfaceForTesting(const gfx::Size& size,
                                             SkColor color) {
  auto surface = SkSurfaces::Raster(
      SkImageInfo::MakeN32Premul(size.width(), size.height()));
  surface->getCanvas()->clear(color);
  return surface;
}

sk_sp<SkImage> CreateSkiaImageForTesting(const gfx::Size& size, SkColor color) {
  return CreateSkiaSurfaceForTesting(size, color)->makeImageSnapshot();
}

static v8::Isolate* g_isolate = nullptr;

v8::Isolate* GetBlinkIsolate() {
  return g_isolate;
}

void SetBlinkIsolate(v8::Isolate* isolate) {
  g_isolate = isolate;
}

blink::WebPrintParams GetDefaultPrintParams() {
  blink::WebPrintParams params;
  params.default_page_description.size = kUSLetterSize;
  params.printable_area_in_css_pixels = kUSLetterRect;
  params.print_scaling_option = printing::mojom::PrintScalingOption::kNone;
  return params;
}

void SetPdfTestTaskEnvironment(base::test::TaskEnvironment* task_environment) {
  g_task_environment = task_environment;
}

base::test::TaskEnvironment& GetPdfTestTaskEnvironment() {
  CHECK(g_task_environment);
  return *g_task_environment;
}

}  // namespace chrome_pdf
