/***************************************************************************
 *   crengine-ng                                                           *
 *   Copyright (C) 2007-2013 Vadim Lopatin <coolreader.org@gmail.com>      *
 *   Copyright (C) 2015 Yifei(Frank) ZHU <fredyifei@gmail.com>             *
 *   Copyright (C) 2016 Hzj_jie <hzj_jie@hotmail.com>                      *
 *   Copyright (C) 2018 NiLuJe <ninuje@gmail.com>                          *
 *   Copyright (C) 2020 Konstantin Potapov <pkbo@users.sourceforge.net>    *
 *   Copyright (C) 2018-2021 poire-z <poire-z@users.noreply.github.com>    *
 *   Copyright (C) 2018-2022 Aleksey Chernov <valexlin@gmail.com>          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License           *
 *   as published by the Free Software Foundation; either version 2        *
 *   of the License, or (at your option) any later version.                *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,            *
 *   MA 02110-1301, USA.                                                   *
 ***************************************************************************/

/**
 * \file lvfreetypeface.h
 * \brief FreeType font interface
 */

#ifndef __LV_FREETYPEFACE_H_INCLUDED__
#define __LV_FREETYPEFACE_H_INCLUDED__

#include <crsetup.h>
#include <lvfont.h>
#include <lvthread.h>
#include <lvarray.h>

#include "lvfontglyphcache.h"

#if (USE_FREETYPE == 1)

#include <ft2build.h>
#include FT_FREETYPE_H

#if USE_HARFBUZZ == 1

#include <hb.h>
#include <hb-ft.h>
#include "lvhashtable.h"

#endif

#define CACHED_UNSIGNED_METRIC_NOT_SET 0xFFFF
class LVFontGlyphUnsignedMetricCache
{
private:
    static const int COUNT = 360;
    lUInt16* ptrs[COUNT]; //support up to 0X2CFFF=360*512-1
public:
    lUInt16 get(lChar32 ch) {
        FONT_GLYPH_CACHE_GUARD
        int inx = (ch >> 9) & 0x1ff;
        if (inx >= COUNT)
            return CACHED_UNSIGNED_METRIC_NOT_SET;
        lUInt16* ptr = ptrs[inx];
        if (!ptr)
            return CACHED_UNSIGNED_METRIC_NOT_SET;
        return ptr[ch & 0x1FF];
    }
    void put(lChar32 ch, lUInt16 m) {
        FONT_GLYPH_CACHE_GUARD
        int inx = (ch >> 9) & 0x1ff;
        if (inx >= COUNT)
            return;
        lUInt16* ptr = ptrs[inx];
        if (!ptr) {
            ptr = new lUInt16[512];
            ptrs[inx] = ptr;
            memset(ptr, CACHED_UNSIGNED_METRIC_NOT_SET, sizeof(lUInt16) * 512);
        }
        ptr[ch & 0x1FF] = m;
    }
    void clear() {
        FONT_GLYPH_CACHE_GUARD
        for (int i = 0; i < 360; i++) {
            if (ptrs[i])
                delete[] ptrs[i];
            ptrs[i] = NULL;
        }
    }
    LVFontGlyphUnsignedMetricCache() {
        memset(ptrs, 0, 360 * sizeof(lUInt16*));
    }
    ~LVFontGlyphUnsignedMetricCache() {
        clear();
    }
};

#define CACHED_SIGNED_METRIC_NOT_SET 0x7FFF
#define CACHED_SIGNED_METRIC_SHIFT   0x8000
class LVFontGlyphSignedMetricCache: public LVFontGlyphUnsignedMetricCache
{
public:
    lInt16 get(lChar32 ch) {
        return (lInt16)(LVFontGlyphUnsignedMetricCache::get(ch) - CACHED_SIGNED_METRIC_SHIFT);
    }
    void put(lChar32 ch, lInt16 m) {
        LVFontGlyphUnsignedMetricCache::put(ch, (lUInt16)(m + CACHED_SIGNED_METRIC_SHIFT));
    }
};

