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

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "core/fxge/android/cfpf_skiafontmgr.h"

#include <algorithm>
#include <array>
#include <functional>
#include <iterator>
#include <utility>

#include "core/fxcrt/cfx_read_only_container_stream.h"
#include "core/fxcrt/compiler_specific.h"
#include "core/fxcrt/containers/adapters.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/fx_folder.h"
#include "core/fxcrt/fx_system.h"
#include "core/fxcrt/mapped_data_bytes.h"
#include "core/fxge/android/cfpf_skiafont.h"
#include "core/fxge/android/cfx_androidfontinfo.h"
#include "core/fxge/cfx_fontmgr.h"
#include "core/fxge/fx_font.h"

namespace {



constexpr int kSkiaMatchNameWeight = 62;
constexpr int kSkiaMatchSystemNameWeight = 60;
constexpr int kSkiaMatchSerifStyleWeight = 16;
constexpr int kSkiaMatchScriptStyleWeight = 8;



// Map from OS/2 table ulCodePageRange1 bits (0-31) to FX_CharsetFlag.
// See https://learn.microsoft.com/en-us/typography/opentype/spec/os2
constexpr auto kFPFSkiaFontFXCharsetFlags =
    std::to_array<const FX_CharsetFlag>({
        FX_CharsetFlag::kANSI,                   // 0 (Latin 1)
        FX_CharsetFlag::kMSWin_EasternEuropean,  // 1 (Latin 2)
        FX_CharsetFlag::kMSWin_Cyrillic,         // 2 (Cyrillic)
        FX_CharsetFlag::kMSWin_Greek,            // 3 (Greek)
        FX_CharsetFlag::kMSWin_Turkish,          // 4 (Turkish)
        FX_CharsetFlag::kMSWin_Hebrew,           // 5 (Hebrew)
        FX_CharsetFlag::kMSWin_Arabic,           // 6 (Arabic)
        FX_CharsetFlag::kMSWin_Baltic,           // 7 (Baltic)
        FX_CharsetFlag::kMSWin_Vietnamese,       // 8 (Vietnamese)
        FX_CharsetFlag::kNone,                   // 9 (Alt ANSI)
        FX_CharsetFlag::kNone,                   // 10 (Alt ANSI)
        FX_CharsetFlag::kNone,                   // 11 (Alt ANSI)
        FX_CharsetFlag::kNone,                   // 12 (Alt ANSI)
        FX_CharsetFlag::kNone,                   // 13 (Alt ANSI)
        FX_CharsetFlag::kNone,                   // 14 (Alt ANSI)
        FX_CharsetFlag::kNone,                   // 15 (Alt ANSI)
        FX_CharsetFlag::kThai,                   // 16 (Thai)
        FX_CharsetFlag::kShiftJIS,               // 17 (JIS/Japan)
        FX_CharsetFlag::kChineseSimplified,      // 18 (CN Simple)
        FX_CharsetFlag::kHangul,                 // 19 (Korean Wansung)
        FX_CharsetFlag::kChineseTraditional,     // 20 (CN Trad)
        FX_CharsetFlag::kJohab,                  // 21 (Korean Johab)
        FX_CharsetFlag::kNone,                   // 22 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 23 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 24 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 25 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 26 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 27 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 28 (Alt ANSI/OEM)
        FX_CharsetFlag::kNone,                   // 29 (Mac US Roman)
        FX_CharsetFlag::kOEM,                    // 30 (OEM)
        FX_CharsetFlag::kSymbol,                 // 31 (Symbol)
    });

Mask<FX_CharsetFlag> SkiaGetFaceCharsets(uint32_t code_range) {
  static_assert(static_cast<uint32_t>(FX_CharsetFlag::kNone) == 0,
                "FX_CharsetFlag::kNone must be 0 for ORing to be a no-op");
  Mask<FX_CharsetFlag> charsets;
  for (int32_t i = 0; i < 32; i++) {
    if (code_range & (1U << i)) {
      charsets |= kFPFSkiaFontFXCharsetFlags[i];
    }
  }
  return charsets;
}

uint32_t GetFamilyHash(ByteStringView family,
                       uint32_t style,
                       FX_Charset charset) {
  ByteString font(family);
  if (FontStyleIsForceBold(style)) {
    font += "Bold";
  }
  if (FontStyleIsItalic(style)) {
    font += "Italic";
  }
  if (FontStyleIsSerif(style)) {
    font += "Serif";
  }
  font += static_cast<uint8_t>(charset);
  return FX_HashCode_GetA(font.AsStringView());
}

bool SkiaIsCJK(FX_Charset charset) {
  return FX_CharSetIsCJK(charset);
}



}  // namespace

