// 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/lib/gpu/context.h"

#include <future>

#include "flutter/lib/gpu/formats.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "fml/make_copyable.h"
#include "impeller/renderer/context.h"
#include "tonic/converter/dart_converter.h"

namespace flutter {
namespace gpu {

bool SupportsNormalOffscreenMSAA(const impeller::Context& context) {
  auto& capabilities = context.GetCapabilities();
  return capabilities->SupportsOffscreenMSAA() &&
         !capabilities->SupportsImplicitResolvingMSAA();
}

IMPLEMENT_WRAPPERTYPEINFO(flutter_gpu, Context);

std::shared_ptr<impeller::Context> Context::default_context_;

void Context::SetOverrideContext(std::shared_ptr<impeller::Context> context) {
  default_context_ = std::move(context);
}

std::shared_ptr<impeller::Context> Context::GetOverrideContext() {
  return default_context_;
}

std::shared_ptr<impeller::Context> Context::GetDefaultContext(
    std::optional<std::string>& out_error) {
  auto override_context = GetOverrideContext();
  if (override_context) {
    return override_context;
  }

  auto dart_state = flutter::UIDartState::Current();
  if (!dart_state->IsFlutterGPUEnabled()) {
    out_error =
        "Flutter GPU must be enabled via the Flutter GPU manifest "
        "setting. This can be done either via command line argument "
        "--enable-flutter-gpu or "
        "by adding the FLTEnableFlutterGPU key set to true on iOS or "
        "io.flutter.embedding.android.EnableFlutterGPU metadata key to true on "
        "Android.";
    return nullptr;
  }
  // Grab the Impeller context from the IO manager.
  std::promise<std::shared_ptr<impeller::Context>> context_promise;
  auto impeller_context_future = context_promise.get_future();
  fml::TaskRunner::RunNowOrPostTask(
      dart_state->GetTaskRunners().GetIOTaskRunner(),
      fml::MakeCopyable([promise = std::move(context_promise),
                         io_manager = dart_state->GetIOManager()]() mutable {
        promise.set_value(io_manager ? io_manager->GetImpellerContext()
                                     : nullptr);
      }));
  auto context = impeller_context_future.get();

  if (!context) {
    out_error = "Unable to retrieve the Impeller context.";
  }
  return context;
}

Context::Context(std::shared_ptr<impeller::Context> context)
    : context_(std::move(context)) {}

Context::~Context() = default;

impeller::Context& Context::GetContext() {
  return *context_;
}

std::shared_ptr<impeller::Context>& Context::GetContextShared() {
  return context_;
}

}  // namespace gpu
}  // namespace flutter

//----------------------------------------------------------------------------
/// Exports
///

Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) {
  std::optional<std::string> out_error;
  auto impeller_context = flutter::gpu::Context::GetDefaultContext(out_error);
  if (out_error.has_value()) {
    return tonic::ToDart(out_error.value());
  }

  auto res = fml::MakeRefCounted<flutter::gpu::Context>(impeller_context);
  res->AssociateWithDartWrapper(wrapper);

  return Dart_Null();
}

extern int InternalFlutterGpu_Context_GetDefaultColorFormat(
    flutter::gpu::Context* wrapper) {
  return static_cast<int>(flutter::gpu::FromImpellerPixelFormat(
      wrapper->GetContext().GetCapabilities()->GetDefaultColorFormat()));
}

extern int InternalFlutterGpu_Context_GetDefaultStencilFormat(
    flutter::gpu::Context* wrapper) {
  return static_cast<int>(flutter::gpu::FromImpellerPixelFormat(
      wrapper->GetContext().GetCapabilities()->GetDefaultStencilFormat()));
}

extern int InternalFlutterGpu_Context_GetDefaultDepthStencilFormat(
    flutter::gpu::Context* wrapper) {
  return static_cast<int>(flutter::gpu::FromImpellerPixelFormat(
      wrapper->GetContext().GetCapabilities()->GetDefaultDepthStencilFormat()));
}

extern int InternalFlutterGpu_Context_GetMinimumUniformByteAlignment(
    flutter::gpu::Context* wrapper) {
  return wrapper->GetContext().GetCapabilities()->GetMinimumUniformAlignment();
}

extern bool InternalFlutterGpu_Context_GetSupportsOffscreenMSAA(
    flutter::gpu::Context* wrapper) {
  return flutter::gpu::SupportsNormalOffscreenMSAA(wrapper->GetContext());
}
