/*
 *  Copyright (c) 2023 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 "api/video_codecs/h265_profile_tier_level.h"

#include <optional>
#include <string>

#include "api/rtp_parameters.h"
#include "api/video/resolution.h"
#include "test/gtest.h"

namespace webrtc {

TEST(H265ProfileTierLevel, TestLevelToString) {
  EXPECT_EQ(H265LevelToString(H265Level::kLevel1), "30");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel2), "60");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel2_1), "63");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel3), "90");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel3_1), "93");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel4), "120");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel4_1), "123");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel5), "150");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel5_1), "153");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel5_2), "156");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel6), "180");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel6_1), "183");
  EXPECT_EQ(H265LevelToString(H265Level::kLevel6_2), "186");
}

TEST(H265ProfileTierLevel, TestProfileToString) {
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMain), "1");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMain10), "2");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMainStill), "3");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileRangeExtensions), "4");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileHighThroughput), "5");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMultiviewMain), "6");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileScalableMain), "7");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfile3dMain), "8");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileScreenContentCoding), "9");
  EXPECT_EQ(H265ProfileToString(H265Profile::kProfileScalableRangeExtensions),
            "10");
  EXPECT_EQ(H265ProfileToString(
                H265Profile::kProfileHighThroughputScreenContentCoding),
            "11");
}

TEST(H265ProfileTierLevel, TestTierToString) {
  EXPECT_EQ(H265TierToString(H265Tier::kTier0), "0");
  EXPECT_EQ(H265TierToString(H265Tier::kTier1), "1");
}

TEST(H265ProfileTierLevel, TestStringToProfile) {
  // Invalid profiles.
  EXPECT_FALSE(StringToH265Profile("0"));
  EXPECT_FALSE(StringToH265Profile("12"));

  // Malformed profiles
  EXPECT_FALSE(StringToH265Profile(""));
  EXPECT_FALSE(StringToH265Profile(" 1"));
  EXPECT_FALSE(StringToH265Profile("12x"));
  EXPECT_FALSE(StringToH265Profile("x12"));
  EXPECT_FALSE(StringToH265Profile("gggg"));

  // Valid profiles.
  EXPECT_EQ(StringToH265Profile("1"), H265Profile::kProfileMain);
  EXPECT_EQ(StringToH265Profile("2"), H265Profile::kProfileMain10);
  EXPECT_EQ(StringToH265Profile("4"), H265Profile::kProfileRangeExtensions);
}

TEST(H265ProfileTierLevel, TestStringToLevel) {
  // Invalid levels.
  EXPECT_FALSE(StringToH265Level("0"));
  EXPECT_FALSE(StringToH265Level("200"));

  // Malformed levels.
  EXPECT_FALSE(StringToH265Level(""));
  EXPECT_FALSE(StringToH265Level(" 30"));
  EXPECT_FALSE(StringToH265Level("30x"));
  EXPECT_FALSE(StringToH265Level("x30"));
  EXPECT_FALSE(StringToH265Level("ggggg"));

  // Valid levels.
  EXPECT_EQ(StringToH265Level("30"), H265Level::kLevel1);
  EXPECT_EQ(StringToH265Level("93"), H265Level::kLevel3_1);
  EXPECT_EQ(StringToH265Level("183"), H265Level::kLevel6_1);
}

TEST(H265ProfileTierLevel, TestStringToTier) {
  // Invalid tiers.
  EXPECT_FALSE(StringToH265Tier("4"));
  EXPECT_FALSE(StringToH265Tier("-1"));

  // Malformed tiers.
  EXPECT_FALSE(StringToH265Tier(""));
  EXPECT_FALSE(StringToH265Tier(" 1"));
  EXPECT_FALSE(StringToH265Tier("t1"));

  // Valid tiers.
  EXPECT_EQ(StringToH265Tier("0"), H265Tier::kTier0);
  EXPECT_EQ(StringToH265Tier("1"), H265Tier::kTier1);
}

TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelAllEmpty) {
  const std::optional<H265ProfileTierLevel> profile_tier_level =
      ParseSdpForH265ProfileTierLevel(CodecParameterMap());
  EXPECT_TRUE(profile_tier_level);
  EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile);
  EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level);
  EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);
}

TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelPartialEmpty) {
  CodecParameterMap params;
  params["profile-id"] = "1";
  params["tier-flag"] = "0";
  std::optional<H265ProfileTierLevel> profile_tier_level =
      ParseSdpForH265ProfileTierLevel(params);
  EXPECT_TRUE(profile_tier_level);
  EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile);
  EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level);
  EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);

  params.clear();
  params["profile-id"] = "2";
  profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
  EXPECT_TRUE(profile_tier_level);
  EXPECT_EQ(H265Profile::kProfileMain10, profile_tier_level->profile);
  EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level);
  EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);

  params.clear();
  params["level-id"] = "180";
  profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
  EXPECT_TRUE(profile_tier_level);
  EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile);
  EXPECT_EQ(H265Level::kLevel6, profile_tier_level->level);
  EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);
}

TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelInvalid) {
  CodecParameterMap params;

  // Invalid profile-tier-level combination.
  params["profile-id"] = "1";
  params["tier-flag"] = "1";
  params["level-id"] = "93";
  std::optional<H265ProfileTierLevel> profile_tier_level =
      ParseSdpForH265ProfileTierLevel(params);
  EXPECT_FALSE(profile_tier_level);
  params.clear();
  params["profile-id"] = "1";
  params["tier-flag"] = "4";
  params["level-id"] = "180";
  profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
  EXPECT_FALSE(profile_tier_level);

  // Valid profile-tier-level combination.
  params.clear();
  params["profile-id"] = "1";
  params["tier-flag"] = "0";
  params["level-id"] = "153";
  profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
  EXPECT_TRUE(profile_tier_level);
}

TEST(H265ProfileTierLevel, TestToStringRoundTrip) {
  CodecParameterMap params;
  params["profile-id"] = "1";
  params["tier-flag"] = "0";
  params["level-id"] = "93";
  std::optional<H265ProfileTierLevel> profile_tier_level =
      ParseSdpForH265ProfileTierLevel(params);
  EXPECT_TRUE(profile_tier_level);
  EXPECT_EQ("1", H265ProfileToString(profile_tier_level->profile));
  EXPECT_EQ("0", H265TierToString(profile_tier_level->tier));
  EXPECT_EQ("93", H265LevelToString(profile_tier_level->level));

  params.clear();
  params["profile-id"] = "2";
  params["tier-flag"] = "1";
  params["level-id"] = "180";
  profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
  EXPECT_TRUE(profile_tier_level);
  EXPECT_EQ("2", H265ProfileToString(profile_tier_level->profile));
  EXPECT_EQ("1", H265TierToString(profile_tier_level->tier));
  EXPECT_EQ("180", H265LevelToString(profile_tier_level->level));
}

TEST(H265ProfileTierLevel, TestProfileTierLevelCompare) {
  CodecParameterMap params1;
  CodecParameterMap params2;

  // None of profile-id/tier-flag/level-id is specified,
  EXPECT_TRUE(H265IsSameProfileTierLevel(params1, params2));

  // Same non-empty PTL
  params1["profile-id"] = "1";
  params1["tier-flag"] = "0";
  params1["level-id"] = "120";
  params2["profile-id"] = "1";
  params2["tier-flag"] = "0";
  params2["level-id"] = "120";
  EXPECT_TRUE(H265IsSameProfileTierLevel(params1, params2));

  // Different profiles.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "2";
  EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));

  // Different levels.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["level-id"] = "93";
  params2["level-id"] = "183";
  EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));

  // Different tiers.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["level-id"] = "180";
  // Level 3.1 is not allowed for tier 1.
  params2["level-id"] = "180";
  params1["tier-flag"] = "0";
  params2["tier-flag"] = "1";
  EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));

  // One of the CodecParameterMap is invalid.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["tier-flag"] = "0";
  params2["tier-flag"] = "4";
  EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));
}

TEST(H265ProfileTierLevel, TestProfileCompare) {
  CodecParameterMap params1;
  CodecParameterMap params2;

  // None of profile-id/tier-flag/level-id is specified,
  EXPECT_TRUE(H265IsSameProfile(params1, params2));

  // Same non-empty PTL
  params1["profile-id"] = "1";
  params1["tier-flag"] = "0";
  params1["level-id"] = "120";
  params2["profile-id"] = "1";
  params2["tier-flag"] = "0";
  params2["level-id"] = "120";
  EXPECT_TRUE(H265IsSameProfile(params1, params2));

  // Different profiles.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "2";
  EXPECT_FALSE(H265IsSameProfile(params1, params2));

  // Different levels. We do not compare HEVC levels.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["level-id"] = "93";
  params2["level-id"] = "183";
  EXPECT_TRUE(H265IsSameProfile(params1, params2));

  // Different tiers.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["level-id"] = "180";
  // level 3.1 is not allowed for tier 1.
  params2["level-id"] = "180";
  params1["tier-flag"] = "0";
  params2["tier-flag"] = "1";
  EXPECT_TRUE(H265IsSameProfile(params1, params2));

  // One of the CodecParameterMap is invalid.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["tier-flag"] = "0";
  params2["tier-flag"] = "4";
  EXPECT_FALSE(H265IsSameProfile(params1, params2));
}

TEST(H265ProfileTierLevel, TestTierCompare) {
  CodecParameterMap params1;
  CodecParameterMap params2;

  // None of profile-id/tier-flag/level-id is specified,
  EXPECT_TRUE(H265IsSameTier(params1, params2));

  // Same non-empty PTL
  params1["profile-id"] = "1";
  params1["tier-flag"] = "0";
  params1["level-id"] = "120";
  params2["profile-id"] = "1";
  params2["tier-flag"] = "0";
  params2["level-id"] = "120";
  EXPECT_TRUE(H265IsSameTier(params1, params2));

  // Different profiles.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "2";
  EXPECT_TRUE(H265IsSameTier(params1, params2));

  // Different levels. We do not compare HEVC levels.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["level-id"] = "93";
  params2["level-id"] = "183";
  EXPECT_TRUE(H265IsSameTier(params1, params2));

  // Different tiers.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["level-id"] = "180";
  // level 3.1 is not allowed for tier 1.
  params2["level-id"] = "180";
  params1["tier-flag"] = "0";
  params2["tier-flag"] = "1";
  EXPECT_FALSE(H265IsSameTier(params1, params2));

  // One of the CodecParameterMap is invalid.
  params1.clear();
  params2.clear();
  params1["profile-id"] = "1";
  params2["profile-id"] = "1";
  params1["tier-flag"] = "0";
  params2["tier-flag"] = "4";
  EXPECT_FALSE(H265IsSameTier(params1, params2));
}

TEST(H265ProfileTierLevel, TestGetSupportedH265Level) {
  // Test with 720p at 30fps
  Resolution r{.width = 1280, .height = 720};
  EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
            H265Level::kLevel3);

  // Test with QCIF at 15fps
  r.width = 176;
  r.height = 144;
  EXPECT_EQ(GetSupportedH265Level(r, 15).value_or(H265Level::kLevel2),
            H265Level::kLevel1);

  // Test with 1080p at 30fps
  r.width = 1920;
  r.height = 1080;
  EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
            H265Level::kLevel3_1);

  // Test with 1080p at 60fps
  EXPECT_EQ(GetSupportedH265Level(r, 60).value_or(H265Level::kLevel1),
            H265Level::kLevel3_1);

  // Test with 4K at 30fps
  r.width = 3840;
  r.height = 2160;
  EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
            H265Level::kLevel4_1);

  // Test with 4K at 60fps
  EXPECT_EQ(GetSupportedH265Level(r, 60).value_or(H265Level::kLevel1),
            H265Level::kLevel4_1);

  // Test with 8K at 30fps
  r.width = 8192;
  r.height = 4320;
  EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
            H265Level::kLevel6);

  // Test with 64x64 at 30fps
  r.width = 64;
  r.height = 64;
  EXPECT_EQ(GetSupportedH265Level(r, 30), std::nullopt);

  // Test with extremly large width or height at 15fps
  r.width = 16928;
  r.height = 64;
  EXPECT_EQ(GetSupportedH265Level(r, 15), std::nullopt);
}

}  // namespace webrtc