CFPF_SkiaFontMgr::CFPF_SkiaFontMgr() = default;

CFPF_SkiaFontMgr::~CFPF_SkiaFontMgr() = default;

CFPF_SkiaFontMgr::Entry::Entry() = default;

CFPF_SkiaFontMgr::Entry::~Entry() = default;

void CFPF_SkiaFontMgr::LoadFonts(const char** user_paths) {
  if (loaded_fonts_) {
    return;
  }

  ScanPath("/system/fonts");

  if (user_paths) {
    // SAFETY: nullptr-terminated array required from caller.
    UNSAFE_BUFFERS({
      for (const char** path = user_paths; *path; ++path) {
        ScanPath(*path);
      }
    });
  }

  loaded_fonts_ = true;
}

CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(ByteStringView family_name,
                                            FX_Charset charset,
                                            uint32_t style) {
  const uint32_t hash = GetFamilyHash(family_name, style, charset);
  auto family_iter = family_font_map_.find(hash);
  if (family_iter != family_font_map_.end()) {
    return family_iter->second.get();
  }

  const uint32_t face_name_hash = SkiaNormalizeFontName(family_name);
  const char* subst_name = SkiaGetSubstFont(face_name_hash, GetSkiaFontmap());
  const char* subst_sans_name =
      SkiaGetSubstFont(face_name_hash, GetSkiaSansFontMap());
  const uint32_t subst_hash =
      subst_name ? SkiaNormalizeFontName(subst_name) : 0;
  const uint32_t subst_sans_hash =
      subst_sans_name ? SkiaNormalizeFontName(subst_sans_name) : 0;
  const bool maybe_symbol = SkiaMaybeSymbol(family_name);
  if (charset != FX_Charset::kMSWin_Arabic && SkiaMaybeArabic(family_name)) {
    charset = FX_Charset::kMSWin_Arabic;
  } else if (charset == FX_Charset::kANSI) {
    charset = FX_Charset::kDefault;
  }
  int32_t expected_score = kSkiaMatchNameWeight +
                           kSkiaMatchSerifStyleWeight * 3 +
                           kSkiaMatchScriptStyleWeight * 2;
  const Entry* best_font = nullptr;
  int32_t best_score = -1;
  int32_t best_glyph_num = 0;
  for (const std::unique_ptr<Entry>& font : pdfium::Reversed(font_faces_)) {
    if (charset != FX_Charset::kDefault) {
      FX_CharsetFlag flag = FX_CharsetFlagForCharset(charset);
      if (flag != FX_CharsetFlag::kNone && !(font->charsets & flag)) {
        continue;
      }
    }
    int32_t score = 0;
    const uint32_t sys_font_name_hash =
        SkiaNormalizeFontName(font->family.AsStringView());
    if (face_name_hash == sys_font_name_hash) {
      score += kSkiaMatchNameWeight;
    }
    bool matches_name = (score == kSkiaMatchNameWeight);
    if (FontStyleIsForceBold(style) == FontStyleIsForceBold(font->style)) {
      score += kSkiaMatchSerifStyleWeight;
    }
    if (FontStyleIsItalic(style) == FontStyleIsItalic(font->style)) {
      score += kSkiaMatchSerifStyleWeight;
    }
    if (FontStyleIsFixedPitch(style) == FontStyleIsFixedPitch(font->style)) {
      score += kSkiaMatchScriptStyleWeight;
    }
    if (FontStyleIsSerif(style) == FontStyleIsSerif(font->style)) {
      score += kSkiaMatchSerifStyleWeight;
    }
    if (FontStyleIsScript(style) == FontStyleIsScript(font->style)) {
      score += kSkiaMatchScriptStyleWeight;
    }
    if (subst_hash == sys_font_name_hash ||
        subst_sans_hash == sys_font_name_hash) {
      score += kSkiaMatchSystemNameWeight;
      matches_name = true;
    }
    if (charset == FX_Charset::kDefault || maybe_symbol) {
      if (score > best_score && matches_name) {
        best_score = score;
        best_font = font.get();
      }
    } else if (SkiaIsCJK(charset)) {
      if (matches_name || font->glyph_num > best_glyph_num) {
        best_font = font.get();
        best_glyph_num = font->glyph_num;
      }
    } else if (score > best_score) {
      best_score = score;
      best_font = font.get();
    }
    if (score >= expected_score) {
      best_font = font.get();
      break;
    }
  }
  if (!best_font) {
    return nullptr;
  }

  auto face = GetFontFace(best_font->path, best_font->face_index);
  if (!face) {
    return nullptr;
  }
  auto font = std::make_unique<CFPF_SkiaFont>(std::move(face), charset);

  CFPF_SkiaFont* ret = font.get();
  family_font_map_[hash] = std::move(font);
  return ret;
}

