// Copyright 2023 Google LLC
// SPDX-License-Identifier: BSD-2-Clause

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

namespace libavif {
namespace {

// Used to pass the data folder path to the GoogleTest suites.
const char* data_path = nullptr;

// Tests encode/decode round trips under different matrix coefficients.
TEST(BasicTest, EncodeDecodeMatrixCoefficients) {
  for (const auto& file_name :
       {"paris_icc_exif_xmp.png", "paris_exif_xmp_icc.jpg"}) {
    // Read a ground truth image with default matrix coefficients.
    const std::string file_path = std::string(data_path) + file_name;
    const testutil::AvifImagePtr ground_truth_image =
        testutil::ReadImage(data_path, file_name);

    for (auto matrix_coefficient : {
#if defined(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R)
           AVIF_MATRIX_COEFFICIENTS_YCGCO_RE, AVIF_MATRIX_COEFFICIENTS_YCGCO_RO,
#endif
               AVIF_MATRIX_COEFFICIENTS_IDENTITY, AVIF_MATRIX_COEFFICIENTS_YCGCO
         }) {
      // Read a ground truth image but ask for certain matrix coefficients.
      testutil::AvifImagePtr image(avifImageCreateEmpty(), avifImageDestroy);
      ASSERT_NE(image, nullptr);
      image->matrixCoefficients = (avifMatrixCoefficients)matrix_coefficient;
      const avifAppFileFormat file_format = avifReadImage(
          file_path.c_str(),
          /*requestedFormat=*/AVIF_PIXEL_FORMAT_NONE,
          /*requestedDepth=*/0,
          /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
          /*ignoreColorProfile=*/false, /*ignoreExif=*/false,
          /*ignoreXMP=*/false, /*allowChangingCicp=*/true, image.get(),
          /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
          /*frameIter=*/nullptr);
#if defined(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R)
      if (matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_YCGCO_RO) {
        // AVIF_MATRIX_COEFFICIENTS_YCGCO_RO does not work because the input
        // depth is not odd.
        ASSERT_EQ(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN);
        continue;
      }
#endif
      ASSERT_NE(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN);

      // Encode.
      testutil::AvifEncoderPtr encoder(avifEncoderCreate(), avifEncoderDestroy);
      ASSERT_NE(encoder, nullptr);
      encoder->speed = AVIF_SPEED_FASTEST;
      encoder->quality = AVIF_QUALITY_LOSSLESS;
      encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS;
      testutil::AvifRwData encoded;
      avifResult result =
          avifEncoderWrite(encoder.get(), image.get(), &encoded);
      ASSERT_EQ(result, AVIF_RESULT_OK) << avifResultToString(result);

      // Decode to RAM.
      testutil::AvifImagePtr decoded(avifImageCreateEmpty(), avifImageDestroy);
      ASSERT_NE(decoded, nullptr);
      testutil::AvifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
      ASSERT_NE(decoder, nullptr);
      result = avifDecoderReadMemory(decoder.get(), decoded.get(), encoded.data,
                                     encoded.size);
      ASSERT_EQ(result, AVIF_RESULT_OK) << avifResultToString(result);

      // Convert to a temporary PNG and read back, to have default matrix
      // coefficients.
      const std::string temp_dir = testing::TempDir();
      const std::string temp_file = "decoded_default.png";
      const std::string tmp_path = temp_dir + temp_file;
      ASSERT_TRUE(testutil::WriteImage(decoded.get(), tmp_path.c_str()));
      const testutil::AvifImagePtr decoded_default =
          testutil::ReadImage(temp_dir.c_str(), temp_file.c_str());

      // Verify that the ground truth and decoded images are the same.
      const bool are_images_equal =
          testutil::AreImagesEqual(*ground_truth_image, *decoded_default);

      if (matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_IDENTITY
#if defined(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R)
          || matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_YCGCO_RE
#endif
      ) {
        ASSERT_TRUE(are_images_equal);
      } else {
        // AVIF_MATRIX_COEFFICIENTS_YCGCO is not lossless because it does not
        // expand the input bit range for YCgCo values.
        ASSERT_FALSE(are_images_equal);
      }
    }
  }
}

}  // namespace
}  // namespace libavif

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  if (argc != 2) {
    std::cerr << "There must be exactly one argument containing the path to "
                 "the test data folder"
              << std::endl;
    return 1;
  }
  libavif::data_path = argv[1];
  return RUN_ALL_TESTS();
}
