/*
 * Copyright 2025 The WebRTC project authors. All rights reserved.
 *
 * Use of this source code is governed by a BSD-style license
 * that can be found in the LICENSE file in the root of the source
 * tree. An additional intellectual property rights grant can be found
 * in the file PATENTS.  All contributing project authors may
 * be found in the AUTHORS file in the root of the source tree.
 */

#include "video/corruption_detection/evaluation/utils.h"

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include "api/scoped_refptr.h"
#include "api/video/i420_buffer.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/frame_reader.h"

namespace webrtc {
namespace {

using ::testing::HasSubstr;
using ::testing::Not;

// Dimension for the test frames.
constexpr int kWidth = 4;
constexpr int kHeight = 4;
constexpr int kChromaWidth = 2;
constexpr int kChromaHeight = 2;
constexpr int kFrameRate = 30;

// An arbitrary 4x4 raw YUV420 frame.
constexpr uint8_t kFrameYContent[kWidth * kHeight] = {
    12,  5,   7,   11,  //
    159, 15,  11,  0,   //
    4,   240, 131, 59,  //
    61,  87,  11,  0    //
};
constexpr uint8_t kFrameUContent[kChromaWidth * kChromaHeight] = {
    248, 184,  //
    139, 229   //
};
constexpr uint8_t kFrameVContent[kChromaWidth * kChromaHeight] = {
    32, 69,  //
    7, 193   //
};

// Concatenates and returns Y, U and V content into a single vector.
std::vector<uint8_t> ConcatenateVideoChannels() {
  std::vector<uint8_t> file_content_flattened;
  file_content_flattened.insert(file_content_flattened.end(), kFrameYContent,
                                kFrameYContent + kWidth * kHeight);
  file_content_flattened.insert(file_content_flattened.end(), kFrameUContent,
                                kFrameUContent + kChromaWidth * kChromaHeight);
  file_content_flattened.insert(file_content_flattened.end(), kFrameVContent,
                                kFrameVContent + kChromaWidth * kChromaHeight);
  return file_content_flattened;
}

TEST(TempY4mFileCreatorTest, CheckIfY4mFileIsCreated) {
  std::vector<uint8_t> file_content_flattened = ConcatenateVideoChannels();

  TempY4mFileCreator temp_y4m_file_creator(kWidth, kHeight, kFrameRate);
  temp_y4m_file_creator.CreateTempY4mFile(file_content_flattened);

  EXPECT_TRUE(test::FileExists(temp_y4m_file_creator.y4m_filepath()));
}

TEST(TempY4mFileCreatorTest, CanCreateMultipleFiles) {
  std::vector<uint8_t> file_content_flattened = ConcatenateVideoChannels();

  TempY4mFileCreator temp_y4m_file_creator_1(kWidth, kHeight, kFrameRate);
  TempY4mFileCreator temp_y4m_file_creator_2(kWidth, kHeight, kFrameRate);
  temp_y4m_file_creator_1.CreateTempY4mFile(file_content_flattened);
  temp_y4m_file_creator_2.CreateTempY4mFile(file_content_flattened);

  // Check that the files are created.
  ASSERT_TRUE(test::FileExists(temp_y4m_file_creator_1.y4m_filepath()));
  ASSERT_TRUE(test::FileExists(temp_y4m_file_creator_2.y4m_filepath()));
  // Check that the created files have different paths.
  EXPECT_THAT(temp_y4m_file_creator_1.y4m_filepath(),
              Not(temp_y4m_file_creator_2.y4m_filepath()));
}

TEST(TempY4mFileCreatorTest, CheckIfY4mFileHasCorrectContent) {
  std::vector<uint8_t> file_content_flattened = ConcatenateVideoChannels();

  TempY4mFileCreator temp_y4m_file_creator(kWidth, kHeight, kFrameRate);
  temp_y4m_file_creator.CreateTempY4mFile(file_content_flattened);
  std::unique_ptr<test::FrameReader> frame_generator =
      CreateY4mFrameReader(std::string(temp_y4m_file_creator.y4m_filepath()),
                           test::YuvFrameReaderImpl::RepeatMode::kSingle);

  scoped_refptr<I420Buffer> i420_buffer = frame_generator->PullFrame();
  ASSERT_EQ(i420_buffer->width(), kWidth);
  ASSERT_EQ(i420_buffer->height(), kHeight);
  for (int i = 0; i < kWidth * kHeight; ++i) {
    EXPECT_EQ(i420_buffer->DataY()[i], kFrameYContent[i]);
  }
  for (int i = 0; i < kChromaWidth * kChromaHeight; ++i) {
    EXPECT_EQ(i420_buffer->DataU()[i], kFrameUContent[i]);
    EXPECT_EQ(i420_buffer->DataV()[i], kFrameVContent[i]);
  }
  // No more frames.
  EXPECT_THAT(frame_generator->PullFrame(), nullptr);
}

#if GTEST_HAS_DEATH_TEST
TEST(TempY4mFileCreatorTest, ContentEndInMiddleError) {
  std::vector<uint8_t> file_content_flattened = ConcatenateVideoChannels();
  std::vector<uint8_t> only_luma_content(
      file_content_flattened.begin(),
      file_content_flattened.begin() + kWidth * kHeight);

  TempY4mFileCreator temp_y4m_file_creator(kWidth, kHeight, kFrameRate);
  EXPECT_DEATH(temp_y4m_file_creator.CreateTempY4mFile(only_luma_content),
               HasSubstr("Content size is not a multiple of frame size."));
}
#endif  // GTEST_HAS_DEATH_TEST

TEST(ReadMetadataFromY4mHeaderTest, ReadMetadataFromY4mHeader) {
  std::vector<uint8_t> file_content_flattened = ConcatenateVideoChannels();

  TempY4mFileCreator temp_y4m_file_creator(kWidth, kHeight, kFrameRate);
  temp_y4m_file_creator.CreateTempY4mFile(file_content_flattened);
  Y4mMetadata y4m_metadata =
      ReadMetadataFromY4mHeader(temp_y4m_file_creator.y4m_filepath());

  EXPECT_EQ(y4m_metadata.width, kWidth);
  EXPECT_EQ(y4m_metadata.height, kHeight);
  EXPECT_EQ(y4m_metadata.framerate, kFrameRate);
}

}  // namespace
}  // namespace webrtc
