/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "tools/fonts/FontToolUtils.h"

#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkImage.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPixelRef.h"  // IWYU pragma: keep
#include "include/core/SkStream.h"
#include "include/core/SkTypeface.h"
#include "include/private/base/SkMutex.h"
#include "include/utils/SkCustomTypeface.h"
#include "src/base/SkUTF.h"
#include "src/core/SkOSFile.h"
#include "tools/Resources.h"
#include "tools/flags/CommandLineFlags.h"
#include "tools/fonts/TestFontMgr.h"

#if defined(SK_BUILD_FOR_WIN) && (defined(SK_FONTMGR_GDI_AVAILABLE) || \
                                  defined(SK_FONTMGR_DIRECTWRITE_AVAILABLE))
#include "include/ports/SkTypeface_win.h"
#endif

#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
#include "include/ports/SkFontMgr_android.h"
#include "src/ports/SkTypeface_FreeType.h"
#endif

#if defined(SK_FONTMGR_CORETEXT_AVAILABLE) && (defined(SK_BUILD_FOR_IOS) || \
                                               defined(SK_BUILD_FOR_MAC))
#include "include/ports/SkFontMgr_mac_ct.h"
#endif

#if defined(SK_FONTMGR_FONTATIONS_AVAILABLE)
#include "include/ports/SkFontMgr_Fontations.h"
#endif

#if defined(SK_FONTMGR_FONTCONFIG_AVAILABLE)
#include "include/ports/SkFontMgr_fontconfig.h"
#endif

#if defined(SK_FONTMGR_FREETYPE_DIRECTORY_AVAILABLE)
#include "include/ports/SkFontMgr_directory.h"
#endif

#if defined(SK_FONTMGR_FREETYPE_EMPTY_AVAILABLE)
#include "include/ports/SkFontMgr_empty.h"
#endif

