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

#include "tests/Test.h"

#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
#include "include/core/SkSurfaceProps.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/graphite/Context.h"
#include "include/gpu/graphite/Surface.h"
#include "tools/graphite/ContextFactory.h"
#include "tools/graphite/GraphiteMemoryPipelineStorage.h"
#include "tools/graphite/TestOptions.h"

using namespace skiatest::graphite;

namespace {

bool draw(GraphiteTestContext* origTestContext,
          const TestOptions& origOptions,
          sk_gpu_test::GraphiteMemoryPipelineStorage* memoryPipelineStorage,
          bool withGradient) {

    // Rebuild the Context with a PersistentCache
    TestOptions newOptions(origOptions);
    newOptions.fContextOptions.fPersistentPipelineStorage = memoryPipelineStorage;

    skiatest::graphite::ContextFactory workaroundFactory(newOptions);
    ContextInfo ctxInfo = workaroundFactory.getContextInfo(origTestContext->contextType());

    skgpu::graphite::Context* newContext = ctxInfo.fContext;
    GraphiteTestContext* newTestContext = ctxInfo.fTestContext;

    std::unique_ptr<skgpu::graphite::Recorder> recorder = newContext->makeRecorder();
    if (!recorder) {
        return false;
    }

    {
        SkSurfaceProps props(0, kRGB_H_SkPixelGeometry);
        auto ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kPremul_SkAlphaType);

        sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(recorder.get(), ii,
                                                            skgpu::Mipmapped::kNo, &props);
        if (!surface) {
            return false;
        }

        SkPaint paint;
        if (withGradient) {
            const SkPoint pts[] = {{0, 0}, {64, 64}};
            const SkColor colors[] = {SK_ColorWHITE, SK_ColorBLACK};
            paint.setShader(SkGradientShader::MakeLinear(pts,
                                                         colors,
                                                         nullptr,
                                                         /* count= */ 2,
                                                         SkTileMode::kClamp));
        }
        surface->getCanvas()->drawRRect(SkRRect::MakeOval({16, 16, 48, 48 }), paint);
    }

    std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
    if (!recording) {
        return false;
    }

    skgpu::graphite::InsertRecordingInfo info;
    info.fRecording = recording.get();
    if (!newContext->insertRecording(info)) {
        return false;
    }
    newTestContext->syncedSubmit(newContext);

    newContext->syncPipelineData();

    return true;
}

} // anonymous namespace

namespace skgpu::graphite {

DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(PersistentPipelineStorageTest,
                                           skiatest::IsVulkanContextType,
                                           reporter,
                                           origContext,
                                           origTestContext,
                                           origOptions,
                                           /* optionsProc= */ nullptr,
                                           /* condition= */ true,
                                           CtsEnforcement::kNextRelease) {

    sk_gpu_test::GraphiteMemoryPipelineStorage memoryPipelineStorage;

    REPORTER_ASSERT(reporter, draw(origTestContext, origOptions, &memoryPipelineStorage,
                                   /* withGradient= */ false));

    // On the first draw there shouldn't be anything to load but we should store the new pipelines.
    REPORTER_ASSERT(reporter, memoryPipelineStorage.numLoads() == 0,
                    "actual: %d", memoryPipelineStorage.numLoads());
    REPORTER_ASSERT(reporter, memoryPipelineStorage.numStores() == 1,
                    "actual: %d", memoryPipelineStorage.numStores());

    memoryPipelineStorage.resetCacheStats();

    REPORTER_ASSERT(reporter, draw(origTestContext, origOptions, &memoryPipelineStorage,
                                   /* withGradient= */ true));

    // On the second draw we should be able to load the prior pipelines but still need to store
    // the new ones.
    REPORTER_ASSERT(reporter, memoryPipelineStorage.numLoads() == 1,
                    "actual: %d", memoryPipelineStorage.numLoads());
    REPORTER_ASSERT(reporter, memoryPipelineStorage.numStores() == 1,
                    "actual: %d", memoryPipelineStorage.numStores());
}

}  // namespace skgpu::graphite
