// 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/benchmarking/benchmarking.h"
#include "flutter/common/settings.h"
#include "flutter/lib/ui/volatile_path_tracker.h"
#include "flutter/lib/ui/window/platform_message_response_dart.h"
#include "flutter/runtime/dart_vm_lifecycle.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/testing/dart_isolate_runner.h"
#include "flutter/testing/fixture_test.h"

#include <future>

namespace flutter {

class Fixture : public testing::FixtureTest {
  void TestBody() override{};
};

static void BM_PlatformMessageResponseDartComplete(benchmark::State& state) {
  ThreadHost thread_host(ThreadHost::ThreadHostConfig(
      "test", ThreadHost::Type::Platform | ThreadHost::Type::RASTER |
                  ThreadHost::Type::IO | ThreadHost::Type::UI));
  TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
                           thread_host.raster_thread->GetTaskRunner(),
                           thread_host.ui_thread->GetTaskRunner(),
                           thread_host.io_thread->GetTaskRunner());
  Fixture fixture;
  auto settings = fixture.CreateSettingsForFixture();
  auto vm_ref = DartVMRef::Create(settings);
  auto isolate =
      testing::RunDartCodeInIsolate(vm_ref, settings, task_runners, "main", {},
                                    testing::GetDefaultKernelFilePath(), {});

  while (state.KeepRunning()) {
    state.PauseTiming();
    bool successful = isolate->RunInIsolateScope([&]() -> bool {
      // Simulate a message of 3 MB
      std::vector<uint8_t> data(3 << 20, 0);
      std::unique_ptr<fml::Mapping> mapping =
          std::make_unique<fml::DataMapping>(data);

      Dart_Handle library = Dart_RootLibrary();
      Dart_Handle closure =
          Dart_GetField(library, Dart_NewStringFromCString("messageCallback"));

      auto message = fml::MakeRefCounted<PlatformMessageResponseDart>(
          tonic::DartPersistentValue(isolate->get(), closure),
          thread_host.ui_thread->GetTaskRunner(), "");

      message->Complete(std::move(mapping));

      return true;
    });
    FML_CHECK(successful);
    state.ResumeTiming();

    // We skip timing everything above because the copy triggered by
    // message->Complete is a task posted on the UI thread. The following wait
    // for a UI task would let us know when that copy is done.
    std::promise<bool> completed;
    task_runners.GetUITaskRunner()->PostTask(
        [&completed] { completed.set_value(true); });
    completed.get_future().wait();
  }
}

static void BM_PathVolatilityTracker(benchmark::State& state) {
  ThreadHost thread_host(ThreadHost::ThreadHostConfig(
      "test", ThreadHost::Type::Platform | ThreadHost::Type::RASTER |
                  ThreadHost::Type::IO | ThreadHost::Type::UI));
  TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
                           thread_host.raster_thread->GetTaskRunner(),
                           thread_host.ui_thread->GetTaskRunner(),
                           thread_host.io_thread->GetTaskRunner());

  VolatilePathTracker tracker(task_runners.GetUITaskRunner(), true);

  while (state.KeepRunning()) {
    std::vector<std::shared_ptr<VolatilePathTracker::TrackedPath>> paths;
    constexpr int path_count = 1000;
    for (int i = 0; i < path_count; i++) {
      auto path = std::make_shared<VolatilePathTracker::TrackedPath>();
      path->path = SkPath();
      path->path.setIsVolatile(true);
      paths.push_back(std::move(path));
    }

    fml::AutoResetWaitableEvent latch;
    task_runners.GetUITaskRunner()->PostTask([&]() {
      for (auto path : paths) {
        tracker.Track(path);
      }
      latch.Signal();
    });

    latch.Wait();

    task_runners.GetUITaskRunner()->PostTask([&]() { tracker.OnFrame(); });

    for (int i = 0; i < path_count - 10; ++i) {
      paths[i].reset();
    }

    task_runners.GetUITaskRunner()->PostTask([&]() { tracker.OnFrame(); });

    latch.Reset();
    task_runners.GetUITaskRunner()->PostTask([&]() { latch.Signal(); });
    latch.Wait();
  }
}

BENCHMARK(BM_PlatformMessageResponseDartComplete)
    ->Unit(benchmark::kMicrosecond);

BENCHMARK(BM_PathVolatilityTracker)->Unit(benchmark::kMillisecond);

}  // namespace flutter
