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

#include "include/core/SkRefCnt.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/ganesh/GrRecordingContext.h"
#include "include/gpu/ganesh/GrTypes.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkTArray.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/ganesh/Device.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrImageContextPriv.h"
#include "src/text/gpu/SubRunControl.h"

#include <memory>
#include <string_view>

class GrAuditTrail;
class GrBackendFormat;
class GrBackendTexture;
class GrContextThreadSafeProxy;
class GrDeferredDisplayList;
class GrDrawingManager;
class GrImageInfo;
class GrOnFlushCallbackObject;
class GrProgramInfo;
class GrProxyProvider;
class GrSurfaceProxy;
class GrSurfaceProxyView;
class GrThreadSafeCache;
class SkArenaAlloc;
class SkColorSpace;
class SkSurfaceProps;
enum SkAlphaType : int;
enum class GrColorType;
struct SkISize;
struct SkImageInfo;

namespace skgpu {
class Swizzle;
class RefCntedCallback;

namespace ganesh {
class SurfaceContext;
class SurfaceFillContext;
}  // namespace ganesh
}  // namespace skgpu

namespace sktext::gpu {
class SubRunAllocator;
class TextBlobRedrawCoordinator;
}  // namespace sktext::gpu

/** Class that exposes methods on GrRecordingContext that are only intended for use internal to
    Skia. This class is purely a privileged window into GrRecordingContext. It should never have
    additional data members or virtual methods. */
class GrRecordingContextPriv : public GrImageContextPriv {
public:
    GrRecordingContext* context() { return static_cast<GrRecordingContext*>(fContext); }
    const GrRecordingContext* context() const {
        return static_cast<const GrRecordingContext*>(fContext);
    }

    GrProxyProvider* proxyProvider() { return this->context()->proxyProvider(); }
    const GrProxyProvider* proxyProvider() const { return this->context()->proxyProvider(); }

    GrDrawingManager* drawingManager() { return this->context()->drawingManager(); }

    SkArenaAlloc* recordTimeAllocator() { return this->context()->arenas().recordTimeAllocator(); }
    sktext::gpu::SubRunAllocator* recordTimeSubRunAllocator() {
        return this->context()->arenas().recordTimeSubRunAllocator();
    }
    GrRecordingContext::Arenas arenas() { return this->context()->arenas(); }

    GrRecordingContext::OwnedArenas&& detachArenas() { return this->context()->detachArenas(); }

    void recordProgramInfo(const GrProgramInfo* programInfo) {
        this->context()->recordProgramInfo(programInfo);
    }

    void detachProgramData(skia_private::TArray<GrRecordingContext::ProgramData>* dst) {
        this->context()->detachProgramData(dst);
    }

    sktext::gpu::TextBlobRedrawCoordinator* getTextBlobCache() {
        return this->context()->getTextBlobRedrawCoordinator();
    }

    GrThreadSafeCache* threadSafeCache() { return this->context()->threadSafeCache(); }

    void moveRenderTasksToDDL(GrDeferredDisplayList*);

    /**
     * Registers an object for flush-related callbacks. (See GrOnFlushCallbackObject.)
     *
     * NOTE: the drawing manager tracks this object as a raw pointer; it is up to the caller to
     * ensure its lifetime is tied to that of the context.
     */
    void addOnFlushCallbackObject(GrOnFlushCallbackObject*);

    GrAuditTrail* auditTrail() { return this->context()->fAuditTrail.get(); }

#if defined(GPU_TEST_UTILS)
    // Used by tests that intentionally exercise codepaths that print warning messages, in order to
    // not confuse users with output that looks like a testing failure.
    class AutoSuppressWarningMessages {
    public:
        AutoSuppressWarningMessages(GrRecordingContext* context) : fContext(context) {
            ++fContext->fSuppressWarningMessages;
        }
        ~AutoSuppressWarningMessages() {
            --fContext->fSuppressWarningMessages;
        }
    private:
        GrRecordingContext* fContext;
    };
    void incrSuppressWarningMessages() { ++this->context()->fSuppressWarningMessages; }
    void decrSuppressWarningMessages() { --this->context()->fSuppressWarningMessages; }
#endif

    void printWarningMessage(const char* msg) const {
#if defined(GPU_TEST_UTILS)
        if (this->context()->fSuppressWarningMessages > 0) {
            return;
        }
#endif
        SkDebugf("%s", msg);
    }

    GrRecordingContext::Stats* stats() {
        return &this->context()->fStats;
    }

#if GR_GPU_STATS && defined(GPU_TEST_UTILS)
    using DMSAAStats = GrRecordingContext::DMSAAStats;
    DMSAAStats& dmsaaStats() { return this->context()->fDMSAAStats; }
#endif

    sktext::gpu::SubRunControl getSubRunControl(bool useSDFTForSmallText) const;

    /**
     * Create a GrRecordingContext without a resource cache
     */
    static sk_sp<GrRecordingContext> MakeDDL(sk_sp<GrContextThreadSafeProxy>);

