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

#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"

#include "hb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
#include "third_party/blink/renderer/platform/fonts/glyph.h"
#include "third_party/blink/renderer/platform/fonts/shaping/variation_selector_mode.h"
#include "third_party/blink/renderer/platform/testing/font_test_base.h"
#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"

namespace blink {

namespace {

String WPTFontPath(const String& font_name) {
  return test::BlinkWebTestsDir() +
         "/external/wpt/css/css-fonts/resources/vs/" + font_name;
}

hb_codepoint_t GetGlyphForVariationSequenceFromFont(
    Font* font,
    UChar32 character,
    UChar32 variation_selector,
    VariationSelectorMode variation_selector_mode) {
  const FontPlatformData& font_without_char_platform_data =
      font->PrimaryFont()->PlatformData();
  HarfBuzzFace* face_without_char =
      font_without_char_platform_data.GetHarfBuzzFace();
  EXPECT_TRUE(face_without_char);
  face_without_char->SetVariationSelectorMode(variation_selector_mode);
  return face_without_char->HarfBuzzGetGlyphForTesting(character,
                                                       variation_selector);
}

hb_codepoint_t GetGlyphForEmojiVSFromFontWithVS15(
    UChar32 character,
    UChar32 variation_selector,
    VariationSelectorMode variation_selector_mode) {
  Font* font =
      test::CreateTestFont(AtomicString("Noto Emoji"),
                           WPTFontPath("NotoEmoji-Regular_subset.ttf"), 11);
  return GetGlyphForVariationSequenceFromFont(
      font, character, variation_selector, variation_selector_mode);
}

hb_codepoint_t GetGlyphForEmojiVSFromFontWithVS16(
    UChar32 character,
    UChar32 variation_selector,
    VariationSelectorMode variation_selector_mode) {
  Font* font = test::CreateTestFont(
      AtomicString("Noto Color Emoji"),
      WPTFontPath("NotoColorEmoji-Regular_subset.ttf"), 11);
  return GetGlyphForVariationSequenceFromFont(
      font, character, variation_selector, variation_selector_mode);
}

hb_codepoint_t GetGlyphForStandardizedVSFromFontWithBaseCharOnly(
    VariationSelectorMode variation_selector_mode) {
  UChar32 character = uchar::kMongolianLetterA;
  UChar32 variation_selector = uchar::kMongolianFreeVariationSelectorTwo;

  Font* font = test::CreateTestFont(AtomicString("Noto Sans Mongolian"),
                                    blink::test::BlinkWebTestsFontsTestDataPath(
                                        "noto/NotoSansMongolian-regular.woff2"),
                                    11);
  return GetGlyphForVariationSequenceFromFont(
      font, character, variation_selector, variation_selector_mode);
}

hb_codepoint_t GetGlyphForCJKVSFromFontWithVS(
    VariationSelectorMode variation_selector_mode) {
  UChar32 character = uchar::kFullwidthExclamationMark;
  UChar32 variation_selector = uchar::kVariationSelector2;

  Font* font = test::CreateTestFont(
      AtomicString("Noto Sans CJK JP"),
      blink::test::BlinkWebTestsFontsTestDataPath(
          "noto/cjk/NotoSansCJKjp-Regular-subset-chws.otf"),
      11);
  return GetGlyphForVariationSequenceFromFont(
      font, character, variation_selector, variation_selector_mode);
}

}  // namespace

class HarfBuzzFaceTest : public FontTestBase {};

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestFontWithVS) {
  hb_codepoint_t glyph =
      GetGlyphForCJKVSFromFontWithVS(kUseSpecifiedVariationSelector);
  EXPECT_TRUE(glyph);
  EXPECT_NE(glyph, kUnmatchedVSGlyphId);
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestFontWithVS_IgnoreVS) {
  hb_codepoint_t glyph =
      GetGlyphForCJKVSFromFontWithVS(kIgnoreVariationSelector);
  EXPECT_TRUE(glyph);
  EXPECT_NE(glyph, kUnmatchedVSGlyphId);
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestFontWithBaseCharOnly) {
  EXPECT_EQ(GetGlyphForStandardizedVSFromFontWithBaseCharOnly(
                kUseSpecifiedVariationSelector),
            kUnmatchedVSGlyphId);
}

