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

#ifndef DrawAtlasPathOp_DEFINED
#define DrawAtlasPathOp_DEFINED

#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/gpu/GpuTypes.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkColorData.h"
#include "src/core/SkIPoint16.h"
#include "src/gpu/ganesh/GrBuffer.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrGpuBuffer.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/GrProcessorSet.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/ops/AtlasInstancedHelper.h"
#include "src/gpu/ganesh/ops/GrDrawOp.h"
#include "src/gpu/ganesh/ops/GrOp.h"

#include <array>
#include <utility>

class GrAppliedClip;
class GrDstProxyView;
class GrOpFlushState;
class GrProgramInfo;
class GrRecordingContext;
enum class GrXferBarrierFlags;

namespace skgpu::ganesh {

// Fills a rectangle of pixels with a clip against coverage values from an atlas.
class DrawAtlasPathOp final : public GrDrawOp {
public:
    DEFINE_OP_CLASS_ID

    DrawAtlasPathOp(SkArenaAlloc* arena, const SkIRect& fillBounds, const SkMatrix& localToDevice,
                    GrPaint&& paint, SkIPoint16 locationInAtlas, const SkIRect& pathDevIBounds,
                    bool transposedInAtlas, GrSurfaceProxyView atlasView, bool isInverseFill)
            : GrDrawOp(ClassID())
            , fHeadInstance(arena->make<Instance>(fillBounds, localToDevice, paint.getColor4f(),
                                                  locationInAtlas, pathDevIBounds,
                                                  transposedInAtlas))
            , fTailInstance(&fHeadInstance->fNext)
            , fAtlasHelper(std::move(atlasView),
                           isInverseFill ? AtlasInstancedHelper::ShaderFlags::kCheckBounds |
                                           AtlasInstancedHelper::ShaderFlags::kInvertCoverage
                                         : AtlasInstancedHelper::ShaderFlags::kNone)
            , fProcessors(std::move(paint)) {
        this->setBounds(SkRect::Make(fillBounds), HasAABloat::kYes, IsHairline::kNo);
    }

    const char* name() const override { return "DrawAtlasPathOp"; }
    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
    void visitProxies(const GrVisitProxyFunc& func) const override {
        func(fAtlasHelper.proxy(), skgpu::Mipmapped::kNo);
        fProcessors.visitProxies(func);
    }
    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
    CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;

    void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView& writeView, GrAppliedClip*,
                      const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override;
    void onPrepare(GrOpFlushState*) override;
    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;

private:
    void prepareProgram(const GrCaps&, SkArenaAlloc*, const GrSurfaceProxyView& writeView,
                        bool usesMSAASurface, GrAppliedClip&&, const GrDstProxyView&,
                        GrXferBarrierFlags, GrLoadOp colorLoadOp);

    struct Instance {
        Instance(const SkIRect& fillIBounds, const SkMatrix& m,
                 const SkPMColor4f& color, SkIPoint16 locationInAtlas,
                 const SkIRect& pathDevIBounds, bool transposedInAtlas)
                : fFillBounds(fillIBounds)
                , fLocalToDeviceIfUsingLocalCoords{m.getScaleX(), m.getSkewY(),
                                                   m.getSkewX(), m.getScaleY(),
                                                   m.getTranslateX(), m.getTranslateY()}
                , fColor(color)
                , fAtlasInstance(locationInAtlas, pathDevIBounds, transposedInAtlas) {
        }
        SkIRect fFillBounds;
        std::array<float, 6> fLocalToDeviceIfUsingLocalCoords;
        SkPMColor4f fColor;
        AtlasInstancedHelper::Instance fAtlasInstance;
        Instance* fNext = nullptr;
    };

    Instance* fHeadInstance;
    Instance** fTailInstance;

    AtlasInstancedHelper fAtlasHelper;
    bool fUsesLocalCoords = false;

    int fInstanceCount = 1;

    GrProgramInfo* fProgram = nullptr;

    sk_sp<const GrBuffer> fInstanceBuffer;
    int fBaseInstance;

    // Only used if sk_VertexID is not supported.
    sk_sp<const GrGpuBuffer> fVertexBufferIfNoIDSupport;

    GrProcessorSet fProcessors;
};

}  // namespace skgpu::ganesh

#endif // DrawAtlasPathOp_DEFINED