    sk_sp<skgpu::ganesh::Device> createDevice(GrColorType,
                                              sk_sp<GrSurfaceProxy>,
                                              sk_sp<SkColorSpace>,
                                              GrSurfaceOrigin,
                                              const SkSurfaceProps&,
                                              skgpu::ganesh::Device::InitContents);
    sk_sp<skgpu::ganesh::Device> createDevice(skgpu::Budgeted,
                                              const SkImageInfo&,
                                              SkBackingFit,
                                              int sampleCount,
                                              skgpu::Mipmapped,
                                              skgpu::Protected,
                                              GrSurfaceOrigin,
                                              const SkSurfaceProps&,
                                              skgpu::ganesh::Device::InitContents);

    // If the passed in GrSurfaceProxy is renderable this will return a SurfaceDrawContext,
    // otherwise it will return a SurfaceContext.
    std::unique_ptr<skgpu::ganesh::SurfaceContext> makeSC(GrSurfaceProxyView readView,
                                                          const GrColorInfo&);

    // Makes either a SurfaceContext, SurfaceFillContext, or a SurfaceDrawContext, depending on
    // GrRenderable and the GrImageInfo.
    std::unique_ptr<skgpu::ganesh::SurfaceContext> makeSC(
            const GrImageInfo&,
            const GrBackendFormat&,
            std::string_view label,
            SkBackingFit = SkBackingFit::kExact,
            GrSurfaceOrigin = kTopLeft_GrSurfaceOrigin,
            skgpu::Renderable = skgpu::Renderable::kNo,
            int renderTargetSampleCnt = 1,
            skgpu::Mipmapped = skgpu::Mipmapped::kNo,
            skgpu::Protected = skgpu::Protected::kNo,
            skgpu::Budgeted = skgpu::Budgeted::kYes);

    /**
     * Uses GrImageInfo's color type to pick the default texture format. Will return a
     * SurfaceDrawContext if possible.
     */
    std::unique_ptr<skgpu::ganesh::SurfaceFillContext> makeSFC(
            GrImageInfo,
            std::string_view label,
            SkBackingFit = SkBackingFit::kExact,
            int sampleCount = 1,
            skgpu::Mipmapped = skgpu::Mipmapped::kNo,
            skgpu::Protected = skgpu::Protected::kNo,
            GrSurfaceOrigin = kTopLeft_GrSurfaceOrigin,
            skgpu::Budgeted = skgpu::Budgeted::kYes);

    /**
     * Makes a custom configured SurfaceFillContext where the caller specifies the specific
     * texture format and swizzles. The color type will be kUnknown. Returns a SurfaceDrawContext
     * if possible.
     */
    std::unique_ptr<skgpu::ganesh::SurfaceFillContext> makeSFC(SkAlphaType,
                                                               sk_sp<SkColorSpace>,
                                                               SkISize dimensions,
                                                               SkBackingFit,
                                                               const GrBackendFormat&,
                                                               int sampleCount,
                                                               skgpu::Mipmapped,
                                                               skgpu::Protected,
                                                               skgpu::Swizzle readSwizzle,
                                                               skgpu::Swizzle writeSwizzle,
                                                               GrSurfaceOrigin,
                                                               skgpu::Budgeted,
                                                               std::string_view label);

    /**
     * Like the above but uses GetFallbackColorTypeAndFormat to find a fallback color type (and
     * compatible format) if the passed GrImageInfo's color type is not renderable.
     */
    std::unique_ptr<skgpu::ganesh::SurfaceFillContext> makeSFCWithFallback(
            GrImageInfo,
            SkBackingFit,
            int sampleCount,
            skgpu::Mipmapped,
            skgpu::Protected,
            GrSurfaceOrigin = kTopLeft_GrSurfaceOrigin,
            skgpu::Budgeted = skgpu::Budgeted::kYes);

    /**
     * Creates a SurfaceFillContext from an existing GrBackendTexture. The GrColorInfo's color
     * type must be compatible with backend texture's format or this will fail. All formats are
     * considered compatible with kUnknown. Returns a SurfaceDrawContext if possible.
     */
    std::unique_ptr<skgpu::ganesh::SurfaceFillContext> makeSFCFromBackendTexture(
            GrColorInfo,
            const GrBackendTexture&,
            int sampleCount,
            GrSurfaceOrigin,
            sk_sp<skgpu::RefCntedCallback> releaseHelper);

protected:
    explicit GrRecordingContextPriv(GrRecordingContext* rContext) : GrImageContextPriv(rContext) {}

private:
    GrRecordingContextPriv(const GrRecordingContextPriv&) = delete;
    GrRecordingContextPriv& operator=(const GrRecordingContextPriv&) = delete;

    // No taking addresses of this type.
    const GrRecordingContextPriv* operator&() const;
    GrRecordingContextPriv* operator&();

    friend class GrRecordingContext;  // to construct/copy this type.
};

inline GrRecordingContextPriv GrRecordingContext::priv() { return GrRecordingContextPriv(this); }

inline const GrRecordingContextPriv GrRecordingContext::priv () const {  // NOLINT(readability-const-return-type)
    return GrRecordingContextPriv(const_cast<GrRecordingContext*>(this));
}

#endif
