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

#include "GrDistanceFieldAdjustTable.h"
#include "GrGeometryProcessor.h"
#include "GrTextBlob.h"
#include "GrTextUtils.h"
#include "SkGlyphRun.h"

#if GR_TEST_UTILS
#include "GrDrawOpTest.h"
#endif

class GrDrawOp;
class GrTextBlobCache;
class SkGlyph;

/*
 * Renders text using some kind of an atlas, ie BitmapText or DistanceField text
 */
class GrTextContext {
public:
    struct Options {
        /**
         * Below this size (in device space) distance field text will not be used. Negative means
         * use a default value.
         */
        SkScalar fMinDistanceFieldFontSize = -1.f;
        /**
         * Above this size (in device space) distance field text will not be used and glyphs will
         * be rendered from outline as individual paths. Negative means use a default value.
         */
        SkScalar fMaxDistanceFieldFontSize = -1.f;
        /** Forces all distance field vertices to use 3 components, not just when in perspective. */
        bool fDistanceFieldVerticesAlwaysHaveW = false;
    };

    static std::unique_ptr<GrTextContext> Make(const Options& options);

    void drawGlyphRunList(GrContext*, GrTextUtils::Target*, const GrClip&,
                          const SkMatrix& viewMatrix, const SkSurfaceProps&, const SkGlyphRunList&,
                          const SkIRect& clipBounds);

    std::unique_ptr<GrDrawOp> createOp_TestingOnly(GrContext*,
                                                   GrTextContext*,
                                                   GrRenderTargetContext*,
                                                   const SkPaint&,
                                                   const SkMatrix& viewMatrix,
                                                   const char* text,
                                                   int x,
                                                   int y);

    static void SanitizeOptions(Options* options);
    static bool CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
                                        const SkSurfaceProps& props,
                                        bool contextSupportsDistanceFieldText,
                                        const Options& options);
    static void InitDistanceFieldPaint(GrTextBlob* blob,
                                       SkPaint* skPaint,
                                       const SkMatrix& viewMatrix,
                                       const Options& options,
                                       SkScalar* textRatio,
                                       SkScalerContextFlags* flags);

    class FallbackGlyphRunHelper {
    public:
        FallbackGlyphRunHelper(const SkMatrix& viewMatrix,
                           const SkPaint& pathPaint,
                           SkScalar maxTextSize,
                           SkScalar textRatio)
                : fViewMatrix(viewMatrix)
                , fTextSize(pathPaint.getTextSize())
                , fMaxTextSize(maxTextSize)
                , fTextRatio(textRatio)
                , fTransformedFallbackTextSize(fMaxTextSize)
                , fUseTransformedFallback(false) {
            fMaxScale = viewMatrix.getMaxScale();
        }

        void appendGlyph(const SkGlyph& glyph, SkGlyphID glyphID, SkPoint glyphPos);
        void drawGlyphs(
                GrTextBlob* blob, int runIndex, GrGlyphCache* cache,
                const SkSurfaceProps& props, const GrTextUtils::Paint& flags,
                SkScalerContextFlags scalerContextFlags);

        void initializeForDraw(SkPaint* paint, SkScalar* textRatio, SkMatrix* matrix) const;
        const std::vector<SkGlyphID>& fallbackText() const { return fFallbackTxt; }

    private:
        std::vector<SkGlyphID> fFallbackTxt;
        std::vector<SkPoint> fFallbackPos;

        const SkMatrix& fViewMatrix;
        SkScalar fTextSize;
        SkScalar fMaxTextSize;
        SkScalar fTextRatio;
        SkScalar fTransformedFallbackTextSize;
        SkScalar fMaxScale;
        bool fUseTransformedFallback;
    };

private:
    GrTextContext(const Options& options);

    // sets up the descriptor on the blob and returns a detached cache.  Client must attach
    static SkColor ComputeCanonicalColor(const SkPaint&, bool lcd);
    // Determines if we need to use fake gamma (and contrast boost):
    static SkScalerContextFlags ComputeScalerContextFlags(const GrColorSpaceInfo&);

    void regenerateGlyphRunList(GrTextBlob* bmp,
                            GrGlyphCache*,
                            const GrShaderCaps&,
                            const GrTextUtils::Paint&,
                            SkScalerContextFlags scalerContextFlags,
                            const SkMatrix& viewMatrix,
                            const SkSurfaceProps&,
                            const SkGlyphRunList& glyphRunList,
                            SkGlyphRunListDrawer* glyphDrawer);

    static void AppendGlyph(GrTextBlob*, int runIndex, GrGlyphCache*,
                            sk_sp<GrTextStrike>*, const SkGlyph&, GrGlyph::MaskStyle maskStyle,
                            SkScalar sx, SkScalar sy,
                            GrColor color, SkGlyphCache*, SkScalar textRatio,
                            bool needsTransform);


    const GrDistanceFieldAdjustTable* dfAdjustTable() const { return fDistanceAdjustTable.get(); }

    sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;

    Options fOptions;

#if GR_TEST_UTILS
    static const SkScalerContextFlags kTextBlobOpScalerContextFlags =
            SkScalerContextFlags::kFakeGammaAndBoostContrast;
    GR_DRAW_OP_TEST_FRIEND(GrAtlasTextOp);
#endif
};

#endif  // GrTextContext_DEFINED