class LVFreeTypeFace: public LVFont
{
protected:
    LVMutex& _mutex;
    lString8 _fileName;
    lString8 _faceName;
    css_font_family_t _fontFamily;
    FT_Library _library;
    FT_Face _face;
    FT_GlyphSlot _slot;
    FT_Matrix _matrix; // helper matrix for fake italic metrics
    int _size;         // caracter height in pixels
    int _height;       // full line height in pixels
    int _hyphen_width;
    int _baseline;
    int _weight; // original font weight 400: normal, 700: bold, 100..900 thin..black
    int _italic; // 0: regular, 1: italic, 2: fake/synthesized italic
    LVArray<int> _extra_metrics;
    LVFontGlyphUnsignedMetricCache _wcache;
    LVFontGlyphSignedMetricCache _lsbcache; // glyph left side bearing cache
    LVFontGlyphSignedMetricCache _rsbcache; // glyph right side bearing cache
    LVFontLocalGlyphCache _glyph_cache;
    bool _drawMonochrome;
    font_antialiasing_t _aa_mode;
    hinting_mode_t _hintingMode;
    shaping_mode_t _shapingMode;
    int _gammaIndex;
    bool _fallbackFontIsSet;
    LVFontRef _fallbackFont;
    /**
     * @brief Fallback mask for this fallback font.
     * 0 - for normal (not fallback) font
     * <any> - A mask with only one bit set, the number of which corresponds to the number in the fallback font chain.
     */
    lUInt32 _fallback_mask;
    int _synth_weight; // fake/synthesized weight
    bool _allowKerning;
    FT_Pos _synth_weight_strength; // for emboldening with FT_Outline_Embolden()
    FT_Pos _synth_weight_half_strength;
    FT_Pos _scale_mul; // only for fixed-size color fonts
    FT_Pos _scale_div; // only for fixed-size color fonts
    int _features;     // requested OpenType features bitmap
#if USE_HARFBUZZ == 1
    hb_font_t* _hb_font;
    hb_buffer_t* _hb_buffer;
    LVArray<hb_feature_t> _hb_features;
    // For use with SHAPING_MODE_HARFBUZZ:
    LVFontLocalGlyphCache _glyph_cache2;
    // For use with SHAPING_MODE_HARFBUZZ_LIGHT:
    LVHashTable<struct LVCharTriplet, struct LVCharPosInfo> _width_cache2;
#endif
public:
    // fallback font support

    virtual lUInt32 getFallbackMask() const {
        return _fallback_mask;
    }

    virtual void setFallbackMask(lUInt32 mask) {
        _fallback_mask = mask;
    }

    /// set fallback font for this font
    virtual void setFallbackFont(LVFontRef font);

    /// get fallback font for this font
    virtual LVFont* getFallbackFont(lUInt32 fallbackPassMask);

    /// returns font weight
    virtual int getWeight() const {
        return _synth_weight > 0 ? _synth_weight : _weight;
    }

    /// returns italic flag
    virtual int getItalic() const {
        return _italic;
    }

    /// sets face name
    virtual void setFaceName(lString8 face) {
        _faceName = face;
    }

    LVMutex& getMutex() {
        return _mutex;
    }

    FT_Library getLibrary() {
        return _library;
    }

    LVFreeTypeFace(LVMutex& mutex, FT_Library library, LVFontGlobalGlyphCache* globalCache);

    virtual ~LVFreeTypeFace();

    virtual void clearCache();

    virtual int getHyphenWidth();

    /// get kerning mode: true==ON, false=OFF
    virtual bool getKerning() const {
        return _allowKerning;
    }

    /// set kerning mode: true==ON, false=OFF
    virtual void setKerning(bool kerningEnabled);

    /// sets current hinting mode
    virtual void setHintingMode(hinting_mode_t mode);

    /// returns current hinting mode
    virtual hinting_mode_t getHintingMode() const {
        return _hintingMode;
    }

    /// sets current shaping mode
    virtual void setShapingMode(shaping_mode_t shapingMode);

    /// returns current kerning mode
    virtual shaping_mode_t getShapingMode() const {
        return _shapingMode;
    }

    /// get gamma correction index
    virtual int getGammaIndex() const {
        return _gammaIndex;
    }

    /// set gamma correction index
    virtual void setGammaIndex(int index);

    /// get bitmap mode (true=bitmap, false=antialiased)
    virtual bool getBitmapMode() {
        return _drawMonochrome;
    }

    /// set bitmap mode (true=bitmap, false=antialiased)
    virtual void setBitmapMode(bool drawBitmap);

    /// get antialiasing mode
    virtual font_antialiasing_t GetAntialiasMode() {
        return _aa_mode;
    }

    /// set antialiasing mode
    virtual void SetAntialiasMode(font_antialiasing_t mode);

    /// get OpenType features (bitmap)
    virtual int getFeatures() const {
        return _features;
    }

    /// set OpenType features (bitmap)
    virtual void setFeatures(int features);

    void setSynthWeight(int synth_weight);

    bool loadFromBuffer(LVByteArrayRef buf, int index, int size, css_font_family_t fontFamily,
                        bool monochrome, bool italicize, int weight = -1);

    bool loadFromFile(const char* fname, int index, int size, css_font_family_t fontFamily,
                      bool monochrome, bool italicize, int weight = -1);

    /** \brief get glyph info
        \param glyph is pointer to glyph_info_t struct to place retrieved info
        \return true if glyh was found
    */
    virtual bool getGlyphInfo(lUInt32 code, glyph_info_t* glyph, lChar32 def_char = 0, lUInt32 fallbackPassMask = 0);
    /*
  // USE GET_CHAR_FLAGS instead
    inline int calcCharFlags( lChar32 ch )
    {
        switch ( ch ) {
        case 0x0020:
            return LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER;
        case UNICODE_SOFT_HYPHEN_CODE:
            return LCHAR_ALLOW_WRAP_AFTER;
        case '-':
            return LCHAR_DEPRECATED_WRAP_AFTER;
        case '\r':
        case '\n':
            return LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER;
        default:
            return 0;
        }
    }
  */

