/*
 * 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_TextBlob_DEFINED
#define sktext_gpu_TextBlob_DEFINED

#include <algorithm>
#include <limits>

#include "include/core/SkRefCnt.h"
#include "include/private/chromium/Slug.h"
#include "src/base/SkTInternalLList.h"
#include "src/core/SkMaskFilterBase.h"
#include "src/text/gpu/SubRunContainer.h"

class SkMatrixProvider;
class SkStrikeClient;
class SkSurfaceProps;
class SkTextBlob;
class SkTextBlobRunIterator;

namespace sktext {
class GlyphRunList;
    namespace gpu {
    class Glyph;
    class StrikeCache;
    }
}

#if defined(SK_GANESH)  // Ganesh support
#include "src/gpu/ganesh/GrColor.h"
#include "src/gpu/ganesh/ops/GrOp.h"
class GrAtlasManager;
class GrDeferredUploadTarget;
class GrMeshDrawTarget;
namespace skgpu::ganesh {
class SurfaceDrawContext;
}
#endif

namespace sktext::gpu {

// -- TextBlob -----------------------------------------------------------------------------------
// A TextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
// on the GPU.  These are initially created with valid positions and colors, but with invalid
// texture coordinates.
//
// A TextBlob contains a number of SubRuns that are created in the blob's arena. Each SubRun
// tracks its own glyph and position data.
//
// In these classes, I'm trying to follow the convention about matrices and origins.
// * drawMatrix and drawOrigin - describes transformations for the current draw command.
// * positionMatrix - is equal to drawMatrix * [drawOrigin-as-translation-matrix]
// * initial Matrix - describes the combined initial matrix and origin the TextBlob was created
//                    with.
//
//
class TextBlob final : public SkRefCnt {
public:
    // Key is not used as part of a hash map, so the hash is never taken. It's only used in a
    // list search using operator =().
    struct Key {
        static std::tuple<bool, Key> Make(const GlyphRunList& glyphRunList,
                                          const SkPaint& paint,
                                          const SkMatrix& drawMatrix,
                                          const SkStrikeDeviceInfo& strikeDevice);
        uint32_t fUniqueID;
        // Color may affect the gamma of the mask we generate, but in a fairly limited way.
        // Each color is assigned to on of a fixed number of buckets based on its
        // luminance. For each luminance bucket there is a "canonical color" that
        // represents the bucket.  This functionality is currently only supported for A8
        SkColor fCanonicalColor;
        SkScalar fFrameWidth;
        SkScalar fMiterLimit;
        SkPixelGeometry fPixelGeometry;
        SkMaskFilterBase::BlurRec fBlurRec;
        uint32_t fScalerContextFlags;
        SkMatrix fPositionMatrix;
        // Below here fields are of size 1 byte.
        bool fHasSomeDirectSubRuns;
        bool fHasBlur;
        SkPaint::Style fStyle;
        SkPaint::Join fJoin;

        bool operator==(const Key& other) const;
    };

    SK_DECLARE_INTERNAL_LLIST_INTERFACE(TextBlob);

    // Make a TextBlob and its sub runs.
    static sk_sp<TextBlob> Make(const sktext::GlyphRunList& glyphRunList,
                                const SkPaint& paint,
                                const SkMatrix& positionMatrix,
                                SkStrikeDeviceInfo strikeDeviceInfo,
                                StrikeForGPUCacheInterface* strikeCache);

    TextBlob(SubRunAllocator&& alloc,
             SubRunContainerOwner subRuns,
             int totalMemorySize,
             SkColor initialLuminance);

    ~TextBlob() override;

    // Change memory management to handle the data after TextBlob, but in the same allocation
    // of memory. Only allow placement new.
    void operator delete(void* p);
    void* operator new(size_t);
    void* operator new(size_t, void* p);

    const Key& key() { return fKey; }

    void addKey(const Key& key);

    bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const;

    const Key& key() const;
    size_t size() const { return SkTo<size_t>(fSize); }

#if defined(SK_GANESH)
    void draw(SkCanvas*,
              const GrClip* clip,
              const SkMatrixProvider& viewMatrix,
              SkPoint drawOrigin,
              const SkPaint& paint,
              skgpu::ganesh::SurfaceDrawContext* sdc);
#endif
#if defined(SK_GRAPHITE)
    void draw(SkCanvas*,
              SkPoint drawOrigin,
              const SkPaint& paint,
              skgpu::graphite::Device* device);
#endif
    const AtlasSubRun* testingOnlyFirstSubRun() const;

private:
    // The allocator must come first because it needs to be destroyed last. Other fields of this
    // structure may have pointers into it.
    SubRunAllocator fAlloc;

    SubRunContainerOwner fSubRuns;

    // Overall size of this struct plus vertices and glyphs at the end.
    const int fSize;

    const SkColor fInitialLuminance;

    Key fKey;
};

}  // namespace sktext::gpu

namespace skgpu::ganesh {
sk_sp<sktext::gpu::Slug> MakeSlug(const SkMatrixProvider& drawMatrix,
                                  const sktext::GlyphRunList& glyphRunList,
                                  const SkPaint& initialPaint,
                                  const SkPaint& drawingPaint,
                                  SkStrikeDeviceInfo strikeDeviceInfo,
                                  sktext::StrikeForGPUCacheInterface* strikeCache);
}  // namespace skgpu::ganesh
#endif  // sktext_gpu_TextBlob_DEFINED