namespace ToolUtils {

static DEFINE_bool(nativeFonts,
                   true,
                   "If true, use native font manager and rendering. "
                   "If false, fonts will draw as portably as possible.");
#if defined(SK_BUILD_FOR_WIN)
static DEFINE_bool(gdi, false, "Use GDI instead of DirectWrite for font rendering.");
#endif
#if defined(SK_FONTMGR_FONTATIONS_AVAILABLE)
static DEFINE_bool(fontations, false, "Use Fontations for native font rendering.");
#endif

sk_sp<SkTypeface> PlanetTypeface() {
    static const sk_sp<SkTypeface> planetTypeface = []() {
        const char* filename;
#if defined(SK_BUILD_FOR_WIN)
        filename = "fonts/planetcolr.ttf";
#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
        filename = "fonts/planetsbix.ttf";
#else
        filename = "fonts/planetcbdt.ttf";
#endif
        sk_sp<SkTypeface> typeface = CreateTypefaceFromResource(filename);
        if (typeface) {
            return typeface;
        }
        return CreateTestTypeface("Planet", SkFontStyle());
    }();
    return planetTypeface;
}

EmojiTestSample EmojiSample() {
    static const EmojiTestSample emojiSample = []() {
        EmojiTestSample sample = {nullptr, ""};
#if defined(SK_BUILD_FOR_WIN)
        sample = EmojiSample(EmojiFontFormat::ColrV0);
#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
        sample = EmojiSample(EmojiFontFormat::Sbix);
#else
        sample = EmojiSample(EmojiFontFormat::Cbdt);
#endif
        if (sample.typeface) {
            return sample;
        }
        return EmojiSample(EmojiFontFormat::Test);
    }();
    return emojiSample;
}

EmojiTestSample EmojiSample(EmojiFontFormat format) {
    EmojiTestSample sample;
    sample.sampleText = "\U0001F600 \u2662";  // 😀 ♢
    switch (format) {
        case EmojiFontFormat::Cbdt:
            sample.typeface = CreateTypefaceFromResource("fonts/cbdt.ttf");
            break;
        case EmojiFontFormat::Sbix:
            sample.typeface = CreateTypefaceFromResource("fonts/sbix.ttf");
            break;
        case EmojiFontFormat::ColrV0:
            sample.typeface = CreateTypefaceFromResource("fonts/colr.ttf");
            break;
        case EmojiFontFormat::Svg:
            sample.typeface = CreateTypefaceFromResource("fonts/SampleSVG.ttf");
            sample.sampleText = "abcdefghij";
            break;
        case EmojiFontFormat::Test:
            sample.typeface = CreatePortableTypeface("Emoji", SkFontStyle());
    }
    return sample;
}

SkString NameForFontFormat(EmojiFontFormat format) {
    switch (format) {
        case EmojiFontFormat::Cbdt:
            return SkString("cbdt");
        case EmojiFontFormat::Sbix:
            return SkString("sbix");
        case EmojiFontFormat::ColrV0:
            return SkString("colrv0");
        case EmojiFontFormat::Test:
            return SkString("test");
        case EmojiFontFormat::Svg:
            return SkString("svg");
    }
    return SkString();
}

sk_sp<SkTypeface> SampleUserTypeface() {
    SkCustomTypefaceBuilder builder;
    SkFont font;
    const float upem = 200;

    {
        SkFontMetrics metrics;
        metrics.fFlags = 0;
        metrics.fTop = -200;
        metrics.fAscent = -150;
        metrics.fDescent = 50;
        metrics.fBottom = -75;
        metrics.fLeading = 10;
        metrics.fAvgCharWidth = 150;
        metrics.fMaxCharWidth = 300;
        metrics.fXMin = -20;
        metrics.fXMax = 290;
        metrics.fXHeight = -100;
        metrics.fCapHeight = 0;
        metrics.fUnderlineThickness = 5;
        metrics.fUnderlinePosition = 2;
        metrics.fStrikeoutThickness = 5;
        metrics.fStrikeoutPosition = -50;
        builder.setMetrics(metrics, 1.0f/upem);
    }
    builder.setFontStyle(SkFontStyle(367, 3, SkFontStyle::kOblique_Slant));

    const SkMatrix scale = SkMatrix::Scale(1.0f/upem, 1.0f/upem);
    for (SkGlyphID index = 0; index <= 67; ++index) {
        SkScalar width;
        width = 100;

        builder.setGlyph(index, width/upem, SkPath::Circle(50, -50, 75).makeTransform(scale));
    }

    return builder.detach();
}

sk_sp<SkTypeface> CreatePortableTypeface(const char* name, SkFontStyle style) {
    static sk_sp<SkFontMgr> portableFontMgr = MakePortableFontMgr();
    SkASSERT_RELEASE(portableFontMgr);
    sk_sp<SkTypeface> face = portableFontMgr->legacyMakeTypeface(name, style);
    SkASSERT_RELEASE(face);
    return face;
}

sk_sp<SkTypeface> DefaultPortableTypeface() {
    // At last check, the default typeface is a serif font.
    sk_sp<SkTypeface> face = CreatePortableTypeface(nullptr, SkFontStyle());
    SkASSERT_RELEASE(face);
    return face;
}

SkFont DefaultPortableFont() {
    return SkFont(DefaultPortableTypeface(), 12);
}

SkBitmap CreateStringBitmap(int w, int h, SkColor c, int x, int y, int textSize,
                            const char* str) {
    SkBitmap bitmap;
    bitmap.allocN32Pixels(w, h);
    SkCanvas canvas(bitmap);

    SkPaint paint;
    paint.setColor(c);

    SkFont font(DefaultPortableTypeface(), textSize);

    canvas.clear(0x00000000);
    canvas.drawSimpleText(str,
                          strlen(str),
                          SkTextEncoding::kUTF8,
                          SkIntToScalar(x),
                          SkIntToScalar(y),
                          font,
                          paint);

    // Tag data as sRGB (without doing any color space conversion). Color-space aware configs
    // will process this correctly but legacy configs will render as if this returned N32.
    SkBitmap result;
    result.setInfo(SkImageInfo::MakeS32(w, h, kPremul_SkAlphaType));
    result.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0);
    return result;
}

