/*
 * Copyright (C) 2006, 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2007-2008 Torch Mobile, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_H_

#include <limits.h>

#include <memory>
#include <string>

#include "base/gtest_prod_util.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
#include "third_party/blink/renderer/platform/fonts/font_data_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
#include "third_party/blink/renderer/platform/fonts/font_platform_data_cache.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkRefCnt.h"

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "ui/gfx/font_fallback_linux.h"
#endif

#if BUILDFLAG(IS_MAC)
#include "third_party/blink/renderer/platform/fonts/mac/character_fallback_cache.h"
#endif

class SkString;
class SkTypeface;

namespace blink {

class FontDescription;
class FontFaceCreationParams;
class FontFallbackMap;
class FontGlobalContext;
class FontPlatformData;
class SimpleFontData;
class WebFontPrewarmer;

enum class AlternateFontName {
  kAllowAlternate,
  kNoAlternate,
  kLocalUniqueFace,
  kLastResort
};

// "und-Zsye" and "und-Zsym", the special locale for retrieving the color emoji
// font and text emoji font correspondingly defined in UTS #51:
// https://unicode.org/reports/tr51/#Emoji_Script
extern const char kColorEmojiLocale[];
extern const char kMonoEmojiLocale[];

#if BUILDFLAG(IS_ANDROID)
extern const char kNotoColorEmojiCompat[];
#endif

class PLATFORM_EXPORT FontCache final {
  DISALLOW_NEW();
 public:
  // FontCache initialisation on Windows depends on a global FontMgr being
  // configured through a call from the browser process. CreateIfNeeded helps
  // avoid early creation of a font cache when these globals have not yet
  // been set.
  static FontCache& Get();

  void Trace(Visitor*) const;

  const SimpleFontData* FallbackFontForCharacter(
      const FontDescription&,
      UChar32,
      const SimpleFontData* font_data_to_substitute,
      FontFallbackPriority = FontFallbackPriority::kText);

  const SimpleFontData* GetFontData(
      const FontDescription&,
      const AtomicString&,
      AlternateFontName = AlternateFontName::kAllowAlternate);
  const SimpleFontData* GetLastResortFallbackFont(const FontDescription&);

  // Should be used in determining whether family names listed in font-family:
  // ... are available locally. Only returns true if family name matches.
  bool IsPlatformFamilyMatchAvailable(const FontDescription&,
                                      const AtomicString& family);

  // Should be used in determining whether the <abc> argument to local in
  // @font-face { ... src: local(<abc>) } are available locally, which should
  // match Postscript name or full font name. Compare
  // https://drafts.csswg.org/css-fonts-3/#src-desc
  // TODO crbug.com/627143 complete this and actually look at the right
  // namerecords.
  bool IsPlatformFontUniqueNameMatchAvailable(
      const FontDescription&,
      const AtomicString& unique_font_name);

  static String FirstAvailableOrFirst(const String&);

  void AddClient(FontCacheClient*);

  void Invalidate();

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
  static WebFontPrewarmer* GetFontPrewarmer() { return prewarmer_; }
  static void SetFontPrewarmer(WebFontPrewarmer* prewarmer) {
    prewarmer_ = prewarmer;
  }
  static void PrewarmFamily(const AtomicString& family_name);
#else
  static void PrewarmFamily(const AtomicString& family_name) {}
#endif

  static void MaybePreloadSystemFonts();

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  // These are needed for calling QueryRenderStyleForStrike, since
  // gfx::GetFontRenderParams makes distinctions based on DSF.
  static float DeviceScaleFactor() { return device_scale_factor_; }
  static void SetDeviceScaleFactor(float device_scale_factor) {
    device_scale_factor_ = device_scale_factor;
  }
#endif

#if !BUILDFLAG(IS_MAC)
  static const AtomicString& SystemFontFamily();
#else
  static const AtomicString& LegacySystemFontFamily();
  static void InvalidateFromAnyThread();
  bool IsFontFamilyUnavailable(const AtomicString& family_name) const;
  void MarkFontFamilyAsUnavailable(const AtomicString& family_name);
#endif

#if !BUILDFLAG(IS_MAC)
  static void SetSystemFontFamily(const AtomicString&);
#endif

#if BUILDFLAG(IS_WIN)
  // TODO(https://crbug.com/808221) System font style configuration is not
  // related to FontCache. Move it somewhere else, e.g. to WebThemeEngine.
  static bool AntialiasedTextEnabled() { return antialiased_text_enabled_; }
  static bool LcdTextEnabled() { return lcd_text_enabled_; }
  static void SetAntialiasedTextEnabled(bool enabled) {
    antialiased_text_enabled_ = enabled;
  }
  static void SetLCDTextEnabled(bool enabled) { lcd_text_enabled_ = enabled; }
  // Functions to cache and retrieve the system font metrics.
  static void SetMenuFontMetrics(const AtomicString& family_name,
                                 int32_t font_height);
  static void SetSmallCaptionFontMetrics(const AtomicString& family_name,
                                         int32_t font_height);
  static void SetStatusFontMetrics(const AtomicString& family_name,
                                   int32_t font_height);
  static int32_t MenuFontHeight() { return menu_font_height_; }
  static const AtomicString& MenuFontFamily() {
    return *menu_font_family_name_;
  }
  static int32_t SmallCaptionFontHeight() { return small_caption_font_height_; }
  static const AtomicString& SmallCaptionFontFamily() {
    return *small_caption_font_family_name_;
  }
  static int32_t StatusFontHeight() { return status_font_height_; }
  static const AtomicString& StatusFontFamily() {
    return *status_font_family_name_;
  }

  const SimpleFontData* GetFallbackFamilyNameFromHardcodedChoices(
      const FontDescription&,
      UChar32 codepoint,
      FontFallbackPriority fallback_priority);

  const SimpleFontData* GetDWriteFallbackFamily(
      const FontDescription&,
      UChar32 codepoint,
      FontFallbackPriority fallback_priority);
#endif  // BUILDFLAG(IS_WIN)

  static void AcceptLanguagesChanged(const String&);

#if BUILDFLAG(IS_ANDROID)
  static AtomicString GetGenericFamilyNameForScript(
      const AtomicString& family_name,
      const AtomicString& generic_family_name_fallback,
      const FontDescription&);
  // Locale-specific families can use different |SkTypeface| for a family name
  // if locale is different.
  static const char* GetLocaleSpecificFamilyName(
      const AtomicString& family_name);
  sk_sp<SkTypeface> CreateLocaleSpecificTypeface(
      const FontDescription& font_description,
      const char* locale_family_name);
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  static bool GetFontForCharacter(UChar32,
                                  const char* preferred_locale,
                                  gfx::FallbackFontData*);
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

  const SimpleFontData* FontDataFromFontPlatformData(
      const FontPlatformData*,
      bool subpixel_ascent_descent = false);

  static void CrashWithFontInfo(const FontDescription*);

  FontFallbackMap& GetFontFallbackMap();

  FontCache(const FontCache&) = delete;
  FontCache& operator=(const FontCache&) = delete;
  ~FontCache();

 private:
  // BCP47 list used when requesting fallback font for a character.
  // inlineCapacity is set to 4: the array vector not need to hold more than 4
  // elements.
  using Bcp47Vector = Vector<const char*, 4>;

  const SimpleFontData* PlatformFallbackFontForCharacter(
      const FontDescription&,
      UChar32,
      const SimpleFontData* font_data_to_substitute,
      FontFallbackPriority = FontFallbackPriority::kText);
  sk_sp<SkTypeface> CreateTypefaceFromUniqueName(
      const FontFaceCreationParams& creation_params);

  static Bcp47Vector GetBcp47LocaleForRequest(
      const FontDescription& font_description,
      FontFallbackPriority fallback_priority);

  friend class FontGlobalContext;
  FontCache();

  // FIXME: This method should eventually be removed.
  const FontPlatformData* GetFontPlatformData(
      const FontDescription&,
      const FontFaceCreationParams&,
      AlternateFontName = AlternateFontName::kAllowAlternate);
#if !BUILDFLAG(IS_MAC)
  const FontPlatformData* SystemFontPlatformData(const FontDescription&);
#endif  // !BUILDFLAG(IS_MAC)

  // These methods are implemented by each platform.
  const FontPlatformData* CreateFontPlatformData(
      const FontDescription&,
      const FontFaceCreationParams&,
      float font_size,
      AlternateFontName = AlternateFontName::kAllowAlternate);

  sk_sp<SkTypeface> CreateTypeface(const FontDescription&,
                                   const FontFaceCreationParams&,
                                   std::string& name);

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
  // SkFontMgr_FCI::onMatchFamilyStyleCharacter always crashes.
  static const FontPlatformData* CreateFontPlatformDataForCharacter(
      SkFontMgr*,
      UChar32,
      const FontDescription&,
      const char* family_name,
      FontFallbackPriority);
#endif

  static sk_sp<SkTypeface> MatchFamilyStyle(const char* family_name,
                                            const SkFontStyle&);

  static sk_sp<SkTypeface> MatchFamilyStyleCharacter(const char* family_name,
                                                     const SkFontStyle&,
                                                     const char* bcp47[],
                                                     int bcp47_count,
                                                     UChar32 character);

  const SimpleFontData* FallbackOnStandardFontStyle(const FontDescription&,
                                                    UChar32);

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
  static WebFontPrewarmer* prewarmer_;
#endif

#if BUILDFLAG(IS_WIN)
  static bool antialiased_text_enabled_;
  static bool lcd_text_enabled_;
  // The system font metrics cache.
  static AtomicString* menu_font_family_name_;
  static int32_t menu_font_height_;
  static AtomicString* small_caption_font_family_name_;
  static int32_t small_caption_font_height_;
  static AtomicString* status_font_family_name_;
  static int32_t status_font_height_;
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  static float device_scale_factor_;
#endif

  HeapHashSet<WeakMember<FontCacheClient>> font_cache_clients_;
  FontPlatformDataCache font_platform_data_cache_;

  FontDataCache font_data_cache_;

  Member<FontFallbackMap> font_fallback_map_;

#if BUILDFLAG(IS_MAC)
  CharacterFallbackCache character_fallback_cache_;
  HashSet<AtomicString> unavailable_font_families_;
#endif

  friend class SimpleFontData;  // For fontDataFromFontPlatformData
  friend class FontFallbackList;
  friend class FontPlatformDataCache;
  friend class FontCacheMacTest;
  FRIEND_TEST_ALL_PREFIXES(FontCacheAndroidTest, LocaleSpecificTypeface);
};

AtomicString ToAtomicString(const SkString&);

#if BUILDFLAG(IS_ANDROID)
// TODO(crbug.com/1241875) Can this be simplified?
// static
inline const char* FontCache::GetLocaleSpecificFamilyName(
    const AtomicString& family_name) {
  // Only `serif` has `fallbackFor` according to the current `fonts.xml`.
  if (family_name == font_family_names::kSerif)
    return "serif";
  return nullptr;
}
#endif

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_H_
