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

#ifndef sktext_gpu_StrikeCache_DEFINED
#define sktext_gpu_StrikeCache_DEFINED

#include "include/core/SkRefCnt.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkDescriptor.h"
#include "src/core/SkStrikeSpec.h"
#include "src/core/SkTHash.h"
#include "src/gpu/AtlasTypes.h"

#include <cstddef>
#include <cstdint>

struct SkPackedGlyphID;

//  SK_DEFAULT_GPU_FONT_CACHE_COUNT_LIMIT and SK_DEFAULT_GPU_FONT_CACHE_LIMIT can be set using -D
//  on your ompiler commandline, or by using the defines in SkUserConfig.h
#ifndef SK_DEFAULT_GPU_FONT_CACHE_COUNT_LIMIT
    #define SK_DEFAULT_GPU_FONT_CACHE_COUNT_LIMIT   2048
#endif

#ifndef SK_DEFAULT_GPU_FONT_CACHE_LIMIT
    #define SK_DEFAULT_GPU_FONT_CACHE_LIMIT     (2 * 1024 * 1024)
#endif

namespace sktext::gpu {

class Glyph;
struct GlyphEntryKey;
class StrikeCache;

// The TextStrike manages an SkArenaAlloc for Glyphs. The SkStrike is what actually creates
// the mask. The TextStrike may outlive the generating SkStrike. However, it retains a copy
// of it's SkDescriptor as a key to access (or regenerate) the SkStrike. TextStrikes are
// created by and owned by a StrikeCache.
class TextStrike : public SkNVRefCnt<TextStrike> {
public:
    TextStrike(StrikeCache* strikeCache,
               const SkStrikeSpec& strikeSpec);

    Glyph* getGlyph(SkPackedGlyphID, skgpu::MaskFormat format);
    const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
    const SkDescriptor& getDescriptor() const { return fStrikeSpec.descriptor(); }

private:
    StrikeCache* const fStrikeCache;

    // Key for retrieving the SkStrike for creating new atlas data.
    const SkStrikeSpec fStrikeSpec;

    struct HashTraits {
        static const GlyphEntryKey& GetKey(const Glyph* glyph);
        static uint32_t Hash(GlyphEntryKey key);
    };
    // Map SkPackedGlyphID -> Glyph*.
    skia_private::THashTable<Glyph*, GlyphEntryKey, HashTraits> fCache;

    // Store for the glyph information.
    SkArenaAlloc fAlloc{512};

    TextStrike*  fNext{nullptr};
    TextStrike*  fPrev{nullptr};
    size_t       fMemoryUsed{sizeof(TextStrike)};
    bool         fRemoved{false};

    friend class StrikeCache;
};

// StrikeCache manages strikes which are indexed by a SkStrike. These strikes can then be
// used to generate individual Glyph Masks.
class StrikeCache {
public:
    ~StrikeCache();

    // The user of the cache may hold a long-lived ref to the returned strike.
    sk_sp<TextStrike> findOrCreateStrike(const SkStrikeSpec& strikeSpec);

    void freeAll();

private:
    friend class TextStrike;  // for TextStrike::getGlyph
    sk_sp<TextStrike> internalFindStrikeOrNull(const SkDescriptor& desc);
    sk_sp<TextStrike> generateStrike(const SkStrikeSpec& strikeSpec);

    void internalRemoveStrike(TextStrike* strike);
    void internalAttachToHead(sk_sp<TextStrike> strike);

    // Checkout budgets, modulated by the specified min-bytes-needed-to-purge,
    // and attempt to purge caches to match.
    // Returns number of bytes freed.
    size_t internalPurge(size_t minBytesNeeded = 0);

    // A simple accounting of what each glyph cache reports and the strike cache total.
    void validate() const;

    TextStrike* fHead{nullptr};
    TextStrike* fTail{nullptr};

    struct HashTraits {
        static const SkDescriptor& GetKey(const sk_sp<TextStrike>& strike);
        static uint32_t Hash(const SkDescriptor& strikeSpec);
    };
    using StrikeHash = skia_private::THashTable<sk_sp<TextStrike>, const SkDescriptor&, HashTraits>;

    StrikeHash fCache;

    size_t  fCacheSizeLimit{SK_DEFAULT_GPU_FONT_CACHE_LIMIT};
    size_t  fTotalMemoryUsed{0};
    int32_t fCacheCountLimit{SK_DEFAULT_GPU_FONT_CACHE_COUNT_LIMIT};
    int32_t fCacheCount{0};
};

}  // namespace sktext::gpu

#endif  // sktext_gpu_StrikeCache_DEFINED
