// 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.

#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_H_

#include <chrono>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <variant>

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/task_runner_window.h"

namespace flutter {

typedef uint64_t (*CurrentTimeProc)();

// A custom task runner that integrates with user32 GetMessage semantics so
// that host app can own its own message loop and flutter still gets to process
// tasks on a timely basis.
class TaskRunner : public TaskRunnerWindow::Delegate {
 public:
  using TaskTimePoint = std::chrono::steady_clock::time_point;
  using TaskExpiredCallback = std::function<void(const FlutterTask*)>;
  using TaskClosure = std::function<void()>;

  // Creates a new task runner with the current thread, current time
  // provider, and callback for tasks that are ready to be run.
  TaskRunner(CurrentTimeProc get_current_time,
             const TaskExpiredCallback& on_task_expired);

  virtual ~TaskRunner();

  // Returns `true` if the current thread is this runner's thread.
  virtual bool RunsTasksOnCurrentThread() const;

  // Post a Flutter engine task to the event loop for delayed execution.
  void PostFlutterTask(FlutterTask flutter_task,
                       uint64_t flutter_target_time_nanos);

  // Post a task to the event loop.
  void PostTask(TaskClosure task);

  // Polls the event loop once. This will only process tasks scheduled through
  // the task runner. It will not process messages sent to other windows.
  void PollOnce(std::chrono::milliseconds timeout);

  // Post a task to the event loop or run it immediately if this is being called
  // from the runner's thread.
  void RunNowOrPostTask(TaskClosure task) {
    if (RunsTasksOnCurrentThread()) {
      task();
    } else {
      PostTask(std::move(task));
    }
  }

  // |TaskRunnerWindow::Delegate|
  std::chrono::nanoseconds ProcessTasks();

 private:
  typedef std::variant<FlutterTask, TaskClosure> TaskVariant;

  struct Task {
    uint64_t order;
    TaskTimePoint fire_time;
    TaskVariant variant;

    struct Comparer {
      bool operator()(const Task& a, const Task& b) {
        if (a.fire_time == b.fire_time) {
          return a.order > b.order;
        }
        return a.fire_time > b.fire_time;
      }
    };
  };

  // Enqueues the given task.
  void EnqueueTask(Task task);

  // Schedules timers to call `ProcessTasks()` at the runner's thread.
  virtual void WakeUp();

  // Returns the current TaskTimePoint that can be used to determine whether a
  // task is expired.
  //
  // Tests can override this to mock up the time.
  virtual TaskTimePoint GetCurrentTimeForTask() const {
    return TaskTimePoint::clock::now();
  }

  // Returns a TaskTimePoint computed from the given target time from Flutter.
  TaskTimePoint TimePointFromFlutterTime(
      uint64_t flutter_target_time_nanos) const;

  CurrentTimeProc get_current_time_;
  TaskExpiredCallback on_task_expired_;
  std::mutex task_queue_mutex_;
  std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
  DWORD main_thread_id_;
  std::shared_ptr<TaskRunnerWindow> task_runner_window_;

  FML_DISALLOW_COPY_AND_ASSIGN(TaskRunner);
};

}  // namespace flutter

#endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_H_
