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

#include "chromecast/starboard/media/renderer/chromium_starboard_conversions.h"

#include <cstring>
#include <optional>
#include <vector>

#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
#include "chromecast/starboard/media/media/test_matchers.h"
#include "media/base/audio_codecs.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/channel_layout.h"
#include "media/base/encryption_scheme.h"
#include "media/base/sample_format.h"
#include "media/base/video_codecs.h"
#include "media/base/video_color_space.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_transformation.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"

namespace chromecast {
namespace media {
namespace {

using ::media::AudioCodec;
using ::media::AudioDecoderConfig;
using ::media::ChannelLayout;
using ::media::ChannelLayoutConfig;
using ::media::EncryptionScheme;
using ::media::SampleFormat;
using ::media::VideoCodec;
using ::media::VideoCodecProfile;
using ::media::VideoColorSpace;
using ::media::VideoDecoderConfig;
using ::media::VideoTransformation;
using ::testing::Optional;

TEST(StarboardConversionsTest, ConvertsValidAudioConfigToStarboardConfig) {
  EXPECT_THAT(
      ToStarboardAudioSampleInfo(AudioDecoderConfig(
          AudioCodec::kAAC, SampleFormat::kSampleFormatS32,
          ChannelLayoutConfig::FromLayout<ChannelLayout::CHANNEL_LAYOUT_5_1>(),
          /*samples_per_second=*/48000,
          /*extra_data=*/{}, EncryptionScheme::kCenc)),
      Optional(MatchesAudioSampleInfo({
          .codec = StarboardAudioCodec::kStarboardAudioCodecAac,
          .mime = R"-(audio/mp4; codecs="mp4a.40.5")-",
          .format_tag = 0,
          .number_of_channels = 6,
          .samples_per_second = 48000,
          .average_bytes_per_second = 48000 * 4 * 6,
          .block_alignment = 4,
          .bits_per_sample = 32,
          .audio_specific_config_size = 0,
          .audio_specific_config = nullptr,
      })));
}

TEST(StarboardConversionsTest, ReturnsNulloptForInvalidAudioConfig) {
  // DTS is not supported in starboard.
  EXPECT_EQ(
      ToStarboardAudioSampleInfo(AudioDecoderConfig(
          AudioCodec::kDTS, SampleFormat::kSampleFormatS32,
          ChannelLayoutConfig::FromLayout<ChannelLayout::CHANNEL_LAYOUT_5_1>(),
          /*samples_per_second=*/48000,
          /*extra_data=*/{}, EncryptionScheme::kCenc)),
      std::nullopt);
}

TEST(StarboardConversionsTest, ConvertsValidVideoConfigToStarboardConfig) {
  EXPECT_THAT(
      ToStarboardVideoSampleInfo(VideoDecoderConfig(
          VideoCodec::kHEVC, VideoCodecProfile::HEVCPROFILE_MAIN10,
          VideoDecoderConfig::AlphaMode::kIsOpaque,
          VideoColorSpace(1, 1, 1, gfx::ColorSpace::RangeID::LIMITED),
          VideoTransformation(), gfx::Size(1920, 1080), gfx::Rect(1920, 1080),
          gfx::Size(1920, 1080), /*extra_data=*/{}, EncryptionScheme::kCenc)),
      Optional(MatchesVideoSampleInfo({
          .codec = StarboardVideoCodec::kStarboardVideoCodecH265,
          .mime = R"-(video/mp4; codecs="hev1.2.6.L0.B0")-",
          .max_video_capabilities = "",
          .is_key_frame = false,
          .frame_width = 1920,
          .frame_height = 1080,
          .color_metadata =
              StarboardColorMetadata{
                  .bits_per_channel = 0,               // unknown
                  .chroma_subsampling_horizontal = 0,  // unknown
                  .chroma_subsampling_vertical = 0,    // unknown
                  .cb_subsampling_horizontal = 0,      // unknown
                  .cb_subsampling_vertical = 0,        // unknown
                  .chroma_siting_horizontal = 0,       // unknown
                  .chroma_siting_vertical = 0,         // unknown
                  .mastering_metadata = {},
                  .max_cll = 0,
                  .max_fall = 0,
                  .primaries = 1,
                  .transfer = 1,
                  .matrix = 1,
                  .range = 1,
              },
      })));
}

TEST(StarboardConversionsTest,
     ConvertsValidVideoConfigWithHdrMetadataToStarboardConfig) {
  VideoDecoderConfig chromium_config(
      VideoCodec::kHEVC, VideoCodecProfile::HEVCPROFILE_MAIN10,
      VideoDecoderConfig::AlphaMode::kIsOpaque,
      VideoColorSpace(9, 16, 9, gfx::ColorSpace::RangeID::LIMITED),
      VideoTransformation(), gfx::Size(3840, 2160), gfx::Rect(3840, 2160),
      gfx::Size(3840, 2160), /*extra_data=*/{}, EncryptionScheme::kUnencrypted);
  gfx::HDRMetadata hdr_metadata;
  skhdr::MasteringDisplayColorVolume smpte;
  smpte.fDisplayPrimaries.fRX = 0.1;
  smpte.fDisplayPrimaries.fRY = 0.2;
  smpte.fDisplayPrimaries.fGX = 0.3;
  smpte.fDisplayPrimaries.fGY = 0.4;
  smpte.fDisplayPrimaries.fBX = 0.5;
  smpte.fDisplayPrimaries.fBY = 0.6;
  smpte.fDisplayPrimaries.fWX = 0.7;
  smpte.fDisplayPrimaries.fWY = 0.8;
  smpte.fMaximumDisplayMasteringLuminance = 1.1;
  smpte.fMinimumDisplayMasteringLuminance = 0.01;

  skhdr::ContentLightLevelInformation cta;
  cta.fMaxCLL = 100;
  cta.fMaxFALL = 1000;

  hdr_metadata.SetMDCV(smpte);
  hdr_metadata.SetCLLI(cta);
  chromium_config.set_hdr_metadata(hdr_metadata);

  EXPECT_THAT(ToStarboardVideoSampleInfo(chromium_config),
              Optional(MatchesVideoSampleInfo({
                  .codec = StarboardVideoCodec::kStarboardVideoCodecH265,
                  .mime = R"-(video/mp4; codecs="hev1.2.6.L0.B0")-",
                  .max_video_capabilities = "",
                  .is_key_frame = false,
                  .frame_width = 3840,
                  .frame_height = 2160,
                  .color_metadata =
                      {
                          .bits_per_channel = 0,               // unknown
                          .chroma_subsampling_horizontal = 0,  // unknown
                          .chroma_subsampling_vertical = 0,    // unknown
                          .cb_subsampling_horizontal = 0,      // unknown
                          .cb_subsampling_vertical = 0,        // unknown
                          .chroma_siting_horizontal = 0,       // unknown
                          .chroma_siting_vertical = 0,         // unknown
                          .mastering_metadata =
                              {
                                  .primary_r_chromaticity_x = 0.1,
                                  .primary_r_chromaticity_y = 0.2,
                                  .primary_g_chromaticity_x = 0.3,
                                  .primary_g_chromaticity_y = 0.4,
                                  .primary_b_chromaticity_x = 0.5,
                                  .primary_b_chromaticity_y = 0.6,
                                  .white_point_chromaticity_x = 0.7,
                                  .white_point_chromaticity_y = 0.8,
                                  .luminance_max = 1.1,
                                  .luminance_min = 0.01,
                              },
                          .max_cll = 100,
                          .max_fall = 1000,
                          .primaries = 9,
                          .transfer = 16,
                          .matrix = 9,
                          .range = 1,
                      },
              })));
}

TEST(StarboardConversionsTest, ReturnsNulloptForInvalidVideoConfig) {
  // Dolby Vision is paired with a bad profile.
  EXPECT_EQ(
      ToStarboardVideoSampleInfo(VideoDecoderConfig(
          VideoCodec::kDolbyVision, VideoCodecProfile::HEVCPROFILE_MAIN,
          VideoDecoderConfig::AlphaMode::kIsOpaque,
          VideoColorSpace(1, 1, 1, gfx::ColorSpace::RangeID::LIMITED),
          VideoTransformation(), gfx::Size(1920, 1080), gfx::Rect(1920, 1080),
          gfx::Size(1920, 1080), /*extra_data=*/{}, EncryptionScheme::kCenc)),
      std::nullopt);
}

}  // namespace
}  // namespace media
}  // namespace chromecast
