// Copyright 2023 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
//
// Tests the jpeg/png/y4m reading code from avifenc.

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "avif/avif.h"
#include "avif_fuzztest_helpers.h"
#include "aviftest_helpers.h"
#include "avifutil.h"
#include "fuzztest/fuzztest.h"
#include "gtest/gtest.h"

using ::fuzztest::Arbitrary;
using ::fuzztest::ElementOf;

namespace avif {
namespace testutil {
namespace {

//------------------------------------------------------------------------------

std::string FileFormatToString(avifAppFileFormat file_format) {
  switch (file_format) {
    case AVIF_APP_FILE_FORMAT_PNG:
      return "PNG";
    case AVIF_APP_FILE_FORMAT_JPEG:
      return "JPEG";
    case AVIF_APP_FILE_FORMAT_Y4M:
      return "Y4M";
    default:
      assert(false);
      return "unknown";
  }
}

void ReadImageFile(const std::string& arbitrary_bytes,
                   avifPixelFormat requested_format, int requested_depth,
                   avifChromaDownsampling chroma_downsampling,
                   bool ignore_color_profile, bool ignore_exif, bool ignore_xmp,
                   bool allow_changing_cicp, bool ignore_gain_map,
                   avifMatrixCoefficients matrix_coefficients) {
  ASSERT_FALSE(GetSeedDataDirs().empty());  // Make sure seeds are available.

  // Write the byte stream to a temp file since avifReadImage() takes a file
  // path as input.
  const std::string file_path = testing::TempDir() + "inputimage";
  std::ofstream out(file_path);
  out << arbitrary_bytes;
  out.close();

  uint32_t out_depth;
  avifAppSourceTiming timing;
  ImagePtr avif_image(avifImageCreateEmpty());
  avif_image->matrixCoefficients = matrix_coefficients;

  // OSS-Fuzz limits the allocated memory to 2560 MB.
  constexpr uint32_t kMaxMem = 2560u * 1024 * 1024;
  // Consider at most four planes of 16-bit samples.
  constexpr uint32_t kMaxImageSize =
      kMaxMem / (AVIF_PLANE_COUNT_YUV + 1) / sizeof(uint16_t);
  // Reduce the limit further to include pixel buffer copies and other memory
  // allocations.
  constexpr uint32_t kImageSizeLimit = kMaxImageSize / 4;
  // SharpYUV is computationally expensive. Avoid timeouts.
  const uint32_t imageSizeLimit =
      (chroma_downsampling == AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV &&
       requested_format == AVIF_PIXEL_FORMAT_YUV420)
          ? kImageSizeLimit / 8
          : kImageSizeLimit;

  const avifAppFileFormat file_format = avifReadImage(
      file_path.c_str(), requested_format, requested_depth, chroma_downsampling,
      ignore_color_profile, ignore_exif, ignore_xmp, allow_changing_cicp,
      ignore_gain_map, imageSizeLimit, avif_image.get(), &out_depth, &timing,
      /*frameIter=*/nullptr);

  if (file_format != AVIF_APP_FILE_FORMAT_UNKNOWN) {
    EXPECT_GT(avif_image->width, 0);
    EXPECT_GT(avif_image->height, 0);

    if (requested_depth != 0 && file_format != AVIF_APP_FILE_FORMAT_Y4M) {
      EXPECT_EQ(avif_image->depth, requested_depth);
    }
    if (file_format != AVIF_APP_FILE_FORMAT_Y4M) {
      EXPECT_EQ(avif_image->yuvFormat, requested_format);
    }
    if (ignore_color_profile) {
      EXPECT_EQ(avif_image->icc.size, 0);
    }
    if (ignore_exif) {
      EXPECT_EQ(avif_image->exif.size, 0);
    }
    if (ignore_xmp) {
      EXPECT_EQ(avif_image->xmp.size, 0);
    }
    std::cout << "Decode successful (" << FileFormatToString(file_format)
              << ")\n";
  }
}

FUZZ_TEST(ReadImageFuzzTest, ReadImageFile)
    .WithDomains(ArbitraryImageWithSeeds({AVIF_APP_FILE_FORMAT_JPEG,
                                          AVIF_APP_FILE_FORMAT_PNG,
                                          AVIF_APP_FILE_FORMAT_Y4M}),
                 ArbitraryPixelFormat(),
                 /*requested_depth=*/ElementOf({0, 8, 10, 12}),
                 ElementOf({AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
                            AVIF_CHROMA_DOWNSAMPLING_FASTEST,
                            AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY,
                            AVIF_CHROMA_DOWNSAMPLING_AVERAGE,
                            AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV}),
                 /*ignore_color_profile=*/Arbitrary<bool>(),
                 /*ignore_exif=*/Arbitrary<bool>(),
                 /*ignore_xmp=*/Arbitrary<bool>(),
                 /*allow_changing_cicp=*/Arbitrary<bool>(),
                 /*ignore_gain_map=*/Arbitrary<bool>(),
                 ElementOf({AVIF_MATRIX_COEFFICIENTS_IDENTITY,
                            AVIF_MATRIX_COEFFICIENTS_BT709,
                            AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED,
                            AVIF_MATRIX_COEFFICIENTS_FCC,
                            AVIF_MATRIX_COEFFICIENTS_BT470BG,
                            AVIF_MATRIX_COEFFICIENTS_BT601,
                            AVIF_MATRIX_COEFFICIENTS_SMPTE240,
                            AVIF_MATRIX_COEFFICIENTS_YCGCO,
                            AVIF_MATRIX_COEFFICIENTS_BT2020_NCL,
                            AVIF_MATRIX_COEFFICIENTS_BT2020_CL,
                            AVIF_MATRIX_COEFFICIENTS_SMPTE2085,
                            AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL,
                            AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL,
                            AVIF_MATRIX_COEFFICIENTS_ICTCP}));

//------------------------------------------------------------------------------

}  // namespace
}  // namespace testutil
}  // namespace avif
