/*
  ==============================================================================

   This file is part of the JUCE framework.
   Copyright (c) Raw Material Software Limited

   JUCE is an open source framework subject to commercial or open source
   licensing.

   By downloading, installing, or using the JUCE framework, or combining the
   JUCE framework with any other source code, object code, content or any other
   copyrightable work, you agree to the terms of the JUCE End User Licence
   Agreement, and all incorporated terms including the JUCE Privacy Policy and
   the JUCE Website Terms of Service, as applicable, which will bind you. If you
   do not agree to the terms of these agreements, we will not license the JUCE
   framework to you, and you must discontinue the installation or download
   process and cease use of the JUCE framework.

   JUCE End User Licence Agreement: https://juce.com/legal/juce-9-licence/
   JUCE Privacy Policy: https://juce.com/juce-privacy-policy
   JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/

   Or:

   You may also use this code under the terms of the AGPLv3:
   https://www.gnu.org/licenses/agpl-3.0.en.html

   THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
   WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

/** A single path-based layer of a colour glyph. Contains the glyph shape and the colour in which
    the shape should be painted.

    @tags{Graphics}
*/
struct ColourLayer
{
    EdgeTable clip;
    std::optional<Colour> colour; ///< nullopt indicates 'foreground'
};

/** A bitmap representing (part of) a glyph, most commonly used to represent colour emoji glyphs.

    @tags{Graphics}
*/
struct ImageLayer
{
    Image image;
    AffineTransform transform;
};

/** A single layer that makes up part of a glyph image.

    @tags{Graphics}
*/
struct GlyphLayer
{
    std::variant<ColourLayer, ImageLayer> layer;
};

/** Identifiers for different styles of typeface metrics.

    In new projects, it's recommended to use the 'portable' metrics kind, so that fonts display
    at a similar size on all platforms.
    Portable metrics are enabled by default when constructing a Font using FontOptions. The old,
    deprecated Font constructors will all request the legacy metrics kind instead.
    JUCE components that display text will query LookAndFeel::getDefaultMetricsKind() to find the
    kind of metrics that should be used. This function can be overridden to switch the metrics kind
    for the entire LookAndFeel.

    The 'legacy' metrics kind uses the platform font facilities to retrieve font metrics.
    Each platform has its own idiosyncratic behaviour when computing metrics - depending on the
    typeface data, it's possible that the 'portable' metrics will differ from the 'legacy' metrics
    on every platform. The biggest differences between legacy and portable metrics are likely to be
    seen on Windows, so it may be helpful to check this platform first.
*/
enum class TypefaceMetricsKind
{
    legacy,         ///< Old-style metrics that may differ for the same font file when running on different platforms.
                    ///< This was the default behaviour prior to JUCE 8.
    portable        ///< Where possible, will return the same font metrics on all platforms.
                    ///< It's a good idea to use this for new JUCE projects, to keep text layout and
                    ///< sizing consistent on all platforms.
};

/** Font metrics using JUCE conventions.

    @tags{Graphics}
*/
struct TypefaceMetrics
{
    /** The proportion of the typeface's height that is above the baseline, as a value between 0 and 1.
        Note that 'height' here refers to the result of adding the absolute ascent and descent values.
        That is, the sum of the ascent and descent will equal 1.
        The sum of the ascent and descent will normally differ from the em size of the font.
        That is, for a font size of 14pt, there will be 14 points per em, but the sum of the ascent
        and descent in points is unlikely to be equal to 14.
    */
    float ascent{};

    /** The factor by which a JUCE font height should be multiplied in order to convert to a font
        size in points.

        May be inf if the font ascent and descent overrides have both been set to 0!
    */
    float heightToPoints{};

    /** This value reflects the font designer's intent when laying out multiple lines of text. It is
        a proportion of the typeface's height that should be used in addition to the ascent and
        descent between successive lines.
    */
    float lineGap{};
};

