// Copyright 2014 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_DBUS_ASYNC_EVENT_SEQUENCER_H_
#define LIBBRILLO_BRILLO_DBUS_ASYNC_EVENT_SEQUENCER_H_

#include <set>
#include <string>
#include <vector>

#include <base/bind.h>
#include <base/macros.h>
#include <base/memory/ref_counted.h>
#include <brillo/brillo_export.h>

namespace brillo {

namespace dbus_utils {

// A helper class for coordinating the multiple async tasks.  A consumer
// may grab any number of callbacks via Get*Handler() and schedule a list
// of completion actions to take.  When all handlers obtained via Get*Handler()
// have been called, the AsyncEventSequencer will call its CompletionActions.
//
// Usage:
//
// void Init(const base::Callback<void(bool success)> cb) {
//   scoped_refptr<AsyncEventSequencer> sequencer(
//       new AsyncEventSequencer());
//   one_delegate_needing_init_.Init(sequencer->GetHandler(
//       "my delegate failed to init", false));
//   dbus_init_delegate_.Init(sequencer->GetExportHandler(
//       "org.test.Interface", "ExposedMethodName",
//       "another delegate is flaky", false));
//   sequencer->OnAllTasksCompletedCall({cb});
// }
class BRILLO_EXPORT AsyncEventSequencer
    : public base::RefCounted<AsyncEventSequencer> {
 public:
  using Handler = base::Callback<void(bool success)>;
  using ExportHandler = base::Callback<void(const std::string& interface_name,
                                            const std::string& method_name,
                                            bool success)>;
  using CompletionAction = base::Callback<void(bool all_succeeded)>;
  using CompletionTask = base::Callback<void(void)>;

  AsyncEventSequencer();

  // Get a Finished handler callback.  Each callback is "unique" in the sense
  // that subsequent calls to GetHandler() will create new handlers
  // which will need to be called before completion actions are run.
  Handler GetHandler(const std::string& descriptive_message,
                     bool failure_is_fatal);

  // Like GetHandler except with a signature tailored to
  // ExportedObject's ExportMethod callback requirements.  Will also assert
  // that the passed interface/method names from ExportedObject are correct.
  ExportHandler GetExportHandler(const std::string& interface_name,
                                 const std::string& method_name,
                                 const std::string& descriptive_message,
                                 bool failure_is_fatal);

  // Once all handlers obtained via GetHandler have run,
  // we'll run each CompletionAction, then discard our references.
  // No more handlers may be obtained after this call.
  void OnAllTasksCompletedCall(std::vector<CompletionAction> actions);

  // Wrap a CompletionTask with a function that discards the result.
  // This CompletionTask retains no references to the AsyncEventSequencer.
  static CompletionAction WrapCompletionTask(const CompletionTask& task);
  // Create a default CompletionAction that doesn't do anything when called.
  static CompletionAction GetDefaultCompletionAction();

 private:
  // We'll partially bind this function before giving it back via
  // GetHandler.  Note that the returned callbacks have
  // references to *this, which gives us the neat property that we'll
  // destroy *this only when all our callbacks have been destroyed.
  BRILLO_PRIVATE void HandleFinish(int registration_number,
                                   const std::string& error_message,
                                   bool failure_is_fatal,
                                   bool success);
  // Similar to HandleFinish.
  BRILLO_PRIVATE void HandleDBusMethodExported(
      const Handler& finish_handler,
      const std::string& expected_interface_name,
      const std::string& expected_method_name,
      const std::string& actual_interface_name,
      const std::string& actual_method_name,
      bool success);
  BRILLO_PRIVATE void RetireRegistration(int registration_number);
  BRILLO_PRIVATE void CheckForFailure(bool failure_is_fatal,
                                      bool success,
                                      const std::string& error_message);
  BRILLO_PRIVATE void PossiblyRunCompletionActions();

  bool started_{false};
  int registration_counter_{0};
  std::set<int> outstanding_registrations_;
  std::vector<CompletionAction> completion_actions_;
  bool had_failures_{false};
  // Ref counted objects have private destructors.
  ~AsyncEventSequencer();
  friend class base::RefCounted<AsyncEventSequencer>;
  DISALLOW_COPY_AND_ASSIGN(AsyncEventSequencer);
};

}  // namespace dbus_utils

}  // namespace brillo

#endif  // LIBBRILLO_BRILLO_DBUS_ASYNC_EVENT_SEQUENCER_H_