    /** \brief get extra glyph metric
    */
    virtual bool getGlyphExtraMetric(glyph_extra_metric_t metric, lUInt32 code, int& value, bool scaled_to_px = true, lChar32 def_char = 0, lUInt32 fallbackPassMask = 0);

    /** \brief measure text
        \param text is text string pointer
        \param len is number of characters to measure
        \return number of characters before max_width reached
    */
    virtual lUInt16 measureText(
            const lChar32* text, int len,
            lUInt16* widths,
            lUInt8* flags,
            int max_width,
            lChar32 def_char,
            TextLangCfg* lang_cfg = NULL,
            int letter_spacing = 0,
            bool allow_hyphenation = true,
            lUInt32 hints = 0,
            lUInt32 fallbackPassMask = 0);

    /** \brief measure text
        \param text is text string pointer
        \param len is number of characters to measure
        \return width of specified string
    */
    virtual lUInt32 getTextWidth(
            const lChar32* text, int len, TextLangCfg* lang_cfg = NULL);

    /** \brief get glyph item
        \param code is unicode character
        \return glyph pointer if glyph was found, NULL otherwise
    */
    virtual LVFontGlyphCacheItem* getGlyph(lUInt32 ch, lChar32 def_char = 0, lUInt32 fallbackPassMask = 0);

    //    /** \brief get glyph image in 1 byte per pixel format
    //        \param code is unicode character
    //        \param buf is buffer [width*height] to place glyph data
    //        \return true if glyph was found
    //    */
    //    virtual bool getGlyphImage(lUInt16 ch, lUInt8 * bmp, lChar32 def_char=0)
    //    {
    //        LVFontGlyphCacheItem * item = getGlyph(ch);
    //        if ( item )
    //            memcpy( bmp, item->bmp, item->bmp_width * item->bmp_height );
    //        return item;
    //    }

    /// returns font baseline offset
    virtual int getBaseline() {
        return _baseline;
    }

    /// returns font height
    virtual int getHeight() const {
        return _height;
    }

    /// returns font character size
    virtual int getSize() const {
        return _size;
    }

    /// returns char width
    virtual int getCharWidth(lChar32 ch, lChar32 def_char = '?');

    /// returns char glyph left side bearing
    int getLeftSideBearing(lChar32 ch, bool negative_only = false, bool italic_only = false);

    /// returns char glyph right side bearing
    virtual int getRightSideBearing(lChar32 ch, bool negative_only = false, bool italic_only = false);

    /// returns extra metric
    virtual int getExtraMetric(font_extra_metric_t metric, bool scaled_to_px = true);

    /// returns if font has OpenType Math tables
    virtual bool hasOTMathSupport() const;

    /// retrieves font handle
    virtual void* GetHandle() {
        return (void*)_face;
    }

    /// returns font typeface name
    virtual lString8 getTypeFace() const {
        return _faceName;
    }

    /// returns font family id
    virtual css_font_family_t getFontFamily() const {
        return _fontFamily;
    }

    virtual bool kerningEnabled() {
#if (ALLOW_KERNING == 1)
#if USE_HARFBUZZ == 1
        return _allowKerning;
#else
        return _allowKerning && FT_HAS_KERNING(_face);
#endif
#else
        return false;
#endif
    }

    /// draws text string
    virtual int DrawTextString(LVDrawBuf* buf, int x, int y,
                               const lChar32* text, int len,
                               lChar32 def_char, lUInt32* palette = NULL,
                               bool addHyphen = false, TextLangCfg* lang_cfg = NULL,
                               lUInt32 flags = 0, int letter_spacing = 0, int width = -1,
                               int text_decoration_back_gap = 0,
                               int target_w = -1, int target_h = -1,
                               lUInt32 fallbackPassMask = 0);

    /// returns true if font is empty
    virtual bool IsNull() const {
        return _face == NULL;
    }

    virtual bool operator!() const {
        return _face == NULL;
    }

    virtual void Clear();
protected:
    void updateTransform();
    FT_UInt getCharIndex(lUInt32 code, lChar32 def_char);
    bool getGlyphIndexInfo(lUInt32 glyph_index, glyph_info_t* glyph);
    void DrawStretchedGlyph(LVDrawBuf* buf, int glyph_index, int x, int y, int w, int h, lUInt32* palette = NULL);
#if USE_HARFBUZZ == 1
    LVFontGlyphCacheItem* getGlyphByIndex(lUInt32 index);
    lChar32 filterChar(lChar32 code, lChar32 def_char = 0);
    bool hbCalcCharWidth(struct LVCharPosInfo* posInfo, const struct LVCharTriplet& triplet,
                         lChar32 def_char, lUInt32 fallbackPassMask);
    bool setHBFeatureValue(const char* tag, uint32_t value);
    bool addHBFeature(const char* tag);
    bool delHBFeature(const char* tag);
    void setupHBFeatures();
#endif
};

#endif // (USE_FREETYPE==1)

#endif // __LV_FREETYPEFACE_H_INCLUDED__