sk_sp<SkImage> CreateStringImage(int w, int h, SkColor c, int x, int y, int textSize,
                                 const char* str) {
    return CreateStringBitmap(w, h, c, x, y, textSize, str).asImage();
}

#ifndef SK_FONT_FILE_PREFIX
#  if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
#    define SK_FONT_FILE_PREFIX "/System/Library/Fonts/"
#  else
#    define SK_FONT_FILE_PREFIX "/usr/share/fonts/"
#  endif
#endif

sk_sp<SkFontMgr> TestFontMgr() {
    static sk_sp<SkFontMgr> mgr;
    static SkOnce once;
    once([] {
        if (!FLAGS_nativeFonts) {
            mgr = MakePortableFontMgr();
        }
#if defined(SK_BUILD_FOR_WIN) && defined(SK_FONTMGR_GDI_AVAILABLE)
        else if (FLAGS_gdi) {
            mgr = SkFontMgr_New_GDI();
        }
#endif
#if defined(SK_FONTMGR_FONTATIONS_AVAILABLE)
        else if (FLAGS_fontations) {
            mgr = SkFontMgr_New_Fontations_Empty();
        }
#endif
        else {
#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_FONTMGR_ANDROID_AVAILABLE)
            mgr = SkFontMgr_New_Android(nullptr, std::make_unique<SkFontScanner_FreeType>());
#elif defined(SK_BUILD_FOR_WIN) && defined(SK_FONTMGR_DIRECTWRITE_AVAILABLE)
            mgr = SkFontMgr_New_DirectWrite();
#elif defined(SK_FONTMGR_CORETEXT_AVAILABLE) && (defined(SK_BUILD_FOR_IOS) || \
                                                defined(SK_BUILD_FOR_MAC))
            mgr = SkFontMgr_New_CoreText(nullptr);
#elif defined(SK_FONTMGR_FONTCONFIG_AVAILABLE)
            mgr = SkFontMgr_New_FontConfig(nullptr);
#elif defined(SK_FONTMGR_FREETYPE_DIRECTORY_AVAILABLE)
            // In particular, this is used on ChromeOS, which is Linux-like but doesn't have
            // FontConfig.
            mgr = SkFontMgr_New_Custom_Directory(SK_FONT_FILE_PREFIX);
#elif defined(SK_FONTMGR_FREETYPE_EMPTY_AVAILABLE)
            mgr = SkFontMgr_New_Custom_Empty();
#else
            mgr = SkFontMgr::RefEmpty();
#endif
        }
        SkASSERT_RELEASE(mgr);
    });
    return mgr;
}

bool FontMgrIsGDI() {
    if (!FLAGS_nativeFonts) {
        return false;
    }
#if defined(SK_BUILD_FOR_WIN)
    if (FLAGS_gdi) {
        return true;
    }
#endif
    return false;
}

void UsePortableFontMgr() { FLAGS_nativeFonts = false; }

sk_sp<SkTypeface> DefaultTypeface() {
    return CreateTestTypeface(nullptr, SkFontStyle());
}

sk_sp<SkTypeface> CreateTestTypeface(const char* name, SkFontStyle style) {
    sk_sp<SkFontMgr> fm = TestFontMgr();
    SkASSERT_RELEASE(fm);
    sk_sp<SkTypeface> face = fm->legacyMakeTypeface(name, style);
    if (face) {
        return face;
    }
    return CreatePortableTypeface(name, style);
}

sk_sp<SkTypeface> CreateTypefaceFromResource(const char* resource, int ttcIndex) {
    sk_sp<SkFontMgr> fm = TestFontMgr();
    SkASSERT_RELEASE(fm);
    return fm->makeFromStream(GetResourceAsStream(resource), ttcIndex);
}

SkFont DefaultFont() {
    return SkFont(DefaultTypeface(), 12);
}

}  // namespace ToolUtils
