// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/testing/test_vulkan_surface.h"
#include <memory>
#include "flutter/fml/logging.h"
#include "flutter/testing/test_vulkan_context.h"

#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"

namespace flutter {
namespace testing {

TestVulkanSurface::TestVulkanSurface(TestVulkanImage&& image)
    : image_(std::move(image)){};

std::unique_ptr<TestVulkanSurface> TestVulkanSurface::Create(
    const TestVulkanContext& context,
    const SkISize& surface_size) {
  auto image_result = context.CreateImage(surface_size);

  if (!image_result.has_value()) {
    FML_LOG(ERROR) << "Could not create VkImage.";
    return nullptr;
  }

  GrVkImageInfo image_info = {
      .fImage = image_result.value().GetImage(),
      .fImageTiling = VK_IMAGE_TILING_OPTIMAL,
      .fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
      .fFormat = VK_FORMAT_R8G8B8A8_UNORM,
      .fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
                          VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
                          VK_IMAGE_USAGE_TRANSFER_DST_BIT |
                          VK_IMAGE_USAGE_SAMPLED_BIT,
      .fSampleCount = 1,
      .fLevelCount = 1,
  };
  GrBackendTexture backend_texture(surface_size.width(),   //
                                   surface_size.height(),  //
                                   image_info              //
  );

  SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);

  auto result = std::unique_ptr<TestVulkanSurface>(
      new TestVulkanSurface(std::move(image_result.value())));
  result->surface_ = SkSurfaces::WrapBackendTexture(
      context.GetGrDirectContext().get(),  // context
      backend_texture,                     // back-end texture
      kTopLeft_GrSurfaceOrigin,            // surface origin
      1,                                   // sample count
      kRGBA_8888_SkColorType,              // color type
      SkColorSpace::MakeSRGB(),            // color space
      &surface_properties,                 // surface properties
      nullptr,                             // release proc
      nullptr                              // release context
  );

  if (!result->surface_) {
    FML_LOG(ERROR)
        << "Could not wrap VkImage as an SkSurface Vulkan render texture.";
    return nullptr;
  }

  return result;
}

bool TestVulkanSurface::IsValid() const {
  return surface_ != nullptr;
}

sk_sp<SkImage> TestVulkanSurface::GetSurfaceSnapshot() const {
  if (!IsValid()) {
    return nullptr;
  }

  if (!surface_) {
    FML_LOG(ERROR) << "Aborting snapshot because of on-screen surface "
                      "acquisition failure.";
    return nullptr;
  }

  auto device_snapshot = surface_->makeImageSnapshot();

  if (!device_snapshot) {
    FML_LOG(ERROR) << "Could not create the device snapshot while attempting "
                      "to snapshot the Vulkan surface.";
    return nullptr;
  }

  auto host_snapshot = device_snapshot->makeRasterImage();

  if (!host_snapshot) {
    FML_LOG(ERROR) << "Could not create the host snapshot while attempting to "
                      "snapshot the Vulkan surface.";
    return nullptr;
  }

  return host_snapshot;
}

VkImage TestVulkanSurface::GetImage() {
  return image_.GetImage();
}

}  // namespace testing
}  // namespace flutter
