// Copyright 2015 The Chromium OS 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 LIBBRILLO_BRILLO_MESSAGE_LOOPS_GLIB_MESSAGE_LOOP_H_
#define LIBBRILLO_BRILLO_MESSAGE_LOOPS_GLIB_MESSAGE_LOOP_H_

#include <map>
#include <memory>

#include <base/location.h>
#include <base/time/time.h>
#include <glib.h>

#include <brillo/brillo_export.h>
#include <brillo/message_loops/message_loop.h>

namespace brillo {

class BRILLO_EXPORT GlibMessageLoop : public MessageLoop {
 public:
  GlibMessageLoop();
  ~GlibMessageLoop() override;

  // MessageLoop overrides.
  TaskId PostDelayedTask(const tracked_objects::Location& from_here,
                         const base::Closure& task,
                         base::TimeDelta delay) override;
  using MessageLoop::PostDelayedTask;
  TaskId WatchFileDescriptor(const tracked_objects::Location& from_here,
                             int fd,
                             WatchMode mode,
                             bool persistent,
                             const base::Closure& task) override;
  using MessageLoop::WatchFileDescriptor;
  bool CancelTask(TaskId task_id) override;
  bool RunOnce(bool may_block) override;
  void Run() override;
  void BreakLoop() override;

 private:
  // Called by the GLib's main loop when is time to call the callback scheduled
  // with Post*Task(). The pointer to the callback passed when scheduling it is
  // passed to this function as a gpointer on |user_data|.
  static gboolean OnRanPostedTask(gpointer user_data);

  // Called by the GLib's main loop when the watched source |source| is
  // ready to perform the operation given in |condition| without blocking.
  static gboolean OnWatchedFdReady(GIOChannel *source,
                                   GIOCondition condition,
                                   gpointer user_data);

  // Called by the GLib's main loop when the scheduled callback is removed due
  // to it being executed or canceled.
  static void DestroyPostedTask(gpointer user_data);

  // Return a new unused task_id.
  TaskId NextTaskId();

  GMainLoop* loop_;

  struct ScheduledTask {
    // A pointer to this GlibMessageLoop so we can remove the Task from the
    // glib callback.
    GlibMessageLoop* loop;
    tracked_objects::Location location;

    MessageLoop::TaskId task_id;
    guint source_id;
    bool persistent;
    base::Closure closure;
  };

  std::map<MessageLoop::TaskId, ScheduledTask*> tasks_;

  MessageLoop::TaskId last_id_ = kTaskIdNull;

  DISALLOW_COPY_AND_ASSIGN(GlibMessageLoop);
};

}  // namespace brillo

#endif  // LIBBRILLO_BRILLO_MESSAGE_LOOPS_GLIB_MESSAGE_LOOP_H_