//==============================================================================
/**
    A typeface represents a size-independent font.

    This base class is abstract, but calling createSystemTypefaceFor() will return
    a platform-specific subclass that can be used.

    Normally you should never need to deal directly with Typeface objects - the Font
    class does everything you typically need for rendering text.

    @see Font

    @tags{Graphics}
*/
class JUCE_API  Typeface  : public ReferenceCountedObject
{
public:
    //==============================================================================
    /** A handy typedef for a pointer to a typeface. */
    using Ptr = ReferenceCountedObjectPtr<Typeface>;

    //==============================================================================
    /** Returns the font family of the typeface.
        @see Font::getTypefaceName
    */
    const String& getName() const noexcept             { return name; }

    //==============================================================================
    /** Returns the font style of the typeface.
        @see Font::getTypefaceStyle
    */
    const String& getStyle() const noexcept            { return style; }

    //==============================================================================
    /** Creates a new system typeface. */
    static Ptr createSystemTypefaceFor (const Font& font);

    /** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image).
        The system will take its own internal copy of the data, so you can free the block once
        this method has returned.

        The typeface will remain registered with the system for as long as there is at least one
        owner of the returned Ptr. This allows typefaces registered with createSystemTypefaceFor to
        be created using just a typeface family name, e.g. in font fallback lists.
    */
    static Ptr createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize);

    /** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image).
        The system will take its own internal copy of the data.

        The typeface will remain registered with the system for as long as there is at least one
        owner of the returned Ptr. This allows typefaces registered with createSystemTypefaceFor to
        be created using just a typeface family name, e.g. in font fallback lists.
    */
    static Ptr createSystemTypefaceFor (Span<const std::byte>);

    //==============================================================================
    /** Destructor. */
    ~Typeface() override;

    /** Returns information about the horizontal metrics of this font. */
    [[nodiscard]] TypefaceMetrics getMetrics (TypefaceMetricsKind) const;

    /** Returns the outline for a glyph.
        The path returned will be normalised to a point size of 1.0.
    */
    void getOutlineForGlyph (int glyphNumber, Path& path) const;

    /** Returns glyph bounds, normalised to a point size of 1.0. */
    Rectangle<float> getGlyphBounds (int glyphNumber) const;

    /** Returns the layers that should be painted in order to display this glyph.

        Layers should be painted in the same order as they are returned, i.e. layer[0], layer[1] etc.

        This should generally be preferred to getEdgeTableForGlyph, as it is more flexible.
        Currently, this only supports COLRv0 and bitmap fonts (no SVG or COLRv1).
        Support for SVG and COLRv1 may be added in the future, depending on demand. However, this
        would require significant additions to JUCE's rendering code, so it has been omitted for
        now.
    */
    std::vector<GlyphLayer> getLayersForGlyph (int glyphNumber,
                                               const AffineTransform&) const;

    /** Kinds of colour glyph format that may be implemented by a particular typeface.
        Most typefaces are monochromatic, and do not support any colour formats.
        Emoji fonts are likely to implement one or more colour font formats.

        At this time, JUCE is able to render only bitmap and COLRv0 fonts.
        If you allow users to customise fonts, you may wish to hide or otherwise prevent users from
        selecting fonts that use unsupported colour formats.
    */
    enum ColourGlyphFormat
    {
        colourGlyphFormatBitmap  = 1 << 0, ///< The typeface includes glyphs represented as bitmaps (normally PNGs)
        colourGlyphFormatSvg     = 1 << 1, ///< The typeface includes glyphs represented as SVGs
        colourGlyphFormatCOLRv0  = 1 << 2, ///< The typeface uses the COLRv0 format, with support for flat colours
        colourGlyphFormatCOLRv1  = 1 << 3, ///< The typeface uses the COLRv1 format, with support for gradients and blending modes
    };

    /** Returns an int with bits set indicating the format of colour glyphs contained in the typeface.

        If the typeface has no colour glyphs, no bits will be set. Otherwise, one or more bits will
        be set depending on the format of the colour glyph information. You can use a bitwise-and
        operation with the members of the ColourGlyphFormat enum to determine whether a particular
        format is supported.

        @code
        const auto isMonochrome   = typeface->getColourGlyphFormats() == 0;
        const auto isSvg          = (typeface->getColourGlyphFormats() & Typeface::colourGlyphFormatSvg) != 0;
        const auto isSimpleColour = (typeface->getColourGlyphFormats() & (Typeface::colourGlyphFormatBitmap | Typeface::colourGlyphFormatCOLRv0)) != 0;
        @endcode
    */
    int getColourGlyphFormats() const;

    //==============================================================================
    /** Changes the number of fonts that are cached in memory. */
    static void setTypefaceCacheSize (int numFontsToCache);

    /** Clears any fonts that are currently cached in memory. */
    static void clearTypefaceCache();

    /** On some platforms, this allows a specific path to be scanned.
        On macOS you can load .ttf and .otf files, otherwise this is only available when using FreeType.
    */
    static void scanFolderForFonts (const File& folder);

    /** Returns the glyph index corresponding to the provided codepoint, or nullopt if no
        such glyph is found.
    */
    std::optional<uint32_t> getNominalGlyphForCodepoint (juce_wchar) const;

    /** Attempts to locate a font with a similar style that is capable of displaying the requested
        string.

        This uses system facilities, so will produce different results depending on the operating
        system and installed fonts. If it's important that your app uses the same fonts on all
        platforms, then you probably shouldn't use the results of this function.

        Note that this accepts a _string_ instead of a single codepoint because the OS may take
        combining marks and variation selectors into account when selecting an appropriate font.
        As an example, many fonts include a 'text'/'outline' version of the smiley face emoji.
        macOS may return Helvetica if the smiley emoji codepoint is passed on its own, but will
        return the emoji font if the emoji codepoint is followed by the variation-selector-16
        codepoint.

        To specify your own fallback fonts:
        - ensure your preferred fonts provide coverage of all languages/scripts/codepoints that you may
          need to display
        - bundle the fonts in your product, e.g. as binary data and register them when your product starts
        - use Font::setPreferredFallbackFamilies() to specify that the bundled fonts should be used before
          requesting a fallback font from the system

        @param text         the returned font will normally be capable of displaying the majority
                            of codepoints in this string
        @param language     BCP 47 language code of the text that includes this codepoint
        @returns nullptr if no fallback could be created
    */
    virtual Typeface::Ptr createSystemFallback (const String& text, const String& language) const = 0;

    //==============================================================================
    /** Creates and returns a copy of this typeface configured with the specified variable font settings.

        Variable fonts (also known as OpenType Font Variations) allow a single font file to contain
        multiple design variations along one or more axes, such as weight, width, slant, optical size,
        and custom axes defined by the font designer. This method creates a new typeface instance with
        the specified axis values applied. Any variable settings applied to the original typeface will
        be replaced in the returned instance.

        This method does not check or cache typefaces, calling this twice with the same settings will
        return a unique pointer.

        @param settings     the variable settings to apply to configure the new typeface instance.
        @returns            A unique Typeface::Ptr or nullptr if this typeface is not a variable typeface.

        @see getSupportedVariables, getRangeForVariable, getDefaultValueForVariable
    */
    [[nodiscard]] virtual Typeface::Ptr cloneWithVariableSettings (Span<const FontVariableSetting> settings) const = 0;

    /** Returns the system's default UI font.

        This will differ depending on the platform.

        On Linux/fontconfig, this returns the typeface mapped to the name "system-ui",
        or nullptr if no such font exists.

        On Windows, this queries SystemParametersInfo with the key SPI_GETNONCLIENTMETRICS,
        and returns the lfMessageFont that is returned, or nullptr if the font cannot be found.

        On macOS and iOS, this returns the result of CTFontCreateUIFontForLanguage() for
        the kCTFontUIFontSystem typeface.

        On Android 29+, this will use AFontMatcher to return the "system-ui" font. On earlier
        Android versions, this will attempt to return the Roboto font.

        NOTE: The metrics of the system typeface may be significantly different from the metrics of
        the sans-serif font that JUCE would normally select to be the default font. This is
        especially evident on Windows: For Segoe UI (the Windows system typeface)
        the sum of ascender and descender is somewhat larger than the em-size of the font,
        but for Verdana (the JUCE default sans-serif font on Windows) the sum of ascender and
        descender is closer to the em-size. When the size of a font is set via
        FontOptions::withHeight() or Font::setHeight(), JUCE will scale fonts based on the sum of
        ascender and descender, so switching to Segoe UI might cause text to render at a much
        smaller size than with Verdana. You may get better results by setting font sizes in points
        using FontOptions::withFontHeight() and Font::setPointHeight(). When using points, Segoe UI
        still renders slightly smaller than Verdana, but the differences are less pronounced.
    */
    static Typeface::Ptr findSystemTypeface();

    /** Returns the OpenType features supported by this typeface.

        This method returns a list of all OpenType font features (such as ligatures,
        small caps, stylistic alternates, etc.) that are available in the current
        typeface.

        @see FontFeatureTag, FontFeatureSetting
    */
    std::vector<FontFeatureTag> getSupportedFeatures() const;

    //==============================================================================
    /** Returns the OpenType variable settings supported by this typeface.

        The returned tags are in sorted order.

        @see FontFeatureTag, FontFeatureSetting, cloneWithVariableSettings
    */
    [[nodiscard]] Span<const FontFeatureTag> getSupportedVariables() const&;
    [[nodiscard]] Span<const FontFeatureTag> getSupportedVariables() const&& = delete;

    /** Returns the default value for a specific variable font axis, or std::nullopt if the variable
        is not supported by this font.

        Variable fonts define a default value for each axis, which represents the normal or
        regular style of the typeface. For example, a variable font's 'wght' (weight) axis
        might have a default value of 400 (Regular weight).

        @see getSupportedVariables, getRangeForVariable, cloneWithVariableSettings
    */
    [[nodiscard]] std::optional<float> getDefaultValueForVariable (FontFeatureTag variableTag) const;

    /** Returns the valid range for a specific variable font axis, or std::nullopt if the variable
        is not supported by this font.

        @see getSupportedVariables, getDefaultValueForVariable, cloneWithVariableSettings
    */
    [[nodiscard]] std::optional<Range<float>> getRangeForVariable (FontFeatureTag variableTag) const;

    /** Returns the predefined named instances supported by this variable typeface. */
    [[nodiscard]] Span<const String> getInstanceNames() const&;
    [[nodiscard]] Span<const String> getInstanceNames() const&& = delete;

    /** Returns the variable font axis settings for a specific named instance.

        Many variable fonts include predefined named instances that represent common design
        variations, such as "Bold", "Light", "Condensed", etc. Each named instance is a
        collection of axis values that together define a specific style.

        Named instances provide a convenient way to access commonly-used variations without
        needing to know the specific axis values.

        @param instanceName  The name of the instance to query. Use getInstanceNames() to get
                             the available instance names for this typeface.

        @returns A span of FontVariableSetting objects that define the axis values for this
                 named instance. The settings will be sorted by tag. Returns an empty span
                 if the instance name is not found or if this is not a variable font.

        @see getInstanceNames, getSupportedVariables, cloneWithVariableSettings
    */
    [[nodiscard]] Span<const FontVariableSetting> getNamedInstanceConfiguration (StringRef instanceName) const&;
    [[nodiscard]] Span<const FontVariableSetting> getNamedInstanceConfiguration (StringRef instanceName) const&& = delete;

    /** Returns the OpenType variable settings this typeface was configured with.

        The returned variables will be sorted by tag.
    */
    [[nodiscard]] Span<const FontVariableSetting> getConfiguredVariables() const&;
    [[nodiscard]] Span<const FontVariableSetting> getConfiguredVariables() const&& = delete;

    /** @internal */
    class Native;

    /** @internal */
    virtual const Native* getNativeDetails() const = 0;

protected:
    /** @internal */
    Typeface (const String&, const String&);

private:
    static Ptr createFromDataImpl (Span<const std::byte>);
    static Ptr createFromFontImpl (const Font&);

    //==============================================================================
    String name;
    String style;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Typeface)
};

} // namespace juce