TEST(HarfBuzzFaceTest,
     HarfBuzzGetNominalGlyph_TestFontWithBaseCharOnly_IgnoreVS) {
  hb_codepoint_t glyph = GetGlyphForStandardizedVSFromFontWithBaseCharOnly(
      kIgnoreVariationSelector);
  EXPECT_FALSE(glyph);
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestFontWithoutBaseChar) {
  UChar32 character = uchar::kFullwidthExclamationMark;
  UChar32 variation_selector = uchar::kVariationSelector2;

  Font* font = test::CreateAhemFont(11);
  EXPECT_FALSE(GetGlyphForVariationSequenceFromFont(
      font, character, variation_selector, kUseSpecifiedVariationSelector));
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestVariantEmojiEmoji) {
  UChar32 character = uchar::kShakingFaceEmoji;
  UChar32 variation_selector = 0;

  hb_codepoint_t glyph_from_font_with_vs15 = GetGlyphForEmojiVSFromFontWithVS15(
      character, variation_selector, kForceVariationSelector16);
  EXPECT_EQ(glyph_from_font_with_vs15, kUnmatchedVSGlyphId);

  hb_codepoint_t glyph_from_font_with_vs16 = GetGlyphForEmojiVSFromFontWithVS16(
      character, variation_selector, kForceVariationSelector16);
  EXPECT_TRUE(glyph_from_font_with_vs16);
  EXPECT_NE(glyph_from_font_with_vs16, kUnmatchedVSGlyphId);
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestVariantEmojiText) {
  UChar32 character = uchar::kShakingFaceEmoji;
  UChar32 variation_selector = 0;

  hb_codepoint_t glyph_from_font_with_vs15 = GetGlyphForEmojiVSFromFontWithVS15(
      character, variation_selector, kForceVariationSelector15);
  EXPECT_TRUE(glyph_from_font_with_vs15);
  EXPECT_NE(glyph_from_font_with_vs15, kUnmatchedVSGlyphId);

  hb_codepoint_t glyph_from_font_with_vs16 = GetGlyphForEmojiVSFromFontWithVS16(
      character, variation_selector, kForceVariationSelector15);
  EXPECT_EQ(glyph_from_font_with_vs16, kUnmatchedVSGlyphId);
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestVariantEmojiUnicode) {
  UChar32 character = uchar::kShakingFaceEmoji;
  UChar32 variation_selector = 0;

  hb_codepoint_t glyph_from_font_with_vs15 = GetGlyphForEmojiVSFromFontWithVS15(
      character, variation_selector, kUseUnicodeDefaultPresentation);
  EXPECT_EQ(glyph_from_font_with_vs15, kUnmatchedVSGlyphId);

  hb_codepoint_t glyph_from_font_with_vs16 = GetGlyphForEmojiVSFromFontWithVS16(
      character, variation_selector, kUseUnicodeDefaultPresentation);
  EXPECT_TRUE(glyph_from_font_with_vs16);
  EXPECT_NE(glyph_from_font_with_vs16, kUnmatchedVSGlyphId);
}

TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestVSOverrideVariantEmoji) {
  UChar32 character = uchar::kShakingFaceEmoji;
  UChar32 variation_selector = uchar::kVariationSelector15;

  hb_codepoint_t glyph_from_font_with_vs15 = GetGlyphForEmojiVSFromFontWithVS15(
      character, variation_selector, kForceVariationSelector16);
  EXPECT_TRUE(glyph_from_font_with_vs15);
  EXPECT_NE(glyph_from_font_with_vs15, kUnmatchedVSGlyphId);

  hb_codepoint_t glyph_from_font_with_vs16 = GetGlyphForEmojiVSFromFontWithVS16(
      character, variation_selector, kForceVariationSelector16);
  EXPECT_EQ(glyph_from_font_with_vs16, kUnmatchedVSGlyphId);
}

// Test emoji variation selectors support in system fallback. We are only
// enabling this feature on Windows, Android and Mac platforms.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
TEST(HarfBuzzFaceTest, HarfBuzzGetNominalGlyph_TestSystemFallbackEmojiVS) {
  UChar32 character = uchar::kShakingFaceEmoji;

  hb_codepoint_t glyph_from_font_with_vs15 = GetGlyphForEmojiVSFromFontWithVS15(
      character, uchar::kVariationSelector15, kUseSpecifiedVariationSelector);
  EXPECT_TRUE(glyph_from_font_with_vs15);
  EXPECT_NE(glyph_from_font_with_vs15, kUnmatchedVSGlyphId);

  hb_codepoint_t glyph_from_font_with_vs16 = GetGlyphForEmojiVSFromFontWithVS16(
      character, uchar::kVariationSelector16, kUseSpecifiedVariationSelector);
  EXPECT_TRUE(glyph_from_font_with_vs16);
  EXPECT_NE(glyph_from_font_with_vs16, kUnmatchedVSGlyphId);
}
#endif

}  // namespace blink