RetainPtr<CFX_Face> CFPF_SkiaFontMgr::GetFontFace(const ByteString& path,
                                                  int32_t face_index) {
  auto mapped_bytes = fxcrt::MappedDataBytes::Create(path);
  if (!mapped_bytes) {
    return nullptr;
  }
  return CFX_Face::New(nullptr,
                       pdfium::MakeRetain<CFX_ReadOnlyMappedDataBytesStream>(
                           std::move(mapped_bytes)),
                       face_index);
}

void CFPF_SkiaFontMgr::ScanPath(const ByteString& path) {
  std::unique_ptr<FX_Folder> handle = FX_Folder::OpenFolder(path);
  if (!handle) {
    return;
  }

  ByteString filename;
  bool is_folder = false;
  while (handle->GetNextFile(&filename, &is_folder)) {
    if (is_folder) {
      if (filename == "." || filename == "..") {
        continue;
      }
    } else {
      ByteString ext = filename.Last(4);
      ext.MakeLower();
      if (ext != ".ttf" && ext != ".ttc" && ext != ".otf") {
        continue;
      }
    }
    ByteString fullpath(path);
    fullpath += "/";
    fullpath += filename;
    if (is_folder) {
      ScanPath(fullpath);
    } else {
      ScanFile(fullpath);
    }
  }
}

void CFPF_SkiaFontMgr::ScanFile(const ByteString& file) {
  constexpr int kFaceIndex = 0;
  RetainPtr<CFX_Face> face = GetFontFace(file, kFaceIndex);
  if (!face) {
    return;
  }
  font_faces_.push_back(ReportFace(face, file, kFaceIndex));
}

std::unique_ptr<CFPF_SkiaFontMgr::Entry> CFPF_SkiaFontMgr::ReportFace(
    RetainPtr<CFX_Face> face,
    const ByteString& file,
    int face_index) {
  Mask<FX_CharsetFlag> charsets;
  std::optional<std::array<uint32_t, 2>> cp_range = face->GetOs2CodePageRange();
  if (cp_range.has_value()) {
    charsets |= SkiaGetFaceCharsets(cp_range.value()[0]);
  }
  auto entry = std::make_unique<Entry>();
  entry->path = file;
  entry->family = face->GetFamilyName();
  entry->style = face->GetFontStyle();
  entry->face_index = face_index;
  entry->charsets = charsets;
  entry->glyph_num = face->GetGlyphCount();
  return entry;
}
