/*
 * Copyright 2023 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/SkBitmap.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkRect.h"
#include "include/core/SkTileMode.h"
#include "include/gpu/graphite/Context.h"
#include "include/gpu/graphite/Image.h"
#include "include/gpu/graphite/Surface.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/Surface_Graphite.h"
#include "src/shaders/SkImageShader.h"
#include "tools/ToolUtils.h"
#include "tools/gpu/ManagedBackendTexture.h"

namespace skgpu::graphite {

namespace {

using DrawFn = void (*)(sk_sp<SkImage>, SkCanvas*, SkRect /*srcRect*/, SkRect /*dstRect*/);

constexpr SkColor4f kTopColor = SkColors::kRed;
constexpr SkColor4f kBottomColor = SkColors::kBlue;
constexpr int32_t kHalfSize = 4;
constexpr SkISize kImageSize = {2*kHalfSize, 2*kHalfSize};

void test_draw(skiatest::Reporter* reporter,
               Context* context,
               skgpu::Origin origin,
               SkRect srcRect,
               SkRect dstRect,
               DrawFn drawImageFn) {
    std::unique_ptr<Recorder> recorder = context->makeRecorder();

    skgpu::Protected isProtected = skgpu::Protected(context->priv().caps()->protectedSupport());

    SkBitmap bitmap;
    bitmap.allocPixels(SkImageInfo::Make(kImageSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
                       0);
    bitmap.eraseColor(kTopColor);
    bitmap.erase(kBottomColor,
                 SkIRect::MakeLTRB(0, kHalfSize, kImageSize.width(), kImageSize.height()));

    auto managedTexture =
            sk_gpu_test::ManagedGraphiteTexture::MakeFromPixmap(recorder.get(),
                                                                bitmap.pixmap(),
                                                                skgpu::Mipmapped::kNo,
                                                                skgpu::Renderable::kNo,
                                                                isProtected);

    REPORTER_ASSERT(reporter, managedTexture);
    if (!managedTexture) {
        return;
    }

    sk_sp<SkImage> image = SkImages::WrapTexture(recorder.get(),
                                                 managedTexture->texture(),
                                                 kRGBA_8888_SkColorType,
                                                 kPremul_SkAlphaType,
                                                 /*colorSpace=*/nullptr,
                                                 origin);

    REPORTER_ASSERT(reporter, image);
    if (!image) {
        return;
    }

    sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(
            recorder.get(),
            SkImageInfo::Make(kImageSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType));

    REPORTER_ASSERT(reporter, surface);
    if (!surface) {
        return;
    }

    SkCanvas* canvas = surface->getCanvas();

    drawImageFn(image, canvas, srcRect, dstRect);

    SkPixmap pm;

    SkBitmap result;
    result.allocPixels(SkImageInfo::Make(kImageSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
    bool peekPixelsSuccess = result.peekPixels(&pm);
    REPORTER_ASSERT(reporter, peekPixelsSuccess);

    bool readPixelsSuccess = surface->readPixels(pm, 0, 0);
    REPORTER_ASSERT(reporter, readPixelsSuccess);

    bool resultTopColorOnTop = origin == skgpu::Origin::kTopLeft;

    for (int32_t y = 0; y < kImageSize.height(); ++y) {
        for (int32_t x = 0; x < kImageSize.width(); ++x) {
            SkColor4f color = pm.getColor4f(x, y);

            SkColor4f expectedColor = ((y < kHalfSize) == resultTopColorOnTop) ? kTopColor
                                                                               : kBottomColor;
            REPORTER_ASSERT(reporter,
                            color == expectedColor,
                            "At position {%d, %d}, "
                            "expected {%.1f, %.1f, %.1f, %.1f}, "
                            "found {%.1f, %.1f, %.1f, %.1f}",
                            x, y,
                            expectedColor.fR, expectedColor.fG, expectedColor.fB, expectedColor.fA,
                            color.fR, color.fG, color.fB, color.fA);
        }
    }
}

const SkRect kTestSrcRects[] = {
    // entire thing
    SkRect::MakeWH(kImageSize.width(), kImageSize.height()),
    // half rect still splitting top and bottom colors
    SkRect::MakeXYWH(2, 2, kHalfSize, kHalfSize),
};

void test_draw_fn(skiatest::Reporter* reporter,
                  Context* context,
                  DrawFn drawImageFn) {
    for (auto origin : {skgpu::Origin::kTopLeft, skgpu::Origin::kBottomLeft}) {
        for (auto srcRect: kTestSrcRects) {
            test_draw(reporter,
                      context,
                      origin,
                      srcRect,
                      SkRect::MakeWH(kImageSize.width(), kImageSize.height()),
                      drawImageFn);
        }
    }
}

void draw_image(sk_sp<SkImage> image,
                SkCanvas* canvas,
                SkRect srcRect,
                SkRect dstRect) {
    canvas->drawImageRect(image,
                          srcRect,
                          dstRect,
                          SkSamplingOptions(),
                          /*paint=*/nullptr,
                          SkCanvas::kStrict_SrcRectConstraint);
}

void draw_image_with_shader(sk_sp<SkImage> image,
                            SkCanvas* canvas,
                            SkRect srcRect,
                            SkRect dstRect) {
    SkPaint p;
    SkMatrix srcToDst = SkMatrix::RectToRect(srcRect, dstRect);
    p.setShader(SkImageShader::MakeSubset(
                std::move(image),
                srcRect,
                SkTileMode::kClamp,
                SkTileMode::kClamp,
                SkSamplingOptions(),
                &srcToDst));
    canvas->drawRect(dstRect, p);
}

}  // anonymous namespace


DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(ImageOriginTest_drawImage_Graphite, reporter, context,
                                         CtsEnforcement::kApiLevel_V) {
    test_draw_fn(reporter, context, draw_image);
}

DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(ImageOriginTest_imageShader_Graphite, reporter, context,
                                         CtsEnforcement::kApiLevel_V) {
    test_draw_fn(reporter, context, draw_image_with_shader);
}

}  // namespace skgpu::graphite
