// Copyright 2017 The Chromium 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 BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_

#include <lib/async/wait.h>

#include "base/base_export.h"
#include "base/fuchsia/async_dispatcher.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump.h"
#include "base/message_loop/watchable_io_message_pump_posix.h"

typedef struct fdio fdio_t;

namespace base {

class BASE_EXPORT MessagePumpFuchsia : public MessagePump,
                                       public WatchableIOMessagePumpPosix {
 public:
  // Implemented by callers to receive notifications of handle & fd events.
  class ZxHandleWatcher {
   public:
    virtual void OnZxHandleSignalled(zx_handle_t handle,
                                     zx_signals_t signals) = 0;

   protected:
    virtual ~ZxHandleWatcher() {}
  };

  // Manages an active watch on an zx_handle_t.
  class ZxHandleWatchController : public async_wait_t {
   public:
    explicit ZxHandleWatchController(const Location& from_here);
    // Deleting the Controller implicitly calls StopWatchingZxHandle.
    virtual ~ZxHandleWatchController();

    // Stop watching the handle, always safe to call.  No-op if there's nothing
    // to do.
    bool StopWatchingZxHandle();

    const Location& created_from_location() { return created_from_location_; }

   protected:
    friend class MessagePumpFuchsia;

    virtual bool WaitBegin();

    static void HandleSignal(async_t* async,
                             async_wait_t* wait,
                             zx_status_t status,
                             const zx_packet_signal_t* signal);

    const Location created_from_location_;

    // This bool is used by the pump when invoking the ZxHandleWatcher callback,
    // and by the FdHandleWatchController when invoking read & write callbacks,
    // to cope with the possibility of the caller deleting the *Watcher within
    // the callback. The pump sets |was_stopped_| to a location on the stack,
    // and the Watcher writes to it, if set, when deleted, allowing the pump
    // to check the value on the stack to short-cut any post-callback work.
    bool* was_stopped_ = nullptr;

    // Set directly from the inputs to WatchFileDescriptor.
    ZxHandleWatcher* watcher_ = nullptr;

    // Used to safely access resources owned by the associated message pump.
    WeakPtr<MessagePumpFuchsia> weak_pump_;

    // A watch may be marked as persistent, which means it remains active even
    // after triggering.
    bool persistent_ = false;

    DISALLOW_COPY_AND_ASSIGN(ZxHandleWatchController);
  };

  class FdWatchController : public FdWatchControllerInterface,
                            public ZxHandleWatchController,
                            public ZxHandleWatcher {
   public:
    explicit FdWatchController(const Location& from_here);
    ~FdWatchController() override;

    // FdWatchControllerInterface:
    bool StopWatchingFileDescriptor() override;

   private:
    friend class MessagePumpFuchsia;

    // Determines the desires signals, and begins waiting on the handle.
    bool WaitBegin() override;

    // ZxHandleWatcher interface.
    void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) override;

    // Set directly from the inputs to WatchFileDescriptor.
    FdWatcher* watcher_ = nullptr;
    int fd_ = -1;
    uint32_t desired_events_ = 0;

    // Set by WatchFileDescriptor to hold a reference to the descriptor's mxio.
    fdio_t* io_ = nullptr;

    DISALLOW_COPY_AND_ASSIGN(FdWatchController);
  };

  enum Mode {
    WATCH_READ = 1 << 0,
    WATCH_WRITE = 1 << 1,
    WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
  };

  MessagePumpFuchsia();
  ~MessagePumpFuchsia() override;

  bool WatchZxHandle(zx_handle_t handle,
                     bool persistent,
                     zx_signals_t signals,
                     ZxHandleWatchController* controller,
                     ZxHandleWatcher* delegate);
  bool WatchFileDescriptor(int fd,
                           bool persistent,
                           int mode,
                           FdWatchController* controller,
                           FdWatcher* delegate);

  // MessagePump implementation:
  void Run(Delegate* delegate) override;
  void Quit() override;
  void ScheduleWork() override;
  void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;

 private:
  // Handles IO events by running |async_dispatcher_|. Returns true if any
  // events were received or if ScheduleWork() was called.
  bool HandleEvents(zx_time_t deadline);

  // This flag is set to false when Run should return.
  bool keep_running_ = true;

  AsyncDispatcher async_dispatcher_;

  // The time at which we should call DoDelayedWork.
  TimeTicks delayed_work_time_;

  base::WeakPtrFactory<MessagePumpFuchsia> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(MessagePumpFuchsia);
};

}  // namespace base

#endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
